glTF animations
This commit is contained in:
parent
c26f626baf
commit
063e8e43ba
3 changed files with 193 additions and 2 deletions
169
libs/gfx/include/psemek/gfx/gltf_animation.hpp
Normal file
169
libs/gfx/include/psemek/gfx/gltf_animation.hpp
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/gltf_parser.hpp>
|
||||
#include <psemek/geom/scale.hpp>
|
||||
#include <psemek/geom/rotation.hpp>
|
||||
#include <psemek/geom/translation.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);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
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())
|
||||
return output_.back();
|
||||
if (it == input_.end())
|
||||
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::default_value()
|
||||
+ 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 std::runtime_error("unsupported 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();
|
||||
}
|
||||
|
||||
private:
|
||||
gltf_scale_animation scale_;
|
||||
gltf_rotation_animation rotation_;
|
||||
gltf_translation_animation translation_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <psemek/gfx/color.hpp>
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/quaternion.hpp>
|
||||
#include <psemek/geom/affine_transform.hpp>
|
||||
#include <psemek/geom/easing.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
|
@ -29,6 +30,7 @@ namespace psemek::gfx
|
|||
geom::vector<float, 3> translation;
|
||||
geom::quaternion<float> rotation;
|
||||
geom::vector<float, 3> scale;
|
||||
geom::affine_transform<float, 3, 3> transform;
|
||||
};
|
||||
|
||||
struct mesh
|
||||
|
|
@ -156,8 +158,16 @@ namespace psemek::gfx
|
|||
std::vector<buffer> buffers;
|
||||
std::vector<light> lights; // KHR_lights_punctual
|
||||
|
||||
std::unordered_map<std::string, std::size_t> node_index;
|
||||
std::unordered_map<std::string, std::size_t> material_index;
|
||||
std::unordered_map<std::string, std::size_t> node_index; // node by name
|
||||
std::unordered_map<std::string, std::size_t> material_index; // material by name
|
||||
|
||||
struct animation_and_channel
|
||||
{
|
||||
std::size_t animation;
|
||||
std::size_t channel;
|
||||
};
|
||||
|
||||
std::vector<std::vector<animation_and_channel>> node_animations; // node to list of animation channels that affect it
|
||||
};
|
||||
|
||||
gltf_asset parse_gltf(io::istream && stream);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
#include <psemek/gfx/gltf_parser.hpp>
|
||||
#include <psemek/geom/scale.hpp>
|
||||
#include <psemek/geom/rotation.hpp>
|
||||
#include <psemek/geom/translation.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
|
@ -122,6 +125,10 @@ namespace psemek::gfx
|
|||
target.scale[i] = scale[i].GetFloat();
|
||||
}
|
||||
|
||||
target.transform = geom::translation(target.translation).transform()
|
||||
* geom::quaternion_rotation(target.rotation).transform()
|
||||
* geom::scale(target.scale).transform();
|
||||
|
||||
if (node.HasMember("children"))
|
||||
{
|
||||
for (auto const & child : node["children"].GetArray())
|
||||
|
|
@ -357,6 +364,11 @@ namespace psemek::gfx
|
|||
for (std::size_t i = 0; i < result.materials.size(); ++i)
|
||||
result.material_index[result.materials[i].name] = i;
|
||||
|
||||
result.node_animations.resize(result.nodes.size());
|
||||
for (std::size_t i = 0; i < result.animations.size(); ++i)
|
||||
for (std::size_t j = 0; j < result.animations[i].channels.size(); ++j)
|
||||
result.node_animations[result.animations[i].channels[j].target].push_back({i, j});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue