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/gfx/color.hpp>
|
||||||
#include <psemek/geom/vector.hpp>
|
#include <psemek/geom/vector.hpp>
|
||||||
#include <psemek/geom/quaternion.hpp>
|
#include <psemek/geom/quaternion.hpp>
|
||||||
|
#include <psemek/geom/affine_transform.hpp>
|
||||||
#include <psemek/geom/easing.hpp>
|
#include <psemek/geom/easing.hpp>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -29,6 +30,7 @@ namespace psemek::gfx
|
||||||
geom::vector<float, 3> translation;
|
geom::vector<float, 3> translation;
|
||||||
geom::quaternion<float> rotation;
|
geom::quaternion<float> rotation;
|
||||||
geom::vector<float, 3> scale;
|
geom::vector<float, 3> scale;
|
||||||
|
geom::affine_transform<float, 3, 3> transform;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mesh
|
struct mesh
|
||||||
|
|
@ -156,8 +158,16 @@ namespace psemek::gfx
|
||||||
std::vector<buffer> buffers;
|
std::vector<buffer> buffers;
|
||||||
std::vector<light> lights; // KHR_lights_punctual
|
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> node_index; // node by name
|
||||||
std::unordered_map<std::string, std::size_t> material_index;
|
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);
|
gltf_asset parse_gltf(io::istream && stream);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
#include <psemek/gfx/gltf_parser.hpp>
|
#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 <psemek/util/to_string.hpp>
|
||||||
|
|
||||||
#include <boost/preprocessor/stringize.hpp>
|
#include <boost/preprocessor/stringize.hpp>
|
||||||
|
|
@ -122,6 +125,10 @@ namespace psemek::gfx
|
||||||
target.scale[i] = scale[i].GetFloat();
|
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"))
|
if (node.HasMember("children"))
|
||||||
{
|
{
|
||||||
for (auto const & child : node["children"].GetArray())
|
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)
|
for (std::size_t i = 0; i < result.materials.size(); ++i)
|
||||||
result.material_index[result.materials[i].name] = 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue