psemek/libs/gfx/source/gltf_animation.cpp

100 lines
2.9 KiB
C++

#include <psemek/gfx/gltf_animation.hpp>
namespace psemek::gfx
{
namespace detail
{
void gltf_animation_traits<gltf_asset::animation::channel::rotation>::canonicalize(geom::easing_type interpolation, std::vector<output_type> & 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_asset::animation::channel::path_t path>
gltf_animation_channel<path>::output_type gltf_animation_channel<path>::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<gltf_asset::animation::channel::scale>;
template struct gltf_animation_channel<gltf_asset::animation::channel::rotation>;
template struct gltf_animation_channel<gltf_asset::animation::channel::translation>;
geom::interval<float> gltf_animation::range() const
{
return scale_.range() | rotation_.range() | translation_.range();
}
geom::affine_transform<float, 3, 3> gltf_animation::operator()(float time) const
{
return geom::translation(translation_(time)).transform()
* geom::quaternion_rotation(rotation_(time)).transform()
* geom::scale(scale_(time)).transform();
}
}