#include namespace psemek::gfx { namespace detail { void gltf_animation_traits::canonicalize(math::easing_type interpolation, std::vector & output) { if (interpolation == math::easing_type::linear) { for (std::size_t i = 1; i < output.size(); ++i) { if (math::dot(output[i - 1].coords, output[i].coords) < 0.f) { output[i] = - output[i]; } } } else if (interpolation == math::easing_type::cubic) { for (std::size_t i = 3; i < output.size(); i += 3) { if (math::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_ == math::easing_type::cubic) return output_[1]; else return output_.front(); } if (it == input_.end()) { if (interpolation_ == math::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 math::easing_type::constant_left: return output_[i - 1]; case math::easing_type::linear: return traits::lerp(output_[i - 1], output_[i], t); case math::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; math::interval gltf_animation::range() const { return scale_.range() | rotation_.range() | translation_.range(); } math::affine_transform gltf_animation::operator()(float time) const { return math::translation(translation_(time)).transform() * math::quaternion_rotation(rotation_(time)).transform() * math::scale(scale_(time)).transform(); } }