Unravel Engine C++ Reference
Loading...
Searching...
No Matches
animation_system.cpp
Go to the documentation of this file.
1#include "animation_system.h"
7#include <engine/events.h>
10
11#include <engine/ecs/ecs.h>
12#include <engine/engine.h>
15#include <logging/logging.h>
16
17#define POOLSTL_STD_SUPPLEMENT 1
18#include <poolstl/poolstl.hpp>
19
20namespace unravel
21{
22
23namespace
24{
25void on_play_begin_impl(animation_component& comp)
26{
27 if(comp.get_autoplay())
28 {
29 auto& player = comp.get_player();
30 player.blend_to(0, comp.get_animation());
31 player.play();
32 }
33}
34}
35
37{
38 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
39 auto& ev = ctx.get_cached<events>();
40
41 ev.on_play_begin.connect(sentinel_, 10, this, &animation_system::on_play_begin);
42 ev.on_play_end.connect(sentinel_, -10, this, &animation_system::on_play_end);
43 ev.on_pause.connect(sentinel_, 10, this, &animation_system::on_pause);
44 ev.on_resume.connect(sentinel_, -10, this, &animation_system::on_resume);
45 ev.on_skip_next_frame.connect(sentinel_, 10, this, &animation_system::on_skip_next_frame);
46
47 return true;
48}
49
51{
52 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
53
54 return true;
55}
56
57void animation_system::on_create_component(entt::registry& r, entt::entity e)
58{
59
60}
61
62void animation_system::on_destroy_component(entt::registry& r, entt::entity e)
63{
64}
65
67{
68 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
69
70 auto& ec = ctx.get_cached<ecs>();
71 auto& scn = ec.get_scene();
72
73 scn.registry->view<animation_component>().each(
74 [&](auto e, auto&& animation_comp)
75 {
76 on_play_begin_impl(animation_comp);
77 });
78}
79
80
81void animation_system::on_play_begin(hpp::span<const entt::handle> entities, delta_t dt)
82{
83 for(auto entity : entities)
84 {
85 if(auto animation_comp = entity.try_get<animation_component>())
86 {
87 on_play_begin_impl(*animation_comp);
88 }
89 }
90}
91
92
93void animation_system::on_play_end(rtti::context& ctx)
94{
95 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
96
97 auto& ec = ctx.get_cached<ecs>();
98 auto& scn = ec.get_scene();
99
100 scn.registry->view<animation_component>().each(
101 [&](auto e, auto&& animation_comp)
102 {
103 auto& player = animation_comp.get_player();
104 player.stop();
105 });
106}
107
108void animation_system::on_pause(rtti::context& ctx)
109{
110}
111
112void animation_system::on_resume(rtti::context& ctx)
113{
114}
115
116void animation_system::on_skip_next_frame(rtti::context& ctx)
117{
118 auto& ec = ctx.get_cached<ecs>();
119 auto& scn = ec.get_scene();
120
121 delta_t step(1.0f / 60.0f);
122 on_update(scn, step, true);
123}
124
125void animation_system::on_update(scene& scn, delta_t dt, bool force)
126{
127 APP_SCOPE_PERF("Animation/System Update");
128
129 auto& ctx = engine::context();
130 auto& th = ctx.get_cached<threader>();
131 // Create a view for entities with transform_component and submesh_component
132 auto view = scn.registry->view<model_component, animation_component, transform_component>();
133
134 // this code should be thread safe as each task works with a whole hierarchy and
135 // there is no interleaving between tasks.
136 std::for_each(std::execution::par,
137 view.begin(),
138 view.end(),
139 [&](entt::entity entity)
140 {
141 auto& animation_comp = view.get<animation_component>(entity);
142 auto& model_comp = view.get<model_component>(entity);
143
144 bool should_update_poses = true;
145 if(animation_comp.get_culling_mode() == animation_component::culling_mode::renderer_based)
146 {
147 if(!model_comp.was_used_last_frame())
148 {
149 should_update_poses = false;
150 }
151 }
152
153 auto& player = animation_comp.get_player();
154
155 // Apply speed to delta time
156 auto speed = animation_comp.get_speed();
157 auto adjusted_dt = dt * speed;
158
159 bool updated = player.update_time(adjusted_dt, force);
160
161 if(updated && should_update_poses)
162 {
163 auto& transform_comp = view.get<transform_component>(entity);
164 // auto physics_comp_ptr = scn.registry->try_get<physics_component>(entity);
165
166 bool apply_root_motion = animation_comp.get_apply_root_motion();
167
168 player.update_poses(
169 model_comp.get_bind_pose(),
170 [&](const animation_pose::node_desc& desc,
171 const math::transform& transform,
172 const animation_pose::root_motion_result& motion_result)
173 {
174 auto armature = model_comp.get_armature_by_index(desc.index);
175 if(armature)
176 {
177 auto& armature_transform_comp = armature.template get<transform_component>();
178
179 bool processed_by_root_motion = false;
180
181 if(apply_root_motion && desc.index == motion_result.root_position_node_index)
182 {
183 armature_transform_comp.set_scale_local(transform.get_scale());
184
185 auto position_local = armature_transform_comp.get_position_local();
186 auto result_positon_local = math::lerp(position_local,
187 transform.get_position(),
188 motion_result.bone_position_weights);
189 armature_transform_comp.set_position_local(result_positon_local);
190
191 math::vec3 delta_translation_logical =
192 motion_result.root_transform_delta.get_translation();
193
194 // // Apply scaling if needed (for example, if BoneRoot’s scale differs
195 // significantly)
196 auto scale_global = armature_transform_comp.get_scale_global();
197 delta_translation_logical *= scale_global;
198
199 // Blend translation as needed:
200 auto result_move_local = math::lerp(math::zero<math::vec3>(),
201 delta_translation_logical,
202 motion_result.root_position_weights);
203 // APPLOG_INFO("position_weights {}", motion_result.position_weights);
204 // if(physics_comp_ptr)
205 // {
206 // if(math::length2(result_move_local) > 0.0f)
207 // {
208 // auto global_delta =
209 // armature_transform_comp.get_transform_global().transform_normal(
210 // result_move_local);
211 // auto rm_velocity = global_delta / dt.count();
212 // physics_comp_ptr->set_velocity(rm_velocity);
213 // }
214 // }
215 // else
216 {
217 transform_comp.move_by_local(result_move_local);
218 }
219 processed_by_root_motion = true;
220 }
221
222 if(apply_root_motion && desc.index == motion_result.root_position_node_index)
223 {
224 armature_transform_comp.set_scale_local(transform.get_scale());
225
226 auto rotation_local = armature_transform_comp.get_rotation_local();
227 auto result_rotation_local = math::slerp(rotation_local,
228 transform.get_rotation(),
229 motion_result.bone_rotation_weight);
230 armature_transform_comp.set_rotation_local(result_rotation_local);
231
232 // --- Rotation ---
233 math::quat delta_rotation_logical =
234 motion_result.root_transform_delta.get_rotation();
235
236 // Optionally blend this delta toward identity:
237 auto result_rotate_local = math::slerp(math::identity<math::quat>(),
238 delta_rotation_logical,
239 motion_result.root_rotation_weight);
240 transform_comp.rotate_by_local(result_rotate_local);
241
242 processed_by_root_motion = true;
243 }
244
245 if(false == processed_by_root_motion)
246 {
247 armature_transform_comp.set_transform_local(transform);
248 }
249
250 // if(desc.index == root_motion_entity_index)
251 // {
252 // model_comp.update_world_bounds(
253 // armature_transform_comp.get_transform_global());
254 // }
255 }
256 else
257 {
258 APPLOG_WARNING("Cannot find armature with index {}", desc.index);
259 }
260 });
261 }
262 });
263}
264
265void animation_system::on_frame_update(scene& scn, delta_t dt)
266{
267 on_update(scn, dt, false);
268}
269
270} // namespace unravel
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(...)
Definition logging.h:17
#define APP_SCOPE_PERF(name)
Create a scoped performance timer that only accepts string literals.
Definition profiler.h:160
entt::entity entity
auto get_cached() -> T &
Definition context.hpp:49
Manages the entity-component-system (ECS) operations for the ACE framework.
Definition ecs.h:12
auto get_scene() -> scene &
Gets the current scene.
Definition ecs.cpp:30
static auto context() -> rtti::context &
Definition engine.cpp:115
hpp::event< void(rtti::context &)> on_play_begin
Definition events.h:24
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:21