diff --git a/libs/gfx/include/psemek/gfx/gltf_animation.hpp b/libs/gfx/include/psemek/gfx/gltf_animation.hpp index dcdb1a63..bbb60618 100644 --- a/libs/gfx/include/psemek/gfx/gltf_animation.hpp +++ b/libs/gfx/include/psemek/gfx/gltf_animation.hpp @@ -36,6 +36,9 @@ namespace psemek::gfx { return v; } + + static void canonicalize(geom::easing_type, std::vector &) + {} }; template <> @@ -57,6 +60,8 @@ namespace psemek::gfx { return geom::normalized(v); } + + static void canonicalize(geom::easing_type interpolation, std::vector & output); }; template <> @@ -78,6 +83,9 @@ namespace psemek::gfx { return v; } + + static void canonicalize(geom::easing_type, std::vector &) + {} }; } @@ -99,31 +107,7 @@ namespace psemek::gfx , output_(std::move(output)) , interpolation_(interpolation) { - if constexpr (path == gltf_asset::animation::channel::rotation) - { - if (interpolation == geom::easing_type::linear) - { - for (std::size_t i = 1; i < input_.size(); ++i) - { - if (geom::dot(output_[i - 1].coords, output_[i].coords) < 0.f) - { - output_[i] = - output_[i]; - } - } - } - else if (interpolation == geom::easing_type::cubic) - { - for (std::size_t i = 1; i < input_.size(); ++i) - { - if (geom::dot(output_[3 * (i - 1) + 1].coords, output_[3 * i + 1].coords) < 0.f) - { - output_[3 * i + 0] = - output_[3 * i + 0]; - output_[3 * i + 1] = - output_[3 * i + 1]; - output_[3 * i + 2] = - output_[3 * i + 2]; - } - } - } - } + traits::canonicalize(interpolation_, output_); } gltf_animation_channel(gltf_animation_channel &&) = default; @@ -135,51 +119,7 @@ namespace psemek::gfx return {input_.front(), input_.back()}; } - output_type operator()(float time) const - { - auto it = std::lower_bound(input_.begin(), input_.end(), time); - if (it == input_.begin()) - { - if (interpolation_ == geom::easing_type::cubic) - return output_[1]; - else - return output_.front(); - } - if (it == input_.end()) - { - if (interpolation_ == geom::easing_type::cubic) - return output_[output_.size() - 2]; - else - return output_.back(); - } - - auto i = it - input_.begin(); - - float td = (input_[i] - input_[i - 1]); - float t = (time - input_[i - 1]) / td; - - switch (interpolation_) - { - case geom::easing_type::constant_left: - return output_[i - 1]; - case geom::easing_type::linear: - return traits::lerp(output_[i - 1], output_[i], t); - case geom::easing_type::cubic: - { - // see https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_007_Animations.html#cubic-spline-interpolation - float t2 = t * t; - float t3 = t * t2; - return traits::normalize(output_type::zero() - + output_[3 * (i - 1) + 1] * ( 2.f * t3 - 3.f * t2 + 1.f) - + output_[3 * (i - 1) + 2] * ( t3 - 2.f * t2 + t ) * td - + output_[3 * (i + 0) + 1] * (- 2.f * t3 + 3.f * t2 ) - + output_[3 * (i + 0) + 0] * ( t3 - t2 ) * td - ); - } - default: - throw util::exception("Unknown glTF animation interpolation type"); - } - } + output_type operator()(float time) const; private: std::vector input_; @@ -187,6 +127,10 @@ namespace psemek::gfx geom::easing_type interpolation_; }; + extern template struct gltf_animation_channel; + extern template struct gltf_animation_channel; + extern template struct gltf_animation_channel; + using gltf_scale_animation = gltf_animation_channel; using gltf_rotation_animation = gltf_animation_channel; using gltf_translation_animation = gltf_animation_channel; @@ -201,17 +145,9 @@ namespace psemek::gfx , translation_(std::move(translation)) {} - geom::interval range() const - { - return scale_.range() | rotation_.range() | translation_.range(); - } + geom::interval range() const; - geom::affine_transform operator()(float time) const - { - return geom::translation(translation_(time)).transform() - * geom::quaternion_rotation(rotation_(time)).transform() - * geom::scale(scale_(time)).transform(); - } + geom::affine_transform operator()(float time) const; gltf_scale_animation const & scale() const { return scale_; } gltf_rotation_animation const & rotation() const { return rotation_; } diff --git a/libs/gfx/source/gltf_animation.cpp b/libs/gfx/source/gltf_animation.cpp new file mode 100644 index 00000000..a9c9fbc4 --- /dev/null +++ b/libs/gfx/source/gltf_animation.cpp @@ -0,0 +1,100 @@ +#include + +namespace psemek::gfx +{ + + namespace detail + { + + void gltf_animation_traits::canonicalize(geom::easing_type interpolation, std::vector & output) + { + if (interpolation == geom::easing_type::linear) + { + for (std::size_t i = 1; i < output.size(); ++i) + { + if (geom::dot(output[i - 1].coords, output[i].coords) < 0.f) + { + output[i] = - output[i]; + } + } + } + else if (interpolation == geom::easing_type::cubic) + { + for (std::size_t i = 3; i < output.size(); i += 3) + { + if (geom::dot(output[(i - 3) + 1].coords, output[i + 1].coords) < 0.f) + { + output[i + 0] = - output[i + 0]; + output[i + 1] = - output[i + 1]; + output[i + 2] = - output[i + 2]; + } + } + } + } + + } + + template + gltf_animation_channel::output_type gltf_animation_channel::operator()(float time) const + { + auto it = std::lower_bound(input_.begin(), input_.end(), time); + if (it == input_.begin()) + { + if (interpolation_ == geom::easing_type::cubic) + return output_[1]; + else + return output_.front(); + } + if (it == input_.end()) + { + if (interpolation_ == geom::easing_type::cubic) + return output_[output_.size() - 2]; + else + return output_.back(); + } + + auto i = it - input_.begin(); + + float td = (input_[i] - input_[i - 1]); + float t = (time - input_[i - 1]) / td; + + switch (interpolation_) + { + case geom::easing_type::constant_left: + return output_[i - 1]; + case geom::easing_type::linear: + return traits::lerp(output_[i - 1], output_[i], t); + case geom::easing_type::cubic: + { + // see https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_007_Animations.html#cubic-spline-interpolation + float t2 = t * t; + float t3 = t * t2; + return traits::normalize(output_type::zero() + + output_[3 * (i - 1) + 1] * ( 2.f * t3 - 3.f * t2 + 1.f) + + output_[3 * (i - 1) + 2] * ( t3 - 2.f * t2 + t ) * td + + output_[3 * (i + 0) + 1] * (- 2.f * t3 + 3.f * t2 ) + + output_[3 * (i + 0) + 0] * ( t3 - t2 ) * td + ); + } + default: + throw util::exception("Unknown glTF animation interpolation type"); + } + } + + template struct gltf_animation_channel; + template struct gltf_animation_channel; + template struct gltf_animation_channel; + + geom::interval gltf_animation::range() const + { + return scale_.range() | rotation_.range() | translation_.range(); + } + + geom::affine_transform gltf_animation::operator()(float time) const + { + return geom::translation(translation_(time)).transform() + * geom::quaternion_rotation(rotation_(time)).transform() + * geom::scale(scale_(time)).transform(); + } + +}