// Copyright 2020-2024 CesiumGS, Inc. and Contributors #include "CesiumPropertyAttributeProperty.h" #include "CesiumMetadataEnum.h" #include "UnrealMetadataConversions.h" #include #include #include namespace { /** * Callback on a std::any, assuming that it contains a * PropertyAttributePropertyView of the specified type. If the type does not * match, the callback is performed on an invalid PropertyAttributePropertyView * instead. * * @param property The std::any containing the property. * @param callback The callback function. * * @tparam TProperty The property type. * @tparam Normalized Whether the PropertyAttributePropertyView is normalized. * @tparam TResult The type of the output from the callback function. * @tparam Callback The callback function type. */ template < typename TProperty, bool Normalized, typename TResult, typename Callback> TResult propertyAttributePropertyCallback( const std::any& property, Callback&& callback) { const CesiumGltf::PropertyAttributePropertyView* pProperty = std::any_cast< CesiumGltf::PropertyAttributePropertyView>( &property); if (pProperty) { return callback(*pProperty); } return callback(CesiumGltf::PropertyAttributePropertyView()); } /** * Callback on a std::any, assuming that it contains a * PropertyAttributePropertyView on a scalar type. If the valueType does not * have a valid component type, the callback is performed on an invalid * PropertyAttributePropertyView instead. * * @param property The std::any containing the property. * @param valueType The FCesiumMetadataValueType of the property. * @param callback The callback function. * * @tparam Normalized Whether the PropertyAttributePropertyView is normalized. * @tparam TResult The type of the output from the callback function. * @tparam Callback The callback function type. */ template TResult scalarPropertyAttributePropertyCallback( const std::any& property, const FCesiumMetadataValueType& valueType, Callback&& callback) { switch (valueType.ComponentType) { case ECesiumMetadataComponentType::Int8: return propertyAttributePropertyCallback< int8_t, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint8: return propertyAttributePropertyCallback< uint8_t, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Int16: return propertyAttributePropertyCallback< int16_t, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint16: return propertyAttributePropertyCallback< uint16_t, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint32: return propertyAttributePropertyCallback< uint32_t, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Float32: return propertyAttributePropertyCallback( property, std::forward(callback)); default: return callback(CesiumGltf::PropertyAttributePropertyView()); } } /** * Callback on a std::any, assuming that it contains a * PropertyAttributePropertyView on a glm::vecN type. If the valueType does not * have a valid component type, the callback is performed on an invalid * PropertyAttributePropertyView instead. * * @param property The std::any containing the property. * @param valueType The FCesiumMetadataValueType of the property. * @param callback The callback function. * * @tparam N The dimensions of the glm::vecN * @tparam Normalized Whether the PropertyAttributePropertyView is normalized. * @tparam TResult The type of the output from the callback function. * @tparam Callback The callback function type. */ template TResult vecNPropertyAttributePropertyCallback( const std::any& property, const FCesiumMetadataValueType& valueType, Callback&& callback) { switch (valueType.ComponentType) { case ECesiumMetadataComponentType::Int8: return propertyAttributePropertyCallback< glm::vec, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint8: return propertyAttributePropertyCallback< glm::vec, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Int16: return propertyAttributePropertyCallback< glm::vec, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint16: return propertyAttributePropertyCallback< glm::vec, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint32: return propertyAttributePropertyCallback< glm::vec, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Float32: return propertyAttributePropertyCallback< glm::vec, false, TResult, Callback>(property, std::forward(callback)); default: return callback(CesiumGltf::PropertyAttributePropertyView()); } } /** * Callback on a std::any, assuming that it contains a * PropertyAttributePropertyView on a glm::vecN type. If the valueType does not * have a valid component type, the callback is performed on an invalid * PropertyAttributePropertyView instead. * * @param property The std::any containing the property. * @param valueType The FCesiumMetadataValueType of the property. * @param callback The callback function. * * @tparam Normalized Whether the PropertyAttributePropertyView is normalized. * @tparam TResult The type of the output from the callback function. * @tparam Callback The callback function type. */ template TResult vecNPropertyAttributePropertyCallback( const std::any& property, const FCesiumMetadataValueType& valueType, Callback&& callback) { switch (valueType.Type) { case ECesiumMetadataType::Vec2: return vecNPropertyAttributePropertyCallback< 2, Normalized, TResult, Callback>(property, valueType, std::forward(callback)); case ECesiumMetadataType::Vec3: return vecNPropertyAttributePropertyCallback< 3, Normalized, TResult, Callback>(property, valueType, std::forward(callback)); case ECesiumMetadataType::Vec4: return vecNPropertyAttributePropertyCallback< 4, Normalized, TResult, Callback>(property, valueType, std::forward(callback)); default: return callback(CesiumGltf::PropertyAttributePropertyView()); } } /** * Callback on a std::any, assuming that it contains a * PropertyAttributePropertyView on a glm::matN type. If the valueType does not * have a valid component type, the callback is performed on an invalid * PropertyAttributePropertyView instead. * * @param property The std::any containing the property. * @param valueType The FCesiumMetadataValueType of the property. * @param callback The callback function. * * @tparam N The dimensions of the glm::matN * @tparam TNormalized Whether the PropertyAttributePropertyView is normalized. * @tparam TResult The type of the output from the callback function. * @tparam Callback The callback function type. */ template TResult matNPropertyAttributePropertyCallback( const std::any& property, const FCesiumMetadataValueType& valueType, Callback&& callback) { switch (valueType.ComponentType) { case ECesiumMetadataComponentType::Int8: return propertyAttributePropertyCallback< glm::mat, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint8: return propertyAttributePropertyCallback< glm::mat, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Int16: return propertyAttributePropertyCallback< glm::mat, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint16: return propertyAttributePropertyCallback< glm::mat, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Uint32: return propertyAttributePropertyCallback< glm::mat, Normalized, TResult, Callback>(property, std::forward(callback)); case ECesiumMetadataComponentType::Float32: return propertyAttributePropertyCallback< glm::mat, false, TResult, Callback>(property, std::forward(callback)); default: return callback(CesiumGltf::PropertyAttributePropertyView()); } } /** * Callback on a std::any, assuming that it contains a * PropertyAttributePropertyView on a glm::matN type. If the valueType does not * have a valid component type, the callback is performed on an invalid * PropertyAttributePropertyView instead. * * @param property The std::any containing the property. * @param valueType The FCesiumMetadataValueType of the property. * @param callback The callback function. * * @tparam Normalized Whether the PropertyAttributePropertyView is normalized. * @tparam TResult The type of the output from the callback function. * @tparam Callback The callback function type. */ template TResult matNPropertyAttributePropertyCallback( const std::any& property, const FCesiumMetadataValueType& valueType, Callback&& callback) { if (valueType.Type == ECesiumMetadataType::Mat2) { return matNPropertyAttributePropertyCallback< 2, Normalized, TResult, Callback>(property, valueType, std::forward(callback)); } if (valueType.Type == ECesiumMetadataType::Mat3) { return matNPropertyAttributePropertyCallback< 3, Normalized, TResult, Callback>(property, valueType, std::forward(callback)); } if (valueType.Type == ECesiumMetadataType::Mat4) { return matNPropertyAttributePropertyCallback< 4, Normalized, TResult, Callback>(property, valueType, std::forward(callback)); } return callback(CesiumGltf::PropertyAttributePropertyView()); } template TResult propertyAttributePropertyCallback( const std::any& property, const FCesiumMetadataValueType& valueType, bool normalized, Callback&& callback) { if (valueType.bIsArray) { // Array types are not supported for property attribute properties. return callback(CesiumGltf::PropertyAttributePropertyView()); } switch (valueType.Type) { case ECesiumMetadataType::Scalar: return normalized ? scalarPropertyAttributePropertyCallback< true, TResult, Callback>( property, valueType, std::forward(callback)) : scalarPropertyAttributePropertyCallback< false, TResult, Callback>( property, valueType, std::forward(callback)); case ECesiumMetadataType::Enum: return scalarPropertyAttributePropertyCallback( property, valueType, std::forward(callback)); case ECesiumMetadataType::Vec2: case ECesiumMetadataType::Vec3: case ECesiumMetadataType::Vec4: return normalized ? vecNPropertyAttributePropertyCallback( property, valueType, std::forward(callback)) : vecNPropertyAttributePropertyCallback< false, TResult, Callback>( property, valueType, std::forward(callback)); case ECesiumMetadataType::Mat2: case ECesiumMetadataType::Mat3: case ECesiumMetadataType::Mat4: return normalized ? matNPropertyAttributePropertyCallback( property, valueType, std::forward(callback)) : matNPropertyAttributePropertyCallback< false, TResult, Callback>( property, valueType, std::forward(callback)); default: return callback(CesiumGltf::PropertyAttributePropertyView()); } } } // namespace ECesiumPropertyAttributePropertyStatus UCesiumPropertyAttributePropertyBlueprintLibrary:: GetPropertyAttributePropertyStatus( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return Property._status; } ECesiumMetadataBlueprintType UCesiumPropertyAttributePropertyBlueprintLibrary::GetBlueprintType( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return CesiumMetadataValueTypeToBlueprintType(Property._valueType); } FCesiumMetadataValueType UCesiumPropertyAttributePropertyBlueprintLibrary::GetValueType( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return Property._valueType; } int64 UCesiumPropertyAttributePropertyBlueprintLibrary::GetPropertySize( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [](const auto& view) -> int64 { return view.size(); }); } uint8 UCesiumPropertyAttributePropertyBlueprintLibrary::GetByte( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, uint8 DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, DefaultValue](const auto& v) -> uint8 { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (maybeValue) { auto value = *maybeValue; return CesiumGltf::MetadataConversions:: convert(value) .value_or(DefaultValue); } return DefaultValue; }); } int32 UCesiumPropertyAttributePropertyBlueprintLibrary::GetInteger( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, int32 DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, DefaultValue](const auto& v) -> int32 { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (maybeValue) { auto value = *maybeValue; return CesiumGltf::MetadataConversions:: convert(value) .value_or(DefaultValue); } return DefaultValue; }); } int64 UCesiumPropertyAttributePropertyBlueprintLibrary::GetInteger64( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, int64 DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, DefaultValue](const auto& v) -> int64 { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (maybeValue) { auto value = *maybeValue; return CesiumGltf::MetadataConversions:: convert(value) .value_or(DefaultValue); } return DefaultValue; }); } float UCesiumPropertyAttributePropertyBlueprintLibrary::GetFloat( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, float DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, DefaultValue](const auto& v) -> float { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (maybeValue) { auto value = *maybeValue; return CesiumGltf::MetadataConversions:: convert(value) .value_or(DefaultValue); } return DefaultValue; }); } double UCesiumPropertyAttributePropertyBlueprintLibrary::GetFloat64( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, double DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, DefaultValue](const auto& v) -> double { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (maybeValue) { auto value = *maybeValue; return CesiumGltf::MetadataConversions:: convert(value) .value_or(DefaultValue); } return DefaultValue; }); } FIntPoint UCesiumPropertyAttributePropertyBlueprintLibrary::GetIntPoint( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, const FIntPoint& DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &DefaultValue](const auto& v) -> FIntPoint { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (!maybeValue) { return DefaultValue; } auto value = *maybeValue; if constexpr (CesiumGltf::IsMetadataString::value) { return UnrealMetadataConversions::toIntPoint(value, DefaultValue); } else { auto maybeVec2 = CesiumGltf:: MetadataConversions::convert(value); return maybeVec2 ? UnrealMetadataConversions::toIntPoint(*maybeVec2) : DefaultValue; } }); } FVector2D UCesiumPropertyAttributePropertyBlueprintLibrary::GetVector2D( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, const FVector2D& DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &DefaultValue](const auto& v) -> FVector2D { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (!maybeValue) { return DefaultValue; } auto value = *maybeValue; if constexpr (CesiumGltf::IsMetadataString::value) { return UnrealMetadataConversions::toVector2D(value, DefaultValue); } else { auto maybeVec2 = CesiumGltf:: MetadataConversions::convert(value); return maybeVec2 ? UnrealMetadataConversions::toVector2D(*maybeVec2) : DefaultValue; } }); } FIntVector UCesiumPropertyAttributePropertyBlueprintLibrary::GetIntVector( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, const FIntVector& DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &DefaultValue](const auto& v) -> FIntVector { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (!maybeValue) { return DefaultValue; } auto value = *maybeValue; if constexpr (CesiumGltf::IsMetadataString::value) { return UnrealMetadataConversions::toIntVector(value, DefaultValue); } else { auto maybeVec3 = CesiumGltf:: MetadataConversions::convert(value); return maybeVec3 ? UnrealMetadataConversions::toIntVector(*maybeVec3) : DefaultValue; } }); } FVector3f UCesiumPropertyAttributePropertyBlueprintLibrary::GetVector3f( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, const FVector3f& DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &DefaultValue](const auto& v) -> FVector3f { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (!maybeValue) { return DefaultValue; } auto value = *maybeValue; if constexpr (CesiumGltf::IsMetadataString::value) { return UnrealMetadataConversions::toVector3f(value, DefaultValue); } else { auto maybeVec3 = CesiumGltf:: MetadataConversions::convert(value); return maybeVec3 ? UnrealMetadataConversions::toVector3f(*maybeVec3) : DefaultValue; } }); } FVector UCesiumPropertyAttributePropertyBlueprintLibrary::GetVector( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, const FVector& DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &DefaultValue](const auto& v) -> FVector { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (!maybeValue) { return DefaultValue; } auto value = *maybeValue; if constexpr (CesiumGltf::IsMetadataString::value) { return UnrealMetadataConversions::toVector(value, DefaultValue); } else { auto maybeVec3 = CesiumGltf:: MetadataConversions::convert(value); return maybeVec3 ? UnrealMetadataConversions::toVector(*maybeVec3) : DefaultValue; } }); } FVector4 UCesiumPropertyAttributePropertyBlueprintLibrary::GetVector4( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, const FVector4& DefaultValue) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &DefaultValue](const auto& v) -> FVector4 { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return DefaultValue; } auto maybeValue = v.get(Index); if (!maybeValue) { return DefaultValue; } auto value = *maybeValue; if constexpr (CesiumGltf::IsMetadataString::value) { return UnrealMetadataConversions::toVector4(value, DefaultValue); } else { auto maybeVec4 = CesiumGltf:: MetadataConversions::convert(value); return maybeVec4 ? UnrealMetadataConversions::toVector4(*maybeVec4) : DefaultValue; } }); } FMatrix UCesiumPropertyAttributePropertyBlueprintLibrary::GetMatrix( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index, const FMatrix& DefaultValue) { auto maybeMat4 = propertyAttributePropertyCallback>( Property._property, Property._valueType, Property._normalized, [Index](const auto& v) -> std::optional { // size() returns zero if the view is invalid. if (Index < 0 || Index >= v.size()) { return std::nullopt; } auto maybeValue = v.get(Index); if (!maybeValue) { return std::nullopt; } auto value = *maybeValue; return CesiumGltf::MetadataConversions:: convert(value); }); return maybeMat4 ? UnrealMetadataConversions::toMatrix(*maybeMat4) : DefaultValue; } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetValue( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &pEnumDefinition = Property._pEnumDefinition]( const auto& view) -> FCesiumMetadataValue { // size() returns zero if the view is invalid. if (Index >= 0 && Index < view.size()) { return FCesiumMetadataValue(view.get(Index), pEnumDefinition); } return FCesiumMetadataValue(); }); } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetRawValue( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property, int64 Index) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [Index, &pEnumDefinition = Property._pEnumDefinition]( const auto& view) -> FCesiumMetadataValue { // Return an empty value if the property is empty. if (view.status() == CesiumGltf::PropertyAttributePropertyViewStatus:: EmptyPropertyWithDefault) { return FCesiumMetadataValue(); } // size() returns zero if the view is invalid. if (Index >= 0 && Index < view.size()) { return FCesiumMetadataValue( CesiumGltf::propertyValueViewToCopy(view.getRaw(Index)), pEnumDefinition); } return FCesiumMetadataValue(); }); } bool UCesiumPropertyAttributePropertyBlueprintLibrary::IsNormalized( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return Property._normalized; } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetOffset( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [](const auto& view) -> FCesiumMetadataValue { // Returns an empty value if no offset is specified. return FCesiumMetadataValue( CesiumGltf::propertyValueViewToCopy(view.offset())); }); } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetScale( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [](const auto& view) -> FCesiumMetadataValue { // Returns an empty value if no scale is specified. return FCesiumMetadataValue( CesiumGltf::propertyValueViewToCopy(view.scale())); }); } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetMinimumValue( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [](const auto& view) -> FCesiumMetadataValue { // Returns an empty value if no min is specified. return FCesiumMetadataValue( CesiumGltf::propertyValueViewToCopy(view.min())); }); } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetMaximumValue( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [](const auto& view) -> FCesiumMetadataValue { // Returns an empty value if no max is specified. return FCesiumMetadataValue( CesiumGltf::propertyValueViewToCopy(view.max())); }); } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetNoDataValue( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [](const auto& view) -> FCesiumMetadataValue { // Returns an empty value if no "no data" value is specified. return FCesiumMetadataValue( CesiumGltf::propertyValueViewToCopy(view.noData())); }); } FCesiumMetadataValue UCesiumPropertyAttributePropertyBlueprintLibrary::GetDefaultValue( UPARAM(ref) const FCesiumPropertyAttributeProperty& Property) { return propertyAttributePropertyCallback( Property._property, Property._valueType, Property._normalized, [](const auto& view) -> FCesiumMetadataValue { // Returns an empty value if no default value is specified. return FCesiumMetadataValue( CesiumGltf::propertyValueViewToCopy(view.defaultValue())); }); }