#pragma once #include "CesiumGltf/ExtensionModelExtStructuralMetadata.h" #include "CesiumGltf/Model.h" #include "CesiumGltf/PropertyTablePropertyView.h" #include "CesiumGltf/PropertyType.h" #include #include 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 PropertyTable}, * but instead indicate that its {@link PropertyTableView::size} is 0. * This enumeration provides the reason. */ enum class PropertyTableViewStatus { /** * @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. */ ErrorMissingMetadataExtension, /** * @brief The property table view's model does not have a schema in its * EXT_structural_metadata extension. */ ErrorMissingSchema, /** * @brief The property table's specified class could not be found in the * extension. */ ErrorClassNotFound }; /** * @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} * does not access out of bounds. */ class PropertyTableView { public: /** * @brief Creates an instance of PropertyTableView. * @param model The glTF Model that contains the property table data. * @param propertyTable The {@link PropertyTable} * from which the view will retrieve data. */ PropertyTableView(const Model& model, const PropertyTable& 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. */ 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. * * @return The number of elements in this PropertyTableView. */ int64_t size() const noexcept { return _status == PropertyTableViewStatus::Valid ? _pPropertyTable->count : 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 noexcept { return _pClass; } /** * @brief Finds the {@link ClassProperty} that * 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& propertyId) const; /** * @brief Gets a {@link PropertyTablePropertyView} that views the data of a property stored * 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 * 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 \ref 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 PropertyTablePropertyView. Likewise, if the value of * Normalized * does not match the value of {@link 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 propertyId The id of the property to retrieve data from * @return A \ref PropertyTablePropertyView of the property. If no valid * property is found, the property view will be invalid. */ template PropertyTablePropertyView getPropertyView(const std::string& propertyId) const { if (this->size() <= 0) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable); } const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNonexistentProperty); } return getPropertyViewImpl(propertyId, *pClassProperty); } /** * @brief Gets a \ref PropertyTablePropertyView through a callback that * accepts a property id and a \ref PropertyTablePropertyView that views the * data of the property with the specified id. * * This method will validate the EXT_structural_metadata format to ensure * \ref 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 \ref PropertyArrayView with T as one of the * aforementioned types. * * If the property is invalid, an empty \ref PropertyTablePropertyView with an * error status will be passed to the callback. Otherwise, a valid property * view will be passed to the callback. * * @param propertyId The id of the property to retrieve data from * @param callback A callback function that accepts a property id and a * \ref PropertyTablePropertyView * @tparam Callback The type of the callback function. */ template void getPropertyView(const std::string& propertyId, Callback&& callback) const { if (this->size() <= 0) { callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidPropertyTable)); return; } const ClassProperty* pClassProperty = getClassProperty(propertyId); if (!pClassProperty) { callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::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)) { // Only integer components may be normalized. callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorInvalidNormalization)); return; } if (pClassProperty->array) { if (normalized) { getArrayPropertyViewImpl( propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getArrayPropertyViewImpl( propertyId, *pClassProperty, type, componentType, std::forward(callback)); } return; } if (type == PropertyType::Scalar) { if (normalized) { getScalarPropertyViewImpl( propertyId, *pClassProperty, componentType, std::forward(callback)); } else { getScalarPropertyViewImpl( propertyId, *pClassProperty, componentType, std::forward(callback)); } return; } if (isPropertyTypeVecN(type)) { if (normalized) { getVecNPropertyViewImpl( propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getVecNPropertyViewImpl( propertyId, *pClassProperty, type, componentType, std::forward(callback)); } return; } if (isPropertyTypeMatN(type)) { if (normalized) { getMatNPropertyViewImpl( propertyId, *pClassProperty, type, componentType, std::forward(callback)); } else { getMatNPropertyViewImpl( propertyId, *pClassProperty, type, componentType, std::forward(callback)); } return; } if (type == PropertyType::String) { callback( propertyId, getPropertyViewImpl( propertyId, *pClassProperty)); return; } if (type == PropertyType::Boolean) { callback( propertyId, getPropertyViewImpl(propertyId, *pClassProperty)); return; } callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } /** * @brief Iterates over each property in the \ref PropertyTable with a * callback that accepts a property id and a \ref PropertyTablePropertyView to * view the data stored in the \ref PropertyTableProperty. * * This method will validate the EXT_structural_metadata format to ensure * \ref 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 \ref PropertyArrayView with T as one of the * aforementioned types. * * If the property is invalid, an empty \ref PropertyTablePropertyView with * an error status code will be passed to the callback. Otherwise, a valid * property view will be passed to the callback. * * @param callback A callback function that accepts property id and * \ref PropertyTablePropertyView * @tparam Callback The type of the callback function. */ template void forEachProperty(Callback&& callback) const { for (const auto& property : this->_pClass->properties) { getPropertyView(property.first, std::forward(callback)); } } private: template void getScalarArrayPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } template void getVecNArrayPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( propertyId, getPropertyViewImpl>, false>( propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( propertyId, getPropertyViewImpl>, false>( propertyId, classProperty)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } template void getVecNArrayPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getVecNArrayPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getVecNArrayPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getVecNArrayPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } template void getMatNArrayPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyId, getPropertyViewImpl< PropertyArrayView>, Normalized>(propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( propertyId, getPropertyViewImpl>, false>( propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( propertyId, getPropertyViewImpl>, false>( propertyId, classProperty)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } template void getMatNArrayPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { const glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getMatNArrayPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getMatNArrayPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getMatNArrayPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } template void getArrayPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { if (type == PropertyType::Scalar) { getScalarArrayPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); } else if (isPropertyTypeVecN(type)) { getVecNArrayPropertyViewImpl( propertyId, classProperty, type, componentType, std::forward(callback)); } else if (isPropertyTypeMatN(type)) { getMatNArrayPropertyViewImpl( propertyId, classProperty, type, componentType, std::forward(callback)); } else if (type == PropertyType::Boolean) { callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); } else if (type == PropertyType::String) { callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); } else { callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); } } template void getVecNPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } template void getVecNPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { const glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getVecNPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getVecNPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getVecNPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } template void getMatNPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint8: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int16: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint16: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyId, getPropertyViewImpl, Normalized>( propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( propertyId, getPropertyViewImpl, false>( propertyId, classProperty)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } template void getMatNPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyType type, PropertyComponentType componentType, Callback&& callback) const { glm::length_t N = getDimensionsFromPropertyType(type); switch (N) { case 2: getMatNPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 3: getMatNPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; case 4: getMatNPropertyViewImpl( propertyId, classProperty, componentType, std::forward(callback)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch)); break; } } template void getScalarPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty, PropertyComponentType componentType, Callback&& callback) const { switch (componentType) { case PropertyComponentType::Int8: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Uint8: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Int16: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); return; case PropertyComponentType::Uint16: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Int32: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Uint32: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Int64: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Uint64: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Float32: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); break; case PropertyComponentType::Float64: callback( propertyId, getPropertyViewImpl(propertyId, classProperty)); break; default: callback( propertyId, PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch)); break; } } template PropertyTablePropertyView getPropertyViewImpl( const std::string& propertyId, const ClassProperty& classProperty) const { auto propertyTablePropertyIter = _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 // 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); } const PropertyTableProperty& propertyTableProperty = propertyTablePropertyIter->second; if constexpr (IsMetadataNumeric::value || IsMetadataBoolean::value) { return getNumericOrBooleanPropertyValues( classProperty, propertyTableProperty); } if constexpr (IsMetadataString::value) { return getStringPropertyValues(classProperty, propertyTableProperty); } 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( const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const { if (classProperty.array) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } const PropertyComponentType componentType = convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } if (classProperty.normalized != Normalized) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } std::span values; const auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { return PropertyTablePropertyView(status); } if (values.size() % sizeof(T) != 0) { return PropertyTablePropertyView( PropertyTablePropertyViewStatus:: 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 PropertyTablePropertyView( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } return PropertyTablePropertyView( propertyTableProperty, classProperty, _pPropertyTable->count, values); } PropertyTablePropertyView getStringPropertyValues( const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const; 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, Normalized>( PropertyTablePropertyViewStatus::ErrorArrayTypeMismatch); } const PropertyType type = convertStringToPropertyType(classProperty.type); if (TypeToPropertyType::value != type) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorTypeMismatch); } const PropertyComponentType componentType = convertStringToPropertyComponentType( classProperty.componentType.value_or("")); if (TypeToPropertyType::component != componentType) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorComponentTypeMismatch); } if (classProperty.normalized != Normalized) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorNormalizationMismatch); } std::span values; auto status = getBufferSafe(propertyTableProperty.values, values); if (status != PropertyTablePropertyViewStatus::Valid) { return PropertyTablePropertyView, Normalized>( status); } if (values.size() % sizeof(T) != 0) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeNotDivisibleByTypeSize); } const int64_t fixedLengthArrayCount = classProperty.count.value_or(0); if (fixedLengthArrayCount > 0 && propertyTableProperty.arrayOffsets >= 0) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferCoexist); } if (fixedLengthArrayCount <= 0 && propertyTableProperty.arrayOffsets < 0) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorArrayCountAndOffsetBufferDontExist); } // Handle fixed-length arrays if (fixedLengthArrayCount > 0) { size_t maxRequiredBytes = maxRequiredBytes = static_cast( _pPropertyTable->count * fixedLengthArrayCount * sizeof(T)); if (values.size() < maxRequiredBytes) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus:: ErrorBufferViewSizeDoesNotMatchPropertyTableCount); } return PropertyTablePropertyView, Normalized>( propertyTableProperty, classProperty, _pPropertyTable->count, values); } // Handle variable-length arrays const PropertyComponentType arrayOffsetType = convertArrayOffsetTypeStringToPropertyComponentType( propertyTableProperty.arrayOffsetType); if (arrayOffsetType == PropertyComponentType::None) { return PropertyTablePropertyView, Normalized>( PropertyTablePropertyViewStatus::ErrorInvalidArrayOffsetType); } constexpr bool checkBitsSize = false; std::span arrayOffsets; status = getArrayOffsetsBufferSafe( propertyTableProperty.arrayOffsets, arrayOffsetType, values.size(), static_cast(_pPropertyTable->count), checkBitsSize, arrayOffsets); if (status != PropertyTablePropertyViewStatus::Valid) { return PropertyTablePropertyView, Normalized>( status); } if constexpr (Normalized) { return PropertyTablePropertyView, true>( propertyTableProperty, classProperty, _pPropertyTable->count, values, arrayOffsets, arrayOffsetType); } else { return PropertyTablePropertyView, false>( propertyTableProperty, classProperty, _pPropertyTable->count, values, arrayOffsets, std::span(), arrayOffsetType, PropertyComponentType::None); } } PropertyTablePropertyView> getStringArrayPropertyValues( const ClassProperty& classProperty, const PropertyTableProperty& propertyTableProperty) const; PropertyViewStatusType getBufferSafe( int32_t bufferView, std::span& buffer) const noexcept; PropertyViewStatusType getArrayOffsetsBufferSafe( int32_t arrayOffsetsBufferView, PropertyComponentType arrayOffsetType, size_t valuesBufferSize, size_t propertyTableCount, bool checkBitsSize, std::span& arrayOffsetsBuffer) const noexcept; PropertyViewStatusType getStringOffsetsBufferSafe( int32_t stringOffsetsBufferView, PropertyComponentType stringOffsetType, size_t valuesBufferSize, size_t propertyTableCount, std::span& stringOffsetsBuffer) const noexcept; const Model* _pModel; const PropertyTable* _pPropertyTable; const Class* _pClass; PropertyTableViewStatus _status; }; } // namespace CesiumGltf