diff --git a/include/fastgltf/dxmath_element_traits.hpp b/include/fastgltf/dxmath_element_traits.hpp index 7be1514bf..6d9632ff3 100644 --- a/include/fastgltf/dxmath_element_traits.hpp +++ b/include/fastgltf/dxmath_element_traits.hpp @@ -8,6 +8,9 @@ namespace fastgltf { +FASTGLTF_EXPORT template +using TransposedElementTraits = ElementTraitsBase; + template <> struct ElementTraits : ElementTraitsBase {}; @@ -17,4 +20,43 @@ struct ElementTraits : ElementTraitsBase struct ElementTraits : ElementTraitsBase {}; +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : ElementTraitsBase {}; + +template<> +struct ElementTraits : TransposedElementTraits {}; + +template<> +struct ElementTraits : TransposedElementTraits {}; + } // namespace fastgltf diff --git a/include/fastgltf/tools.hpp b/include/fastgltf/tools.hpp index 310df52df..6118a5066 100644 --- a/include/fastgltf/tools.hpp +++ b/include/fastgltf/tools.hpp @@ -107,12 +107,13 @@ struct ComponentTypeConverter { }; #endif -FASTGLTF_EXPORT template +FASTGLTF_EXPORT template struct ElementTraitsBase { using element_type = ElementType; using component_type = ComponentType; static constexpr auto type = EnumAccessorType; static constexpr auto enum_component_type = ComponentTypeConverter::type; + static constexpr auto needs_transpose = transpose; }; FASTGLTF_EXPORT template @@ -305,25 +306,35 @@ constexpr DestType convertComponent(const std::byte* bytes, std::size_t index, A return convertComponent(deserializeComponent(bytes, index), normalized); } +constexpr std::size_t getComponentIndex(std::size_t i, AccessorType type, bool transposed) { + if (transposed) { + auto n = getElementRowCount(type); + return (i % n) * n + (i / n); + } + + return i; +} + template -constexpr DestType getAccessorComponentAt(ComponentType componentType, AccessorType type, const std::byte* bytes, std::size_t componentIdx, bool normalized) { +constexpr DestType getAccessorComponentAt(ComponentType componentType, AccessorType type, const std::byte* bytes, std::size_t componentIdx, bool normalized = false, bool transposed = false) { + auto idx = getComponentIndex(componentIdx, type, transposed); switch (componentType) { case ComponentType::Byte: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::UnsignedByte: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::Short: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::UnsignedShort: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::Int: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::UnsignedInt: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::Float: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::Double: - return internal::convertComponent(bytes, componentIdx, type, normalized); + return internal::convertComponent(bytes, idx, type, normalized); case ComponentType::Invalid: default: return DestType {}; @@ -335,14 +346,15 @@ template requires Element #endif constexpr ElementType convertAccessorElement(const std::byte* bytes, bool normalized, std::index_sequence) { - using DestType = typename ElementTraits::component_type; - static_assert(std::is_arithmetic_v, "Accessor traits must provide a valid component type"); + using Traits = ElementTraits; + static_assert(std::is_arithmetic_v, "Accessor traits must provide a valid component type"); - constexpr auto accessorType = ElementTraits::type; if constexpr (std::is_aggregate_v) { - return {convertComponent(bytes, I, accessorType, normalized)...}; + return {convertComponent( + bytes, getComponentIndex(I, Traits::type, Traits::needs_transpose), Traits::type, normalized)...}; } else { - return ElementType(convertComponent(bytes, I, accessorType, normalized)...); + return ElementType(convertComponent( + bytes, getComponentIndex(I, Traits::type, Traits::needs_transpose), Traits::type, normalized)...); } } diff --git a/tests/accessor_tests.cpp b/tests/accessor_tests.cpp index f60ccbb6c..70f6e47c8 100644 --- a/tests/accessor_tests.cpp +++ b/tests/accessor_tests.cpp @@ -130,6 +130,25 @@ TEST_CASE("Test matrix data padding", "[gltf-tools]") { } } +TEST_CASE("Test matrix transpose", "[gltf-tools]") { + // First a case that doesn't require any padding + std::array mat2 {{ + 1, 2, + 3, 4 + }}; + REQUIRE(fastgltf::getElementByteSize(fastgltf::AccessorType::Mat2, fastgltf::ComponentType::Float) == mat2.size() * sizeof(float)); + + static constexpr std::array transposed {{ + 1, 3, 2, 4 + }}; + for (std::size_t i = 0; i < 4; ++i) { + REQUIRE(float(i + 1) == fastgltf::internal::getAccessorComponentAt( + fastgltf::ComponentType::Float, fastgltf::AccessorType::Mat2, reinterpret_cast(mat2.data()), i, false, false)); + REQUIRE(transposed[i] == fastgltf::internal::getAccessorComponentAt( + fastgltf::ComponentType::Float, fastgltf::AccessorType::Mat2, reinterpret_cast(mat2.data()), i, false, true)); + } +} + TEST_CASE("Test accessor", "[gltf-tools]") { auto lightsLamp = sampleModels / "2.0" / "LightsPunctualLamp" / "glTF";