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 = {};
102 if(
layer.target_state.state.clip == clip)
104 layer.target_state.state.loop = loop;
109 if(
layer.current_state.state.clip == clip)
112 if(!
layer.target_state.state.clip)
114 layer.current_state.state.loop = loop;
121 std::swap(
layer.current_state,
layer.target_state);
122 layer.current_state.state.loop = loop;
124 if(
auto* bot = std::get_if<blend_over_time>(&
layer.blending_state.state))
126 float progress_before = 0.0f;
127 if(bot->duration.count() > 0.0f)
129 progress_before = float(bot->elapsed.count() / bot->duration.count());
130 progress_before = std::clamp(progress_before, 0.0f, 1.0f);
133 if(progress_before > 1e-4f)
138 auto stretched =
seconds_t(duration.count() / progress_before);
139 bot->duration = stretched;
140 bot->elapsed =
seconds_t(stretched.count() * (1.0f - progress_before));
144 bot->duration = duration;
153 layer.blending_state.easing = easing;
158 layer.target_state.state.clip = clip;
159 layer.target_state.state.loop = loop;
161 auto phase = phase_sync ?
layer.current_state.state.get_progress() : 0.0f;
164 if(duration > clip.
get()->duration)
166 duration = clip.
get()->duration;
170 layer.blending_state.easing = easing;
175 auto&
layer = get_layer(layer_idx);
177 layer.current_state.state.loop = loop;
179 if(
layer.current_state.state.blend_space == blend_space)
184 layer.current_state.state.blend_space = blend_space;
187 layer.target_state = {};
188 layer.blending_state = {};
193 auto&
layer = get_layer(layer_idx);
194 layer.current_state.parameters = params;
224 for(
auto&
layer : layers_)
238 bool any_valid =
false;
239 for(
auto&
layer : layers_)
241 any_valid |=
layer.current_state.is_valid();
242 any_valid |=
layer.target_state.is_valid();
256 if(playing_ && !paused_)
258 for(
auto&
layer : layers_)
260 update_state(delta_time,
layer.current_state.state);
262 update_state(delta_time,
layer.target_state.state);
265 hpp::visit(hpp::overload(
274 layer.blending_state.state);
289 for(
auto&
layer : layers_)
292 update_pose(
layer.current_state, retargeting_mode);
295 if(update_pose(
layer.target_state, retargeting_mode))
298 float blend_progress = get_blend_progress(
layer);
299 float blend_factor = compute_blend_factor(
layer, blend_progress);
305 if(blend_progress >= 1.0f)
309 layer.target_state = {};
310 layer.blending_state = {};
315 if(layers_.size() == 1)
317 auto final_pose = layers_.front().get_final_pose();
320 for(
const auto& node : final_pose->nodes)
322 set_transform_callback(node.desc, node.transform, final_pose->motion_result);
328 blend_poses_additive(*layers_[0].get_final_pose(), *layers_[1].get_final_pose(), ref_pose, 1.0f, final_pose);
330 for(
size_t i = 2; i < layers_.size(); ++i)
333 blend_poses_additive(final_pose, *layers_[i].get_final_pose(), ref_pose, 1.0f, next_final_pose);
335 final_pose = next_final_pose;
339 for(
const auto& node : final_pose.nodes)
341 set_transform_callback(node.desc, node.transform, final_pose.motion_result);
348 auto& state =
layer.state;
350 auto& parameters =
layer.parameters;
352 if(state.blend_space)
355 state.blend_space->compute_blend(parameters, state.blend_clips);
358 state.blend_poses.resize(state.blend_clips.size());
359 for(
size_t i = 0; i < state.blend_clips.size(); ++i)
361 const auto& clip_weight_pair = state.blend_clips[i];
362 sample_animation(clip_weight_pair.first.get().get(), state.elapsed, retargeting_mode, state.blend_poses[i]);
367 if(!state.blend_poses.empty())
370 pose = state.blend_poses[0];
371 float total_weight = state.blend_clips[0].second;
373 for(
size_t i = 1; i < state.blend_poses.size(); ++i)
376 state.blend_poses[i],
377 state.blend_clips[i].second / (total_weight + state.blend_clips[i].second),
379 total_weight += state.blend_clips[i].second;
387 sample_animation(state.clip.get().get(), state.elapsed, retargeting_mode,
pose);
394void animation_player::update_state(seconds_t delta_time, animation_state& state)
398 state.elapsed += delta_time;
399 auto target_anim = state.clip.get();
402 if(state.elapsed > target_anim->duration)
406 state.elapsed =
seconds_t(std::fmod(state.elapsed.count(), target_anim->duration.count()));
410 state.elapsed = target_anim->duration;
418auto animation_player::get_blend_progress(
const animation_layer&
layer)
const ->
float
420 return hpp::visit(hpp::overload(
421 [](
const hpp::monostate& state)
425 [](
const auto& state)
427 return state.get_progress();
429 layer.blending_state.state);
432auto animation_player::compute_blend_factor(
const animation_layer&
layer,
float normalized_blend_time)
noexcept ->
float
434 float blend_factor = 0.0f;
437 blend_factor =
layer.blending_state.easing(normalized_blend_time);
440 if(normalized_blend_time >= 1.0f)
449void animation_player::sample_animation(
const animation_clip* anim_clip,
452 animation_pose&
pose)
const noexcept
459 pose.nodes.reserve(anim_clip->channels.size());
461 for(
const auto& channel : anim_clip->channels)
463 math::vec3
position = interpolate(channel.position_keys, time);
464 math::quat
rotation = interpolate(channel.rotation_keys, time);
465 math::vec3 scaling = interpolate(channel.scaling_keys, time);
467 auto& node =
pose.nodes.emplace_back();
470 node.desc.index = channel.node_index;
471 node.desc.name = channel.node_name;
472 node.transform.set_position(
position);
473 node.transform.set_rotation(
rotation);
474 node.transform.set_scale(scaling);
478 bool is_root_position_node =
false;
479 bool is_root_rotation_node =
false;
484 if(node.desc.name == anim_clip->root_motion.position_node_name)
486 is_root_position_node =
true;
488 if(node.desc.name == anim_clip->root_motion.rotation_node_name)
490 is_root_rotation_node =
true;
496 if(
int(node.desc.index) == anim_clip->root_motion.position_node_index)
498 is_root_position_node =
true;
500 if(
int(node.desc.index) == anim_clip->root_motion.rotation_node_index)
502 is_root_rotation_node =
true;
506 if(is_root_position_node)
508 pose.motion_result.root_position_node_index = anim_clip->root_motion.position_node_index;
509 pose.motion_result.root_position_node_name = anim_clip->root_motion.position_node_name;
511 const auto& clip_start_pos = channel.position_keys.front().value;
512 const auto& clip_end_pos = channel.position_keys.back().value;
514 pose.motion_result.root_position_weights = {1.0f, 1.0f, 1.0f};
515 pose.motion_result.bone_position_weights = {0.0f, 0.0f, 0.0f};
517 if(
pose.motion_state.root_position_time == seconds_t(0))
519 pose.motion_state.root_position_time = time;
520 pose.motion_state.root_position_at_time = clip_start_pos;
523 auto delta_position =
position -
pose.motion_state.root_position_at_time;
525 if(time <
pose.motion_state.root_position_time)
527 auto loop_pos_offset = clip_end_pos - clip_start_pos;
528 delta_position = delta_position + loop_pos_offset;
533 if(anim_clip->root_motion.keep_position_y)
535 pose.motion_result.root_position_weights.y = 0.0f;
536 pose.motion_result.bone_position_weights.y = 1.0f;
540 if(anim_clip->root_motion.keep_position_xz)
542 pose.motion_result.root_position_weights.x = 0.0f;
543 pose.motion_result.root_position_weights.z = 0.0f;
545 pose.motion_result.bone_position_weights.x = 1.0f;
546 pose.motion_result.bone_position_weights.z = 1.0f;
549 if(anim_clip->root_motion.keep_in_place)
551 pose.motion_result.root_position_weights.y = 0.0f;
552 pose.motion_result.root_position_weights.x = 0.0f;
553 pose.motion_result.root_position_weights.z = 0.0f;
555 pose.motion_result.bone_position_weights.y = 1.0f;
556 pose.motion_result.bone_position_weights.x = 0.0f;
557 pose.motion_result.bone_position_weights.z = 0.0f;
560 pose.motion_state.root_position_time = time;
562 pose.motion_result.root_transform_delta.set_position(delta_position);
565 if(is_root_rotation_node)
567 pose.motion_result.root_rotation_node_index = anim_clip->root_motion.rotation_node_index;
568 pose.motion_result.root_rotation_node_name = anim_clip->root_motion.rotation_node_name;
570 const auto& clip_start_rotation = channel.rotation_keys.front().value;
571 const auto& clip_end_rotation = channel.rotation_keys.back().value;
573 pose.motion_result.root_rotation_weight = {1.0f};
574 pose.motion_result.bone_rotation_weight = {0.0f};
576 if(
pose.motion_state.root_rotation_time == seconds_t(0))
578 pose.motion_state.root_rotation_time = time;
580 pose.motion_state.root_rotation_at_time = clip_start_rotation;
583 auto delta_rotation =
rotation * glm::inverse(
pose.motion_state.root_rotation_at_time);
585 if(time <
pose.motion_state.root_rotation_time)
587 auto loop_rotation_offset = clip_end_rotation * glm::inverse(clip_start_rotation);
588 delta_rotation = loop_rotation_offset * delta_rotation;
591 if(anim_clip->root_motion.keep_rotation)
593 pose.motion_result.root_rotation_weight = 0.0f;
594 pose.motion_result.bone_rotation_weight = 1.0f;
598 if(anim_clip->root_motion.keep_in_place)
600 pose.motion_result.root_rotation_weight = 0.0f;
601 pose.motion_result.bone_rotation_weight = 1.0f;
604 pose.motion_state.root_rotation_time = time;
606 pose.motion_result.root_transform_delta.set_rotation(delta_rotation);
613 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, animation_retargeting_mode retargeting_mode, 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)
animation_retargeting_mode
Animation retargeting mode for bone matching.
@ name_based
Flexible, works with different skeletons (uses cached hash map)
void blend_poses_additive(const animation_pose &base, const animation_pose &additive, const animation_pose &ref_pose, float weight, animation_pose &result)
std::vector< math::quat > rotation
Thread-safe handle to an asset.
auto get(bool wait=true) const -> std::shared_ptr< T >
Gets the shared pointer to the asset.
animation_clip::seconds_t elapsed