199 lines
5.1 KiB
C++
199 lines
5.1 KiB
C++
#pragma once
|
|
|
|
#include <psemek/gfx/gltf_parser.hpp>
|
|
#include <psemek/geom/scale.hpp>
|
|
#include <psemek/geom/rotation.hpp>
|
|
#include <psemek/geom/translation.hpp>
|
|
#include <psemek/util/exception.hpp>
|
|
|
|
#include <algorithm>
|
|
|
|
namespace psemek::gfx
|
|
{
|
|
|
|
namespace detail
|
|
{
|
|
|
|
template <gltf_asset::animation::channel::path_t path>
|
|
struct gltf_animation_traits;
|
|
|
|
template <>
|
|
struct gltf_animation_traits<gltf_asset::animation::channel::scale>
|
|
{
|
|
using output_type = geom::vector<float, 3>;
|
|
|
|
static output_type default_value()
|
|
{
|
|
return {1.f, 1.f, 1.f};
|
|
}
|
|
|
|
static output_type lerp(output_type const & v1, output_type const & v2, float t)
|
|
{
|
|
return geom::lerp(v1, v2, t);
|
|
}
|
|
|
|
static output_type normalize(output_type const & v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct gltf_animation_traits<gltf_asset::animation::channel::rotation>
|
|
{
|
|
using output_type = geom::quaternion<float>;
|
|
|
|
static output_type default_value()
|
|
{
|
|
return output_type::identity();
|
|
}
|
|
|
|
static output_type lerp(output_type const & v1, output_type const & v2, float t)
|
|
{
|
|
return geom::slerp(v1, v2, t);
|
|
}
|
|
|
|
static output_type normalize(output_type const & v)
|
|
{
|
|
return geom::normalized(v);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct gltf_animation_traits<gltf_asset::animation::channel::translation>
|
|
{
|
|
using output_type = geom::vector<float, 3>;
|
|
|
|
static output_type default_value()
|
|
{
|
|
return {0.f, 0.f, 0.f};
|
|
}
|
|
|
|
static output_type lerp(output_type const & v1, output_type const & v2, float t)
|
|
{
|
|
return geom::lerp(v1, v2, t);
|
|
}
|
|
|
|
static output_type normalize(output_type const & v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
template <gltf_asset::animation::channel::path_t path>
|
|
struct gltf_animation_channel
|
|
{
|
|
using traits = detail::gltf_animation_traits<path>;
|
|
using output_type = typename traits::output_type;
|
|
|
|
gltf_animation_channel()
|
|
: input_{0.f}
|
|
, output_{traits::default_value()}
|
|
, interpolation_{geom::easing_type::constant_left}
|
|
{}
|
|
|
|
gltf_animation_channel(std::vector<float> input, std::vector<output_type> output, geom::easing_type interpolation)
|
|
: input_(std::move(input))
|
|
, output_(std::move(output))
|
|
, interpolation_(interpolation)
|
|
{}
|
|
|
|
gltf_animation_channel(gltf_animation_channel &&) = default;
|
|
|
|
gltf_animation_channel & operator = (gltf_animation_channel &&) = default;
|
|
|
|
geom::interval<float> range() const
|
|
{
|
|
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 t = (time - input_[i - 1]) / (input_[i] - input_[i - 1]);
|
|
|
|
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 )
|
|
+ output_[3 * (i + 0) + 1] * (- 2.f * t3 + 3.f * t2 )
|
|
+ output_[3 * (i + 0) + 0] * ( t3 - t2 )
|
|
);
|
|
}
|
|
default:
|
|
throw util::exception("Unknown glTF animation interpolation type");
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::vector<float> input_;
|
|
std::vector<output_type> output_;
|
|
geom::easing_type interpolation_;
|
|
};
|
|
|
|
using gltf_scale_animation = gltf_animation_channel<gltf_asset::animation::channel::scale>;
|
|
using gltf_rotation_animation = gltf_animation_channel<gltf_asset::animation::channel::rotation>;
|
|
using gltf_translation_animation = gltf_animation_channel<gltf_asset::animation::channel::translation>;
|
|
|
|
struct gltf_animation
|
|
{
|
|
gltf_animation() = default;
|
|
|
|
gltf_animation(gltf_scale_animation scale, gltf_rotation_animation rotation, gltf_translation_animation translation)
|
|
: scale_(std::move(scale))
|
|
, rotation_(std::move(rotation))
|
|
, translation_(std::move(translation))
|
|
{}
|
|
|
|
geom::interval<float> range() const
|
|
{
|
|
return scale_.range() | rotation_.range() | translation_.range();
|
|
}
|
|
|
|
geom::affine_transform<float, 3, 3> operator()(float time) const
|
|
{
|
|
return geom::translation(translation_(time)).transform()
|
|
* geom::quaternion_rotation(rotation_(time)).transform()
|
|
* geom::scale(scale_(time)).transform();
|
|
}
|
|
|
|
gltf_scale_animation const & scale() const { return scale_; }
|
|
gltf_rotation_animation const & rotation() const { return rotation_; }
|
|
gltf_translation_animation const & translation() const { return translation_; }
|
|
|
|
private:
|
|
gltf_scale_animation scale_;
|
|
gltf_rotation_animation rotation_;
|
|
gltf_translation_animation translation_;
|
|
};
|
|
|
|
}
|