#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 attribute 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 = 14; /** * @brief This property view is associated with a {@link ClassProperty} of an * unsupported type. */ 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 = 16; /** * @brief This property view's attribute does not have a valid accessor index. */ 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 = 18; /** * @brief This property view's component type does not match the type of the * accessor it uses. */ 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 = 20; /** * @brief This property view uses an accessor that does not have a valid * buffer view index. */ 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 = 22; /** * @brief This property view uses an accessor that points outside the bounds * of its target buffer view. */ 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 = 24; }; /** * @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{}, _size{0} {} /** * @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{}, _size{0} { CESIUM_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} { if (this->_status != PropertyAttributePropertyViewStatus::Valid) { // Don't override the status / size if something is wrong with the class // property's definition. return; } 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; 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 accessorView 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}, _size{ this->_status == PropertyAttributePropertyViewStatus::Valid ? accessorView.size() : 0} {} /** * @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. * * 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 { if (this->_status == PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault) { return this->defaultValue(); } 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 { CESIUM_ASSERT( this->_status == PropertyAttributePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); CESIUM_ASSERT( size() > 0 && "Check the size() of the view to make sure it's not empty"); CESIUM_ASSERT(index >= 0 && "index must be non-negative"); CESIUM_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 { return _size; } private: AccessorView _accessor; int64_t _size; }; /** * @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{}, _size{0} {} /** * @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{}, _size{0} { CESIUM_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 \ref get is called. However, \ref 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} { if (this->_status != PropertyAttributePropertyViewStatus::Valid) { // Don't override the status / size if something is wrong with the class // property's definition. return; } 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; 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 accessorView 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}, _size{ this->_status == PropertyAttributePropertyViewStatus::Valid ? accessorView.size() : 0} {} /** * @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. * * 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 { if (this->_status == PropertyAttributePropertyViewStatus::EmptyPropertyWithDefault) { return this->defaultValue(); } 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 { CESIUM_ASSERT( this->_status == PropertyAttributePropertyViewStatus::Valid && "Check the status() first to make sure view is valid"); CESIUM_ASSERT( size() > 0 && "Check the size() of the view to make sure it's not empty"); CESIUM_ASSERT(index >= 0 && "index must be non-negative"); CESIUM_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 { return _size; } private: AccessorView _accessor; int64_t _size; }; } // namespace CesiumGltf