12#include <glm/gtc/epsilon.hpp>
18#define POOLSTL_STD_SUPPLEMENT 1
19#include <poolstl/poolstl.hpp>
26auto remap_root_motion_translation(
const math::vec3& delta,
const math::quat& correction)
noexcept -> math::vec3
28 if(math::all(math::epsilonEqual(correction,
29 math::identity<math::quat>(),
30 math::epsilon<float>())))
35 return correction * delta;
38auto remap_root_motion_rotation(
const math::quat& delta,
const math::quat& correction)
noexcept -> math::quat
40 if(math::all(math::epsilonEqual(correction,
41 math::identity<math::quat>(),
42 math::epsilon<float>())))
47 return correction * delta * glm::conjugate(correction);
50void on_play_begin_impl(animation_component& comp)
52 if(comp.get_autoplay())
54 auto& player = comp.get_player();
55 player.blend_to(0, comp.get_animation());
63 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
64 auto& ev = ctx.get_cached<
events>();
67 ev.on_play_end.connect(sentinel_, -10,
this, &animation_system::on_play_end);
68 ev.on_pause.connect(sentinel_, 10,
this, &animation_system::on_pause);
69 ev.on_resume.connect(sentinel_, -10,
this, &animation_system::on_resume);
70 ev.on_skip_next_frame.connect(sentinel_, 10,
this, &animation_system::on_skip_next_frame);
77 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
92 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
98 [&](
auto e,
auto&& animation_comp)
100 on_play_begin_impl(animation_comp);
106 for(
auto entity : entities)
110 on_play_begin_impl(*animation_comp);
117 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
123 [&](
auto e,
auto&& animation_comp)
125 auto& player = animation_comp.get_player();
144 on_update(scn, step,
true);
151 static const std::string thread_name =
"Animation/System Update Thread";
155 auto view = scn.registry->view<model_component, animation_component, transform_component>();
159 std::for_each(poolstl::par,
164 APP_SCOPE_PERF_THREAD(
"Animation/Update Poses",
"Pool Thread");
167 tpp::this_thread::register_this_thread(thread_name, true);
169 auto& animation_comp = view.get<animation_component>(entity);
170 auto& model_comp = view.get<model_component>(entity);
172 bool should_update_poses = true;
173 if(animation_comp.get_culling_mode() == animation_component::culling_mode::renderer_based)
175 if(!model_comp.was_used_last_frame())
177 should_update_poses = false;
181 auto& player = animation_comp.get_player();
184 auto speed = animation_comp.get_speed();
185 auto adjusted_dt = dt * speed;
187 bool updated = player.update_time(adjusted_dt,
force);
189 if(updated && should_update_poses)
191 auto& transform_comp = view.get<transform_component>(entity);
194 bool apply_root_motion = animation_comp.get_apply_root_motion();
195 auto retargeting_mode = animation_comp.get_retargeting_mode();
196 const auto facing_correction = model_comp.get_facing_adjustment_rotation();
199 model_comp.get_bind_pose(),
201 [&](const animation_pose::node_desc& desc,
202 const math::transform& transform,
203 const animation_pose::root_motion_result& motion_result)
206 auto armature_index = desc.index;
207 if(retargeting_mode == animation_retargeting_mode::name_based)
210 int name_based_armature_index =
211 model_comp.get_armature_index_by_name_cached(desc.name);
212 if(name_based_armature_index >= 0 &&
213 name_based_armature_index != static_cast<int>(armature_index))
215 armature_index = name_based_armature_index;
219 auto armature = model_comp.get_armature_by_index(armature_index);
222 auto& armature_transform_comp = armature.template get<transform_component>();
224 bool processed_by_root_motion = false;
226 bool is_root_position_node = false;
227 bool is_root_rotation_node = false;
228 if(retargeting_mode == animation_retargeting_mode::name_based)
230 is_root_position_node = desc.name == motion_result.root_position_node_name;
231 is_root_rotation_node = desc.name == motion_result.root_rotation_node_name;
235 is_root_position_node = desc.index == motion_result.root_position_node_index;
236 is_root_rotation_node = desc.index == motion_result.root_position_node_index;
239 if(apply_root_motion && is_root_position_node)
241 armature_transform_comp.set_scale_local(transform.get_scale());
243 auto position_local = armature_transform_comp.get_position_local();
244 auto result_positon_local = math::mix(position_local,
245 transform.get_position(),
246 motion_result.bone_position_weights);
247 armature_transform_comp.set_position_local(result_positon_local);
249 math::vec3 delta_translation_logical = remap_root_motion_translation(
250 motion_result.root_transform_delta.get_translation(),
255 auto scale_global = armature_transform_comp.get_scale_global();
256 delta_translation_logical *= scale_global;
259 auto result_move_local = math::mix(math::zero<math::vec3>(),
260 delta_translation_logical,
261 motion_result.root_position_weights);
276 transform_comp.move_by_local(result_move_local);
278 processed_by_root_motion = true;
281 if(apply_root_motion && is_root_rotation_node)
283 armature_transform_comp.set_scale_local(transform.get_scale());
285 auto rotation_local = armature_transform_comp.get_rotation_local();
286 auto result_rotation_local = math::slerp(rotation_local,
287 transform.get_rotation(),
288 motion_result.bone_rotation_weight);
289 armature_transform_comp.set_rotation_local(result_rotation_local);
292 math::quat delta_rotation_logical = remap_root_motion_rotation(
293 motion_result.root_transform_delta.get_rotation(),
297 auto result_rotate_local = math::slerp(math::identity<math::quat>(),
298 delta_rotation_logical,
299 motion_result.root_rotation_weight);
300 transform_comp.rotate_by_local(result_rotate_local);
302 processed_by_root_motion = true;
305 if(!processed_by_root_motion)
307 armature_transform_comp.set_transform_local(transform);
318 APPLOG_WARNING(
"Cannot find armature with index {}", desc.index);
static void on_create_component(entt::registry &r, entt::entity e)
Called when the component is created.
void on_play_begin(hpp::span< const entt::handle > entities, delta_t dt)
auto init(rtti::context &ctx) -> bool
auto deinit(rtti::context &ctx) -> bool
static void on_destroy_component(entt::registry &r, entt::entity e)
Called when the component is destroyed.
std::chrono::duration< float > delta_t
#define APPLOG_TRACE(...)
auto on_update(entt::registry ®) -> decltype(auto)
#define APP_SCOPE_PERF(name_literal)
Create a scoped performance timer that records to the timeline profiler. Only accepts string literals...
Manages the entity-component-system (ECS) operations for the ACE framework.
auto get_scene() -> scene &
Gets the current scene.
static auto context() -> rtti::context &
hpp::event< void(rtti::context &)> on_play_begin
Represents a scene in the ACE framework, managing entities and their relationships.