Move heavy gltf animation code to a cpp file

This commit is contained in:
Nikita Lisitsa 2024-11-14 21:45:08 +03:00
parent fbb032fe63
commit 5138e00c35
2 changed files with 116 additions and 80 deletions

View file

@ -36,6 +36,9 @@ namespace psemek::gfx
{
return v;
}
static void canonicalize(geom::easing_type, std::vector<output_type> &)
{}
};
template <>
@ -57,6 +60,8 @@ namespace psemek::gfx
{
return geom::normalized(v);
}
static void canonicalize(geom::easing_type interpolation, std::vector<output_type> & output);
};
template <>
@ -78,6 +83,9 @@ namespace psemek::gfx
{
return v;
}
static void canonicalize(geom::easing_type, std::vector<output_type> &)
{}
};
}
@ -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<float> input_;
@ -187,6 +127,10 @@ namespace psemek::gfx
geom::easing_type interpolation_;
};
extern template struct gltf_animation_channel<gltf_asset::animation::channel::scale>;
extern template struct gltf_animation_channel<gltf_asset::animation::channel::rotation>;
extern template struct gltf_animation_channel<gltf_asset::animation::channel::translation>;
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>;
@ -201,17 +145,9 @@ namespace psemek::gfx
, translation_(std::move(translation))
{}
geom::interval<float> range() const
{
return scale_.range() | rotation_.range() | translation_.range();
}
geom::interval<float> range() const;
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();
}
geom::affine_transform<float, 3, 3> operator()(float time) const;
gltf_scale_animation const & scale() const { return scale_; }
gltf_rotation_animation const & rotation() const { return rotation_; }

View file

@ -0,0 +1,100 @@
#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();
}
}