Animations work now

This commit is contained in:
2023-09-19 02:17:12 +03:00
parent b553b8ca1d
commit 49b11221a6
4 changed files with 169 additions and 117 deletions

View File

@@ -6,6 +6,10 @@
"future": "cpp", "future": "cpp",
"istream": "cpp", "istream": "cpp",
"functional": "cpp", "functional": "cpp",
"numbers": "cpp" "numbers": "cpp",
"*.inc": "cpp",
"*.tcc": "cpp",
"ranges": "cpp",
"streambuf": "cpp"
} }
} }

View File

@@ -5,23 +5,27 @@
#include <flecs.h> #include <flecs.h>
#include "animation_system.h" #include "animation_system.h"
#include "character.h"
/* private functions */ /* private functions */
namespace ECS { namespace ECS {
static void _animation_process_animation(struct AnimationPlayerData *player, static void _animation_process_animation(flecs::entity_t entity,
flecs::world &ecs, struct AnimationPlayerData *player,
AnimationData *p_anim, float p_time, float p_delta, float p_interp, AnimationData *p_anim, float p_time, float p_delta, float p_interp,
bool p_is_current = true, bool p_seeked = false, bool p_is_current = true, bool p_seeked = false,
bool p_started = false); bool p_started = false);
static void _ensure_node_caches(struct AnimationPlayerData *player, static void _ensure_node_caches(struct AnimationPlayerData *player,
AnimationData *p_anim, Node *p_root_override = NULL); flecs::entity_t entity, flecs::world &ecs, AnimationData *p_anim,
static void _animation_process_data(struct AnimationPlayerData *player, Node *p_root_override = NULL);
PlaybackData &cd, float p_delta, float p_blend, bool p_seeked, static void _animation_process_data(flecs::entity_t entity, flecs::world &ecs,
bool p_started); struct AnimationPlayerData *player, PlaybackData &cd, float p_delta,
static void _animation_process2( float p_blend, bool p_seeked, bool p_started);
static void _animation_process2(flecs::entity_t entity, flecs::world &ecs,
struct AnimationPlayerData *player, float p_delta, bool p_started); struct AnimationPlayerData *player, float p_delta, bool p_started);
static void _animation_update_transforms(struct AnimationPlayerData *player); static void _animation_update_transforms(flecs::entity_t entity,
static void _animation_process( flecs::world &ecs, struct AnimationPlayerData *player);
struct AnimationPlayerData *player, float p_delta); static void _animation_process(flecs::entity_t entity, flecs::world &ecs,
struct AnimationPlayerData *player, float p_time);
// static void _node_removed(struct AnimationPlayerData *player, // static void _node_removed(struct AnimationPlayerData *player,
// Node *p_node); // Node *p_node);
static void _stop_playing_caches(struct AnimationPlayerData *player); static void _stop_playing_caches(struct AnimationPlayerData *player);
@@ -145,7 +149,8 @@ void ECS::play(struct ECS::AnimationPlayerData *player,
} }
} }
static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player, static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player,
AnimationData *p_anim, Node *p_root_override) flecs::entity_t entity, flecs::world &ecs, AnimationData *p_anim,
Node *p_root_override)
{ {
// Already cached? // Already cached?
assert(p_anim->animation.is_valid()); assert(p_anim->animation.is_valid());
@@ -205,12 +210,16 @@ static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player,
#endif #endif
ECS::TrackNodeCacheKey key; ECS::TrackNodeCacheKey key;
key.id = id; key.id = entity;
key.bone_idx = bone_idx; flecs::entity e = ecs.entity(entity);
String path = a->track_get_path(i); flecs::entity bone_e =
e.lookup(String(np.get_subname(0)).ascii().get_data());
flecs::entity_t bone_id = bone_e.id();
key.bone_idx = bone_id;
String path = np.get_subname(0);
print_line("TRACK: " + itos(i) + print_line("TRACK: " + itos(i) +
" BONE IDX: " + itos(bone_idx) + " ID: " + itos(id) + " BONE IDX: " + itos(bone_id) +
" PATH: " + path); " ID: " + itos(entity) + " PATH: " + path);
if (!player->node_cache_map.has(key)) if (!player->node_cache_map.has(key))
player->node_cache_map[key] = ECS::TrackNodeCache(); player->node_cache_map[key] = ECS::TrackNodeCache();
@@ -240,17 +249,18 @@ static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player,
.get_subname(0); .get_subname(0);
/* use entities */ /* use entities */
p_anim->node_cache[i]->bone_idx =
key.bone_idx;
#if 0 #if 0
p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name); p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name);
#endif #endif
#if 0
if (p_anim->node_cache[i]->bone_idx < if (p_anim->node_cache[i]->bone_idx <
0) { 0) {
// broken track (nonexistent // broken track (nonexistent
// bone) // bone)
#if 0
p_anim->node_cache[i]->skeleton = nullptr; p_anim->node_cache[i]->skeleton = nullptr;
p_anim->node_cache[i]->spatial = nullptr; p_anim->node_cache[i]->spatial = nullptr;
#endif
ERR_CONTINUE( ERR_CONTINUE(
p_anim->node_cache[i] p_anim->node_cache[i]
->bone_idx < ->bone_idx <
@@ -260,55 +270,58 @@ static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player,
#if 0 #if 0
// no property, just use spatialnode // no property, just use spatialnode
p_anim->node_cache[i]->skeleton = nullptr; p_anim->node_cache[i]->skeleton = nullptr;
#endif
}
#endif #endif
} }
} }
}
if (a->track_get_type(i) == Animation::TYPE_VALUE) { #if 0
if (!p_anim->node_cache[i]->property_anim.has( if (a->track_get_type(i) == Animation::TYPE_VALUE) {
a->track_get_path(i) if (!p_anim->node_cache[i]->property_anim.has(
.get_concatenated_subnames())) { a->track_get_path(i)
TrackNodeCache::PropertyAnim pa; .get_concatenated_subnames())) {
pa.subpath = leftover_path; TrackNodeCache::PropertyAnim pa;
pa.subpath = leftover_path;
#if 0 #if 0
pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
#endif #endif
pa.special = SP_NONE; pa.special = SP_NONE;
pa.owner = p_anim->node_cache[i]; pa.owner = p_anim->node_cache[i];
p_anim->node_cache[i]->property_anim p_anim->node_cache[i]->property_anim
[a->track_get_path(i) [a->track_get_path(i)
.get_concatenated_subnames()] = .get_concatenated_subnames()] =
pa; pa;
}
} }
}
if (a->track_get_type(i) == Animation::TYPE_BEZIER && if (a->track_get_type(i) == Animation::TYPE_BEZIER &&
leftover_path.size()) { leftover_path.size()) {
if (!p_anim->node_cache[i]->bezier_anim.has( if (!p_anim->node_cache[i]->bezier_anim.has(
a->track_get_path(i) a->track_get_path(i)
.get_concatenated_subnames())) { .get_concatenated_subnames())) {
TrackNodeCache::BezierAnim ba; TrackNodeCache::BezierAnim ba;
ba.bezier_property = leftover_path; ba.bezier_property = leftover_path;
#if 0 #if 0
ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
#endif #endif
ba.owner = p_anim->node_cache[i]; ba.owner = p_anim->node_cache[i];
p_anim->node_cache[i]->bezier_anim p_anim->node_cache[i]->bezier_anim
[a->track_get_path(i) [a->track_get_path(i)
.get_concatenated_subnames()] = .get_concatenated_subnames()] =
ba; ba;
} }
#endif
} }
} }
} }
static void ECS::_animation_process_animation( static void ECS::_animation_process_animation(flecs::entity_t entity,
struct ECS::AnimationPlayerData *player, ECS::AnimationData *p_anim, flecs::world &ecs, struct ECS::AnimationPlayerData *player,
float p_time, float p_delta, float p_interp, bool p_is_current, ECS::AnimationData *p_anim, float p_time, float p_delta,
bool p_seeked, bool p_started) float p_interp, bool p_is_current, bool p_seeked, bool p_started)
{ {
_ensure_node_caches(player, p_anim); _ensure_node_caches(player, entity, ecs, p_anim);
ERR_FAIL_COND(p_anim->node_cache_size != ERR_FAIL_COND(p_anim->node_cache_size !=
p_anim->animation->get_track_count()); p_anim->animation->get_track_count());
@@ -318,20 +331,23 @@ static void ECS::_animation_process_animation(
#endif #endif
for (int i = 0; i < a->get_track_count(); i++) { for (int i = 0; i < a->get_track_count(); i++) {
// If an animation changes this animation (or it animates // If an animation changes this animation (or it
// itself) we need to recreate our animation cache // animates itself) we need to recreate our animation
// cache
if (p_anim->node_cache_size != a->get_track_count()) { if (p_anim->node_cache_size != a->get_track_count()) {
_ensure_node_caches(player, p_anim); _ensure_node_caches(player, entity, ecs, p_anim);
} }
TrackNodeCache *nc = p_anim->node_cache[i]; TrackNodeCache *nc = p_anim->node_cache[i];
if (!nc) { if (!nc) {
continue; // no node cache for this track, skip it continue; // no node cache for this track, skip
// it
} }
if (!a->track_is_enabled(i)) { if (!a->track_is_enabled(i)) {
continue; // do nothing if the track is disabled continue; // do nothing if the track is
// disabled
} }
flecs::log::trace("track keys: %d", i); flecs::log::trace("track keys: %d", i);
if (a->track_get_key_count(i) == 0) { if (a->track_get_key_count(i) == 0) {
@@ -351,8 +367,8 @@ static void ECS::_animation_process_animation(
Error err = a->transform_track_interpolate( Error err = a->transform_track_interpolate(
i, p_time, &loc, &rot, &scale); i, p_time, &loc, &rot, &scale);
//ERR_CONTINUE(err!=OK); //used for testing, //ERR_CONTINUE(err!=OK); //used for
//should be removed //testing, should be removed
if (err != OK) { if (err != OK) {
continue; continue;
@@ -369,6 +385,8 @@ static void ECS::_animation_process_animation(
nc->loc_accum = loc; nc->loc_accum = loc;
nc->rot_accum = rot; nc->rot_accum = rot;
nc->scale_accum = scale; nc->scale_accum = scale;
print_line("cache update: " +
itos(nc->bone_idx));
} else { } else {
nc->loc_accum = nc->loc_accum =
@@ -382,6 +400,8 @@ static void ECS::_animation_process_animation(
.linear_interpolate( .linear_interpolate(
scale, scale,
p_interp); p_interp);
print_line("cache update2: " +
itos(nc->bone_idx));
} }
} break; } break;
@@ -418,7 +438,8 @@ static void ECS::_animation_process_animation(
int key_count = int key_count =
a->track_get_key_count(i); a->track_get_key_count(i);
if (key_count == 0) { if (key_count == 0) {
continue; //eeh not worth it continue; //eeh not
//worth it
} }
float first_key_time = float first_key_time =
@@ -427,11 +448,15 @@ static void ECS::_animation_process_animation(
int first_key = 0; int first_key = 0;
if (first_key_time == 0.0) { if (first_key_time == 0.0) {
//ignore, use for transition //ignore, use for
//transition
if (key_count == 1) { if (key_count == 1) {
continue; //with one continue; //with
//key we //one
//can't do //key
//we
//can't
//do
//anything //anything
} }
transition = transition =
@@ -497,14 +522,14 @@ static void ECS::_animation_process_animation(
continue; continue;
} }
//thanks to trigger mode, this should //thanks to trigger mode, this
//be solved now.. //should be solved now..
/* /*
if (p_delta==0 && if (p_delta==0 &&
value.get_type()==Variant::STRING) value.get_type()==Variant::STRING)
continue; // doing this with continue; // doing this
strings is messy, should find another with strings is messy, should
way find another way
*/ */
if (pa->accum_pass != if (pa->accum_pass !=
player->accum_pass) { player->accum_pass) {
@@ -623,8 +648,10 @@ static void ECS::_animation_process_animation(
#endif #endif
static_assert(VARIANT_ARG_MAX == 8, static_assert(VARIANT_ARG_MAX == 8,
"This code needs to be " "This code needs to "
"updated if VARIANT_ARG_MAX " "be "
"updated if "
"VARIANT_ARG_MAX "
"!= 8"); "!= 8");
#if 0 #if 0
if (player->can_call) { if (player->can_call) {
@@ -703,7 +730,8 @@ static void ECS::_animation_process_animation(
} }
if (p_seeked) { if (p_seeked) {
//find whatever should be playing //find whatever should be
//playing
int idx = a->track_find_key(i, p_time); int idx = a->track_find_key(i, p_time);
if (idx < 0) { if (idx < 0) {
continue; continue;
@@ -863,7 +891,8 @@ static void ECS::_animation_process_animation(
} break; } break;
case Animation::TYPE_ANIMATION: { case Animation::TYPE_ANIMATION: {
/* animations of animations - need rework */ /* animations of animations - need
* rework */
#if 0 #if 0
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(nc->node); AnimationPlayer *player = Object::cast_to<AnimationPlayer>(nc->node);
if (!player) { if (!player) {
@@ -930,9 +959,10 @@ static void ECS::_animation_process_animation(
} }
} }
} }
static void ECS::_animation_process_data( static void ECS::_animation_process_data(flecs::entity_t entity,
struct ECS::AnimationPlayerData *player, PlaybackData &cd, flecs::world &ecs, struct ECS::AnimationPlayerData *player,
float p_delta, float p_blend, bool p_seeked, bool p_started) PlaybackData &cd, float p_delta, float p_blend, bool p_seeked,
bool p_started)
{ {
float delta = p_delta * player->speed_scale * cd.speed_scale; float delta = p_delta * player->speed_scale * cd.speed_scale;
float next_pos = cd.pos + delta; float next_pos = cd.pos + delta;
@@ -953,36 +983,39 @@ static void ECS::_animation_process_data(
bool backwards = bool backwards =
signbit(delta); // Negative zero means playing backwards too signbit(delta); // Negative zero means playing backwards too
delta = next_pos - delta = next_pos -
cd.pos; // Fix delta (after determination of backwards because cd.pos; // Fix delta (after determination of backwards
// negative zero is lost here) // because negative zero is lost here)
if (&cd == &player->playback.current) { if (&cd == &player->playback.current) {
if (!backwards && cd.pos <= len && next_pos == len) { if (!backwards && cd.pos <= len && next_pos == len) {
//playback finished //playback finished
player->end_reached = true; player->end_reached = true;
player->end_notify = cd.pos < player->end_notify =
len; // Notify only if not already at the end cd.pos < len; // Notify only if not
// already at the end
} }
if (backwards && cd.pos >= 0 && next_pos == 0) { if (backwards && cd.pos >= 0 && next_pos == 0) {
//playback finished //playback finished
player->end_reached = true; player->end_reached = true;
player->end_notify = player->end_notify =
cd.pos > 0; // Notify only if not already at cd.pos > 0; // Notify only if not
// the beginning // already at the beginning
} }
} else { } else {
float looped_next_pos = Math::fposmod(next_pos, len); float looped_next_pos = Math::fposmod(next_pos, len);
if (looped_next_pos == 0 && next_pos != 0) if (looped_next_pos == 0 && next_pos != 0)
// Loop multiples of the length to it, rather than 0 // Loop multiples of the length to it, rather
// so state at time=length is previewable in the editor // than 0 so state at time=length is
// previewable in the editor
next_pos = len; next_pos = len;
else else
next_pos = looped_next_pos; next_pos = looped_next_pos;
} }
cd.pos = next_pos; cd.pos = next_pos;
_animation_process_animation(player, cd.from, cd.pos, delta, p_blend, _animation_process_animation(entity, ecs, player, cd.from, cd.pos,
&cd == &player->playback.current, p_seeked, p_started); delta, p_blend, &cd == &player->playback.current, p_seeked,
p_started);
} }
static void ECS::_animation_process2( static void ECS::_animation_process2(flecs::entity_t entity, flecs::world &ecs,
struct ECS::AnimationPlayerData *player, float p_delta, bool p_started) struct ECS::AnimationPlayerData *player, float p_delta, bool p_started)
{ {
ECS::Playback &c = player->playback; ECS::Playback &c = player->playback;
@@ -994,7 +1027,7 @@ static void ECS::_animation_process2(
} }
flecs::log::trace("length: %d", flecs::log::trace("length: %d",
player->playback.current.from->animation->get_length()); player->playback.current.from->animation->get_length());
_animation_process_data(player, c.current, p_delta, 1.0f, _animation_process_data(entity, ecs, player, c.current, p_delta, 1.0f,
c.seeked && p_delta != 0, p_started); c.seeked && p_delta != 0, p_started);
if (p_delta != 0) if (p_delta != 0)
c.seeked = false; c.seeked = false;
@@ -1011,16 +1044,17 @@ static void ECS::_animation_process2(
c.blend.erase(E); c.blend.erase(E);
continue; continue;
} }
_animation_process_data( _animation_process_data(entity, ecs, player, b.data, p_delta,
player, b.data, p_delta, blend, false, false); blend, false, false);
b.blend_left -= Math::absf(player->speed_scale * p_delta); b.blend_left -= Math::absf(player->speed_scale * p_delta);
prev = E->prev(); prev = E->prev();
if (b.blend_left < 0) if (b.blend_left < 0)
c.blend.erase(E); c.blend.erase(E);
} }
} }
static void ECS::_animation_update_transforms( /* transforms are set here */
struct ECS::AnimationPlayerData *player) static void ECS::_animation_update_transforms(flecs::entity_t entity,
flecs::world &ecs, struct ECS::AnimationPlayerData *player)
{ {
int i; int i;
{ {
@@ -1029,6 +1063,15 @@ static void ECS::_animation_update_transforms(
TrackNodeCache *nc = player->cache_update[i]; TrackNodeCache *nc = player->cache_update[i];
t.origin = nc->loc_accum; t.origin = nc->loc_accum;
t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum); t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum);
flecs::entity bone_entity = ecs.entity(nc->bone_idx);
if (bone_entity) {
ECS::BonePose *bone =
bone_entity.get_mut<ECS::BonePose>();
bone->pose = t;
bone_entity.modified<ECS::BonePose>();
flecs::log::err("good bone %d", nc->bone_idx);
} else
flecs::log::err("bad bone %d", nc->bone_idx);
#if 0 #if 0
/* update transform on skeleton or node */ /* update transform on skeleton or node */
if (nc->skeleton && nc->bone_idx >= 0) if (nc->skeleton && nc->bone_idx >= 0)
@@ -1049,14 +1092,17 @@ static void ECS::_animation_update_transforms(
pa->subpath, pa->value_accum, &valid); pa->subpath, pa->value_accum, &valid);
if (!valid) { if (!valid) {
/* error setting key /* error setting key
ERR_PRINT("Failed setting key at time " ERR_PRINT("Failed setting key
+ rtos(playback.current.pos) + " in at time "
Animation '" + get_current_animation() + rtos(playback.current.pos) +
+ "' at Node '" + get_path() + "', " in Animation '" +
Track '" + String(pa->owner->path) + get_current_animation()
"'. Check if property exists or the + "' at Node '" + get_path() +
type of key is right for the "', Track '" +
property"); String(pa->owner->path) +
"'. Check if property exists or
the type of key is right for
the property");
*/ */
} }
} break; } break;
@@ -1087,7 +1133,7 @@ static void ECS::_set_process(
{ {
/* Nothing */ /* Nothing */
} }
static void ECS::_animation_process( static void ECS::_animation_process(flecs::entity_t entity, flecs::world &ecs,
struct ECS::AnimationPlayerData *player, float p_time) struct ECS::AnimationPlayerData *player, float p_time)
{ {
if (player->playback.current.from) { if (player->playback.current.from) {
@@ -1096,10 +1142,11 @@ static void ECS::_animation_process(
->get_length()); ->get_length());
player->end_reached = false; player->end_reached = false;
player->end_notify = false; player->end_notify = false;
_animation_process2(player, p_time, player->playback.started); _animation_process2(
entity, ecs, player, p_time, player->playback.started);
if (player->playback.started) if (player->playback.started)
player->playback.started = false; player->playback.started = false;
_animation_update_transforms(player); _animation_update_transforms(entity, ecs, player);
if (player->end_reached) { if (player->end_reached) {
if (player->queued.size()) { if (player->queued.size()) {
String old = player->playback.assigned; String old = player->playback.assigned;
@@ -1222,13 +1269,14 @@ void ECS::clear_caches(struct ECS::AnimationPlayerData *player)
player->cache_update_bezier_size = 0; player->cache_update_bezier_size = 0;
} }
void ECS::advance(struct ECS::AnimationPlayerData *player, float p_time) void ECS::advance(flecs::entity_t entity, flecs::world &ecs,
ECS::AnimationPlayerData *player, float p_time)
{ {
if (player->playback.current.from) { if (player->playback.current.from) {
flecs::log::trace("length: %f", flecs::log::trace("length: %f",
player->playback.current.from->animation player->playback.current.from->animation
->get_length()); ->get_length());
} }
ECS::_animation_process(player, p_time); ECS::_animation_process(entity, ecs, player, p_time);
player->playback.current.from = nullptr; player->playback.current.from = nullptr;
} }

View File

@@ -28,7 +28,7 @@ struct TrackNodeCache {
Node2D *node_2d; Node2D *node_2d;
Skeleton *skeleton; Skeleton *skeleton;
#endif #endif
int bone_idx; flecs::entity_t bone_idx;
// accumulated transforms // accumulated transforms
Vector3 loc_accum; Vector3 loc_accum;
Quat rot_accum; Quat rot_accum;
@@ -234,8 +234,7 @@ struct BlendKey {
} }
}; };
struct TrackNodeCacheKey { struct TrackNodeCacheKey {
uint32_t id; flecs::entity_t id, bone_idx;
int bone_idx;
inline bool operator<(const TrackNodeCacheKey &p_right) const inline bool operator<(const TrackNodeCacheKey &p_right) const
{ {
@@ -348,7 +347,8 @@ void seek_delta(
struct AnimationPlayerData *player, float p_time, float p_delta); struct AnimationPlayerData *player, float p_time, float p_delta);
float get_current_animation_position(struct AnimationPlayerData *player); float get_current_animation_position(struct AnimationPlayerData *player);
float get_current_animation_length(struct AnimationPlayerData *player); float get_current_animation_length(struct AnimationPlayerData *player);
void advance(struct AnimationPlayerData *player, float p_time); void advance(flecs::entity_t entity, flecs::world &ecs,
AnimationPlayerData *player, float p_time);
void set_root(struct AnimationPlayerData *player, const NodePath &p_root); void set_root(struct AnimationPlayerData *player, const NodePath &p_root);
NodePath get_root(struct AnimationPlayerData *player); NodePath get_root(struct AnimationPlayerData *player);
void clear_caches(struct AnimationPlayerData *player); void clear_caches(struct AnimationPlayerData *player);

View File

@@ -124,15 +124,14 @@ int Character::create_character(int id)
} }
for (i = 0; i < scene_data[id].meshes.size(); i++) { for (i = 0; i < scene_data[id].meshes.size(); i++) {
// flecs::log::trace("creating slot -"); // flecs::log::trace("creating slot -");
flecs::entity entity = ecs.entity(scene_data[id]
ecs.entity(scene_data[id] .meshes[i]
.meshes[i] .slot_name.ascii()
.slot_name.ascii() .get_data())
.get_data()) .child_of(root)
.child_of(root) .emplace<ECS::CharacterSlot>(
.emplace<ECS::CharacterSlot>( scene_data[id].meshes[i].mesh,
scene_data[id].meshes[i].mesh, scene_data[id].meshes[i].skin);
scene_data[id].meshes[i].skin);
} }
// print_line("character created"); // print_line("character created");
int xid = entities.size(); int xid = entities.size();
@@ -383,7 +382,8 @@ void Character::animation_system_init()
.each([](flecs::entity e, ECS::AnimationPlayerData &player) { .each([](flecs::entity e, ECS::AnimationPlayerData &player) {
if (!player.playing) if (!player.playing)
ECS::play(&player, "stand1-loop"); ECS::play(&player, "stand1-loop");
ECS::advance(&player, flecs::world w = e.world();
ECS::advance(e.id(), w, &player,
SceneTree::get_singleton() SceneTree::get_singleton()
->get_physics_process_time()); ->get_physics_process_time());
}); });