2#include <hpp/utility/overload.hpp>
24 int high = (int)keys.size(),
low = -1;
28 if(keys[probe].time < time)
41 return keys.front().value;
44 if(
high == (
int)keys.size())
47 return keys.back().value;
50 const auto& key1 = keys[
low];
51 const auto& key2 = keys[
low + 1];
54 float factor = (time.count() - key1.time.count()) / (key2.time.count() - key1.time.count());
57 if constexpr(std::is_same_v<T, math::vec3>)
59 return math::lerp(key1.value, key2.value, factor);
61 else if constexpr(std::is_same_v<T, math::quat>)
63 return math::slerp(key1.value, key2.value, factor);
70auto animation_player::get_layer(
size_t index) -> animation_layer&
72 if(index >= layers_.size())
74 layers_.resize(index + 1);
76 return layers_[index];
80 auto&
layer = get_layer(layer_idx);
91 auto&
layer = get_layer(layer_idx);
94 if(
layer.current_state.state.clip)
96 layer.current_state = {};
100 layer.target_state.state.loop = loop;
102 if(
layer.target_state.state.clip == clip)
107 if(
layer.current_state.state.clip == clip)
112 layer.target_state.state.clip = clip;
113 auto phase = phase_sync ?
layer.current_state.state.get_progress() : 0.0f;
114 layer.target_state.state.set_progress(phase);
117 if(duration > clip.
get()->duration)
119 duration = clip.
get()->duration;
124 layer.blending_state.easing = easing;
129 auto&
layer = get_layer(layer_idx);
131 layer.current_state.state.loop = loop;
133 if(
layer.current_state.state.blend_space == blend_space)
138 layer.current_state.state.blend_space = blend_space;
141 layer.target_state = {};
142 layer.blending_state = {};
147 auto&
layer = get_layer(layer_idx);
148 layer.current_state.parameters = params;
178 for(
auto&
layer : layers_)
192 bool any_valid =
false;
193 for(
auto&
layer : layers_)
195 any_valid |=
layer.current_state.is_valid();
196 any_valid |=
layer.target_state.is_valid();
210 if(playing_ && !paused_)
212 for(
auto&
layer : layers_)
214 update_state(delta_time,
layer.current_state.state);
216 update_state(delta_time,
layer.target_state.state);
219 hpp::visit(hpp::overload(
228 layer.blending_state.state);
243 for(
auto&
layer : layers_)
246 update_pose(
layer.current_state);
249 if(update_pose(
layer.target_state))
252 float blend_progress = get_blend_progress(
layer);
253 float blend_factor = compute_blend_factor(
layer, blend_progress);
259 if(blend_progress >= 1.0f)
263 layer.target_state = {};
264 layer.blending_state = {};
269 if(layers_.size() == 1)
271 auto final_pose = layers_.front().get_final_pose();
274 for(
const auto& node : final_pose->nodes)
276 set_transform_callback(node.desc, node.transform, final_pose->motion_result);
282 blend_poses_additive(*layers_[0].get_final_pose(), *layers_[1].get_final_pose(), ref_pose, 1.0f, final_pose);
284 for(
size_t i = 2; i < layers_.size(); ++i)
287 blend_poses_additive(final_pose, *layers_[i].get_final_pose(), ref_pose, 1.0f, next_final_pose);
289 final_pose = next_final_pose;
293 for(
const auto& node : final_pose.nodes)
295 set_transform_callback(node.desc, node.transform, final_pose.motion_result);
300auto animation_player::update_pose(animation_layer_state&
layer) ->
bool
302 auto& state =
layer.state;
303 auto& pose =
layer.pose;
304 auto& parameters =
layer.parameters;
306 if(state.blend_space)
309 state.blend_space->compute_blend(parameters, state.blend_clips);
312 state.blend_poses.resize(state.blend_clips.size());
313 for(
size_t i = 0; i < state.blend_clips.size(); ++i)
315 const auto& clip_weight_pair = state.blend_clips[i];
316 sample_animation(clip_weight_pair.first.get().get(), state.elapsed, state.blend_poses[i]);
321 if(!state.blend_poses.empty())
324 pose = state.blend_poses[0];
325 float total_weight = state.blend_clips[0].second;
327 for(
size_t i = 1; i < state.blend_poses.size(); ++i)
330 state.blend_poses[i],
331 state.blend_clips[i].second / (total_weight + state.blend_clips[i].second),
333 total_weight += state.blend_clips[i].second;
341 sample_animation(state.clip.get().get(), state.elapsed, pose);
348void animation_player::update_state(seconds_t delta_time, animation_state& state)
352 state.elapsed += delta_time;
353 auto target_anim = state.clip.get();
356 if(state.elapsed > target_anim->duration)
360 state.elapsed =
seconds_t(std::fmod(state.elapsed.count(), target_anim->duration.count()));
364 state.elapsed = target_anim->duration;
372auto animation_player::get_blend_progress(
const animation_layer&
layer)
const ->
float
374 return hpp::visit(hpp::overload(
375 [](
const hpp::monostate& state)
379 [](
const auto& state)
381 return state.get_progress();
383 layer.blending_state.state);
386auto animation_player::compute_blend_factor(
const animation_layer&
layer,
float normalized_blend_time)
noexcept ->
float
388 float blend_factor = 0.0f;
391 blend_factor =
layer.blending_state.easing(normalized_blend_time);
394 if(normalized_blend_time >= 1.0f)
403void animation_player::sample_animation(
const animation_clip* anim_clip,
405 animation_pose& pose)
const noexcept
412 pose.nodes.reserve(anim_clip->channels.size());
414 for(
const auto& channel : anim_clip->channels)
416 math::vec3 position = interpolate(channel.position_keys, time);
417 math::quat rotation = interpolate(channel.rotation_keys, time);
418 math::vec3 scaling = interpolate(channel.scaling_keys, time);
420 auto& node = pose.nodes.emplace_back();
421 node.desc.index = channel.node_index;
423 node.transform.set_position(position);
424 node.transform.set_rotation(rotation);
425 node.transform.set_scale(scaling);
427 bool processed =
false;
429 if(
int(node.desc.index) == anim_clip->root_motion.position_node_index)
431 pose.motion_result.root_position_node_index = anim_clip->root_motion.position_node_index;
433 const auto& clip_start_pos = channel.position_keys.front().value;
434 const auto& clip_end_pos = channel.position_keys.back().value;
436 pose.motion_result.root_position_weights = {1.0f, 1.0f, 1.0f};
437 pose.motion_result.bone_position_weights = {0.0f, 0.0f, 0.0f};
439 if(pose.motion_state.root_position_time == seconds_t(0))
441 pose.motion_state.root_position_time = time;
442 pose.motion_state.root_position_at_time = clip_start_pos;
445 auto delta_position = position - pose.motion_state.root_position_at_time;
447 if(time < pose.motion_state.root_position_time)
449 auto loop_pos_offset = clip_end_pos - clip_start_pos;
450 delta_position = delta_position + loop_pos_offset;
455 if(anim_clip->root_motion.keep_position_y)
457 pose.motion_result.root_position_weights.y = 0.0f;
458 pose.motion_result.bone_position_weights.y = 1.0f;
462 if(anim_clip->root_motion.keep_position_xz)
464 pose.motion_result.root_position_weights.x = 0.0f;
465 pose.motion_result.root_position_weights.z = 0.0f;
467 pose.motion_result.bone_position_weights.x = 1.0f;
468 pose.motion_result.bone_position_weights.z = 1.0f;
471 if(anim_clip->root_motion.keep_in_place)
473 pose.motion_result.root_position_weights.y = 0.0f;
474 pose.motion_result.root_position_weights.x = 0.0f;
475 pose.motion_result.root_position_weights.z = 0.0f;
477 pose.motion_result.bone_position_weights.y = 1.0f;
478 pose.motion_result.bone_position_weights.x = 0.0f;
479 pose.motion_result.bone_position_weights.z = 0.0f;
482 pose.motion_state.root_position_time = time;
483 pose.motion_state.root_position_at_time = position;
484 pose.motion_result.root_transform_delta.set_position(delta_position);
487 if(
int(node.desc.index) == anim_clip->root_motion.rotation_node_index)
489 pose.motion_result.root_rotation_node_index = anim_clip->root_motion.rotation_node_index;
491 const auto& clip_start_rotation = channel.rotation_keys.front().value;
492 const auto& clip_end_rotation = channel.rotation_keys.back().value;
494 pose.motion_result.root_rotation_weight = {1.0f};
495 pose.motion_result.bone_rotation_weight = {0.0f};
497 if(pose.motion_state.root_rotation_time == seconds_t(0))
499 pose.motion_state.root_rotation_time = time;
501 pose.motion_state.root_rotation_at_time = clip_start_rotation;
504 auto delta_rotation = rotation * glm::inverse(pose.motion_state.root_rotation_at_time);
506 if(time < pose.motion_state.root_rotation_time)
508 auto loop_rotation_offset = clip_end_rotation * glm::inverse(clip_start_rotation);
509 delta_rotation = loop_rotation_offset * delta_rotation;
512 if(anim_clip->root_motion.keep_rotation)
514 pose.motion_result.root_rotation_weight = 0.0f;
515 pose.motion_result.bone_rotation_weight = 1.0f;
519 if(anim_clip->root_motion.keep_in_place)
521 pose.motion_result.root_rotation_weight = 0.0f;
522 pose.motion_result.bone_rotation_weight = 1.0f;
525 pose.motion_state.root_rotation_time = time;
526 pose.motion_state.root_rotation_at_time = rotation;
527 pose.motion_result.root_transform_delta.set_rotation(delta_rotation);
534 return playing_ && !paused_;
auto play() -> bool
Starts or resumes the animation playback.
void pause()
Pauses the animation playback.
animation_clip::seconds_t seconds_t
void clear(size_t layer_idx)
void set_blend_space(size_t layer_idx, const std::shared_ptr< blend_space_def > &blend_space, bool loop=true)
void stop()
Stops the animation playback and resets the time.
std::function< void(const animation_pose::node_desc &desc, const math::transform &abs, const animation_pose::root_motion_result &motion_result)> update_callback_t
auto is_playing() const -> bool
Returns whether the animation is currently playing.
void blend_to(size_t layer_idx, const asset_handle< animation_clip > &clip, seconds_t duration=seconds_t(0.3), bool loop=true, bool phase_sync=false, const blend_easing_t &easing=math::linearInterpolation< float >)
Blends to the animation over the specified time with the specified easing.
void set_blend_space_parameters(size_t layer_idx, const std::vector< float > ¶ms)
void resume()
Resumes the animation playback.
auto update_time(seconds_t delta_time, bool force=false) -> bool
Updates the animation player, advancing the animation time and applying transformations.
void update_poses(const animation_pose &ref_pose, const update_callback_t &set_transform_callback)
auto is_paused() const -> bool
Returns whether the animation is currently paused.
std::function< float(float)> blend_easing_t
void blend_poses(const animation_pose &pose1, const animation_pose &pose2, float factor, animation_pose &result_pose)
void blend_poses_additive(const animation_pose &base, const animation_pose &additive, const animation_pose &ref_pose, float weight, animation_pose &result)
Represents a handle to an asset, providing access and management functions.
auto get(bool wait=true) const -> std::shared_ptr< T >
Gets the shared pointer to the asset.
animation_clip::seconds_t elapsed