#pragma once #include #include #include #include #include #include namespace CesiumGltf { /** * @brief Normalizes the given value between [0, 1] if unsigned or [-1, 1] if * signed, based on the type's maximum value. * * @param value The value to normalize. */ 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; } } /** * @brief Normalizes the given vector's components between [0, 1] if unsigned or * [-1, 1] if signed, based on the type's maximum value. * * @param value The value to normalize. */ 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; } } /** * @brief Normalizes the given matrix's components between [0, 1] if unsigned or * [-1, 1] if signed, based on the type's maximum value. * * @param value The value to normalize. */ 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; } /** * @brief Multiplies each component of the value by the given scale factor. * * @param value The value to scale. * @param scale The scale factor to apply to the value. */ 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; } } /** * @brief Transforms the value by optional offset and scale factors. * * @param value The value to transform. * @param offset The amount to offset the value by, or `std::nullopt` to apply * no offset. * @param scale The amount to scale the value by, or `std::nullopt` to apply no * scale. See \ref applyScale. */ template T transformValue( 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; } /** * @brief Transforms each element of an array of values by optional offset and * scale factors. See \ref transformValue. * * @param value The array whose elements will be transformed. * @param offset The amount to offset each element by, or `std::nullopt` to * apply no offset. * @param scale The amount to scale each element by, or `std::nullopt` to apply * no scale factor. * @returns A transformed copy of the input array. */ template PropertyArrayCopy transformArray( 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++) { const size_t ui = static_cast(i); result[ui] = value[i]; if (scale) { result[ui] = applyScale(result[ui], (*scale)[i]); } if (offset) { result[ui] = result[ui] + (*offset)[i]; } } return PropertyArrayCopy(std::move(result)); } /** * @brief Normalizes each element of an array of values and transforms them by * optional offset and scale factors. See \ref transformValue and \ref * transformArray. * * @param value The array whose elements will be transformed. * @param offset The amount to offset each element by, or `std::nullopt` to * apply no offset. The offset will be applied after normalization. * @param scale The amount to scale each element by, or `std::nullopt` to apply * no scale factor. The scale will be applied after normalization. * @returns A normalized and transformed copy of the input array. */ template < typename T, typename NormalizedType = typename TypeToNormalizedType::type> PropertyArrayCopy 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++) { const size_t ui = static_cast(i); result[ui] = normalize(value[i]); if (scale) { result[ui] = result[ui] * (*scale)[i]; } if (offset) { result[ui] = result[ui] + (*offset)[i]; } } return PropertyArrayCopy(std::move(result)); } /** * @brief Normalizes each element of an array of vectors and transforms them by * optional offset and scale factors. See \ref transformNormalizedArray. * * @param value The array whose elements will be transformed. * @param offset The amount to offset each element by, or `std::nullopt` to * apply no offset. The offset will be applied after normalization. * @param scale The amount to scale each element by, or `std::nullopt` to apply * no scale factor. The scale will be applied after normalization. * @returns A normalized and transformed copy of the input array. */ template PropertyArrayCopy> 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++) { const size_t ui = static_cast(i); result[ui] = normalize(value[i]); if (scale) { result[ui] = result[ui] * (*scale)[i]; } if (offset) { result[ui] = result[ui] + (*offset)[i]; } } return PropertyArrayCopy(std::move(result)); } /** * @brief Normalizes each element of an array of matrices and transforms them by * optional offset and scale factors. See \ref transformNormalizedArray. * * @param value The array whose elements will be transformed. * @param offset The amount to offset each element by, or `std::nullopt` to * apply no offset. The offset will be applied after normalization. * @param scale The amount to scale each element by, or `std::nullopt` to apply * no scale factor. The scale will be applied after normalization. * @returns A normalized and transformed copy of the input array. */ template PropertyArrayCopy> 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++) { const size_t ui = static_cast(i); result[ui] = normalize(value[i]); if (scale) { result[ui] = applyScale>(result[ui], (*scale)[i]); } if (offset) { result[ui] = result[ui] + (*offset)[i]; } } return PropertyArrayCopy(std::move(result)); } } // namespace CesiumGltf