From 2ac6c4afe116986c0ca89ecb5bf8864591ea262f Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Thu, 14 Sep 2023 12:21:26 +0300 Subject: [PATCH] Added animation system --- src/modules/character/animation_system.cpp | 971 +++++++++++++++++++ src/modules/character/animation_system.h | 251 +++++ src/modules/character/character.cpp | 260 +---- src/modules/character/character.h | 1 + src/modules/character/character_skeleton.cpp | 13 +- src/modules/character/character_slot.cpp | 16 +- 6 files changed, 1260 insertions(+), 252 deletions(-) create mode 100644 src/modules/character/animation_system.cpp create mode 100644 src/modules/character/animation_system.h diff --git a/src/modules/character/animation_system.cpp b/src/modules/character/animation_system.cpp new file mode 100644 index 0000000..f9f4b6f --- /dev/null +++ b/src/modules/character/animation_system.cpp @@ -0,0 +1,971 @@ +#include +#include + +#include "animation_system.h" +/* private functions */ +namespace ECS { +static void _animation_process_animation(struct AnimationPlayerData *player, AnimationData *p_anim, + float p_time, float p_delta, float p_interp, + bool p_is_current = true, bool p_seeked = false, + bool p_started = false); +static void _ensure_node_caches(struct AnimationPlayerData *player, + AnimationData *p_anim, Node *p_root_override = NULL); +static void _animation_process_data(struct AnimationPlayerData *player, + PlaybackData &cd, float p_delta, float p_blend, bool p_seeked, bool p_started); +static void _animation_process2(struct AnimationPlayerData *player, + float p_delta, bool p_started); +static void _animation_update_transforms(struct AnimationPlayerData *player); +static void _animation_process(struct AnimationPlayerData *player, + float p_delta); +// static void _node_removed(struct AnimationPlayerData *player, +// Node *p_node); +static void _stop_playing_caches(struct AnimationPlayerData *player); +static void _animation_changed(struct AnimationPlayerData *player); +static void _ref_anim(struct AnimationPlayerData *player, + const Ref &p_anim); +static void _unref_anim(struct AnimationPlayerData *player, + const Ref &p_anim); +static void _set_process(struct AnimationPlayerData *player, + bool p_process, bool p_force = false); +/* protected functions (need redesign) */ +#if 0 +bool _set(const StringName &p_name, const Variant &p_value); +bool _get(const StringName &p_name, Variant &r_ret) const; +virtual void _validate_property(PropertyInfo &property) const; +void _get_property_list(List *p_list) const; +void _notification(int p_what); +static void _bind_methods(); +#endif +} +void ECS::play(struct ECS::AnimationPlayerData *player, + const StringName &p_name, + float p_custom_blend, float p_custom_scale, + bool p_from_end) +{ + StringName name = p_name; + + if (String(name) == "") { + name = player->playback.assigned; + } + + ERR_FAIL_COND_MSG(!player->animation_set.has(name), "Animation not found: " + name + "."); + + Playback &c = player->playback; + + if (c.current.from) { + float blend_time = 0; + // find if it can blend + ECS::BlendKey bk; + bk.from = c.current.from->name; + bk.to = name; + + if (p_custom_blend >= 0) { + blend_time = p_custom_blend; + } else if (player->blend_times.has(bk)) { + blend_time = player->blend_times[bk]; + } else { + bk.from = "*"; + if (player->blend_times.has(bk)) { + blend_time = player->blend_times[bk]; + } else { + bk.from = c.current.from->name; + bk.to = "*"; + + if (player->blend_times.has(bk)) { + blend_time = player->blend_times[bk]; + } + } + } + + if (p_custom_blend < 0 && blend_time == 0 && player->default_blend_time) { + blend_time = player->default_blend_time; + } + if (blend_time > 0) { + Blend b; + b.data = c.current; + b.blend_time = b.blend_left = blend_time; + c.blend.push_back(b); + } + } + + if (get_current_animation(player) != p_name) { + _stop_playing_caches(player); + } + + c.current.from = &player->animation_set[name]; + + if (c.assigned != name) { // reset + c.current.pos = p_from_end ? c.current.from->animation->get_length() : 0; + } else { + if (p_from_end && c.current.pos == 0) { + // Animation reset BUT played backwards, set position to the end + c.current.pos = c.current.from->animation->get_length(); + } else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) { + // Animation resumed but already ended, set position to the beginning + c.current.pos = 0; + } + } + + c.current.speed_scale = p_custom_scale; + c.assigned = name; + c.seeked = false; + c.started = true; + + if (!player->end_reached) { + player->queued.clear(); + } + _set_process(player, true); // always process when starting an animation + player->playing = true; + +#if 0 + emit_signal(SceneStringNames::get_singleton()->animation_started, c.assigned); + + if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) { + return; // no next in this case + } +#endif + + StringName next = animation_get_next(player, p_name); + if (next != StringName() && player->animation_set.has(next)) { + queue(player, next); + } +} +static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player, + AnimationData *p_anim, Node *p_root_override) +{ + // Already cached? + if (p_anim->node_cache.size() == p_anim->animation->get_track_count()) { + return; + } +#if 0 + Node *parent = p_root_override ? p_root_override : get_node(root); + + ERR_FAIL_COND(!parent); +#endif + + Animation *a = p_anim->animation.operator->(); + + p_anim->node_cache.resize(a->get_track_count()); + + for (int i = 0; i < a->get_track_count(); i++) { + p_anim->node_cache.write[i] = NULL; + RES resource; + Vector leftover_path; + /* handle entities here instead */ +#if 0 + Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path); + ERR_CONTINUE_MSG(!child, "On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'."); // couldn't find the child node + uint32_t id = resource.is_valid() ? resource->get_instance_id() : child->get_instance_id(); +#endif + int bone_idx = -1; + int id = -1; /* supposed to be object id but... */ + + if (a->track_get_path(i).get_subname_count() == 1 /* && Object::cast_to(child) */) { + /* use entities */ +#if 0 + Skeleton *sk = Object::cast_to(child); + bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0)); + if (bone_idx == -1) { + continue; + } +#endif + } +#if 0 + + { + if (!child->is_connected("tree_exiting", this, "_node_removed")) { + child->connect("tree_exiting", this, "_node_removed", make_binds(child), CONNECT_ONESHOT); + } + } +#endif + + ECS::TrackNodeCacheKey key; + key.id = id; + key.bone_idx = bone_idx; + + if (!player->node_cache_map.has(key)) + player->node_cache_map[key] = ECS::TrackNodeCache(); + + p_anim->node_cache.write[i] = &player->node_cache_map[key]; + p_anim->node_cache[i]->path = a->track_get_path(i); +#if 0 + p_anim->node_cache[i]->node = child; +#endif + p_anim->node_cache[i]->resource = resource; +#if 0 + p_anim->node_cache[i]->node_2d = Object::cast_to(child); +#endif + if (a->track_get_type(i) == Animation::TYPE_TRANSFORM) { + // special cases and caches for transform tracks +#if 0 + // cache spatial + p_anim->node_cache[i]->spatial = Object::cast_to(child); + // cache skeleton + p_anim->node_cache[i]->skeleton = Object::cast_to(child); +#endif + if (true /* || p_anim->node_cache[i]->skeleton */) { + if (a->track_get_path(i).get_subname_count() == 1) { + StringName bone_name = a->track_get_path(i).get_subname(0); + + /* use entities */ +#if 0 + p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name); +#endif + if (p_anim->node_cache[i]->bone_idx < 0) { + // broken track (nonexistent bone) +#if 0 + p_anim->node_cache[i]->skeleton = nullptr; + p_anim->node_cache[i]->spatial = nullptr; +#endif + ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0); + } + } else { +#if 0 + // no property, just use spatialnode + p_anim->node_cache[i]->skeleton = nullptr; +#endif + } + } + } + + if (a->track_get_type(i) == Animation::TYPE_VALUE) { + if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) { + TrackNodeCache::PropertyAnim pa; + pa.subpath = leftover_path; +#if 0 + pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; +#endif + pa.special = SP_NONE; + pa.owner = p_anim->node_cache[i]; + p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa; + } + } + + if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) { + if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { + TrackNodeCache::BezierAnim ba; + ba.bezier_property = leftover_path; +#if 0 + ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; +#endif + ba.owner = p_anim->node_cache[i]; + + p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba; + } + } + } +} +static void ECS::_animation_process_animation(struct ECS::AnimationPlayerData *player, ECS::AnimationData *p_anim, + float p_time, float p_delta, float p_interp, + bool p_is_current, bool p_seeked, + bool p_started) +{ + _ensure_node_caches(player, p_anim); + ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count()); + + Animation *a = p_anim->animation.operator->(); +#if 0 + bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); +#endif + + for (int i = 0; i < a->get_track_count(); i++) { + // If an animation changes this animation (or it animates itself) + // we need to recreate our animation cache + if (p_anim->node_cache.size() != a->get_track_count()) { + _ensure_node_caches(player, p_anim); + } + + TrackNodeCache *nc = p_anim->node_cache[i]; + + if (!nc) { + continue; // no node cache for this track, skip it + } + + if (!a->track_is_enabled(i)) { + continue; // do nothing if the track is disabled + } + + if (a->track_get_key_count(i) == 0) { + continue; // do nothing if track is empty + } + + switch (a->track_get_type(i)) { + case Animation::TYPE_TRANSFORM: { +#if 0 + if (!nc->spatial) { + continue; + } +#endif + Vector3 loc; + Quat rot; + Vector3 scale; + + Error err = a->transform_track_interpolate(i, p_time, &loc, &rot, &scale); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + if (nc->accum_pass != player->accum_pass) { + ERR_CONTINUE(player->cache_update_size >= NODE_CACHE_UPDATE_MAX); + player->cache_update[player->cache_update_size++] = nc; + nc->accum_pass = player->accum_pass; + nc->loc_accum = loc; + nc->rot_accum = rot; + nc->scale_accum = scale; + + } else { + nc->loc_accum = nc->loc_accum.linear_interpolate(loc, p_interp); + nc->rot_accum = nc->rot_accum.slerp(rot, p_interp); + nc->scale_accum = nc->scale_accum.linear_interpolate(scale, p_interp); + } + + } break; + case Animation::TYPE_VALUE: { +#if 0 + if (!nc->node) { + continue; + } +#endif + + //StringName property=a->track_get_path(i).get_property(); + + Map::Element *E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames()); + ERR_CONTINUE(!E); //should it continue, or create a new one? + + TrackNodeCache::PropertyAnim *pa = &E->get(); + + Animation::UpdateMode update_mode = a->value_track_get_update_mode(i); + + if (update_mode == Animation::UPDATE_CAPTURE) { + if (p_started || pa->capture == Variant()) { + pa->capture = pa->object->get_indexed(pa->subpath); + } + + int key_count = a->track_get_key_count(i); + if (key_count == 0) { + continue; //eeh not worth it + } + + float first_key_time = a->track_get_key_time(i, 0); + float transition = 1.0; + int first_key = 0; + + if (first_key_time == 0.0) { + //ignore, use for transition + if (key_count == 1) { + continue; //with one key we can't do anything + } + transition = a->track_get_key_transition(i, 0); + first_key_time = a->track_get_key_time(i, 1); + first_key = 1; + } + + if (p_time < first_key_time) { + float c = Math::ease(p_time / first_key_time, transition); + Variant first_value = a->track_get_key_value(i, first_key); + Variant interp_value; + Variant::interpolate(pa->capture, first_value, c, interp_value); + + if (pa->accum_pass != player->accum_pass) { + ERR_CONTINUE(player->cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); + player->cache_update_prop[player->cache_update_prop_size++] = pa; + pa->value_accum = interp_value; + pa->accum_pass = player->accum_pass; + } else { + Variant::interpolate(pa->value_accum, interp_value, p_interp, pa->value_accum); + } + + continue; //handled + } + } + + if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE || (p_delta == 0 && update_mode == Animation::UPDATE_DISCRETE)) { //delta == 0 means seek + + Variant value = a->value_track_interpolate(i, p_time); + + if (value == Variant()) { + continue; + } + + //thanks to trigger mode, this should be solved now.. + /* + if (p_delta==0 && value.get_type()==Variant::STRING) + continue; // doing this with strings is messy, should find another way + */ + if (pa->accum_pass != player->accum_pass) { + ERR_CONTINUE(player->cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); + player->cache_update_prop[player->cache_update_prop_size++] = pa; + pa->value_accum = value; + pa->accum_pass = player->accum_pass; + } else { + Variant::interpolate(pa->value_accum, value, p_interp, pa->value_accum); + } + + } else if (p_is_current && p_delta != 0) { + List indices; + a->value_track_get_key_indices(i, p_time, p_delta, &indices); + + for (List::Element *F = indices.front(); F; F = F->next()) { + Variant value = a->track_get_key_value(i, F->get()); + switch (pa->special) { + case SP_NONE: { + bool valid; + pa->object->set_indexed(pa->subpath, value, &valid); //you are not speshul +#if 0 +#ifdef DEBUG_ENABLED + if (!valid) { + ERR_PRINT("Failed setting track value '" + String(pa->owner->path) + "'. Check if property exists or the type of key is valid. Animation '" + a->get_name() + "' at node '" + get_path() + "'."); + } +#endif +#endif + + } break; +#if 0 + case SP_NODE2D_POS: { +#ifdef DEBUG_ENABLED + if (value.get_type() != Variant::VECTOR2) { + ERR_PRINT("Position key at time " + rtos(p_time) + " in Animation Track '" + String(pa->owner->path) + "' not of type Vector2(). Animation '" + a->get_name() + "' at node '" + get_path() + "'."); + } +#endif + static_cast(pa->object)->set_position(value); + } break; + case SP_NODE2D_ROT: { +#ifdef DEBUG_ENABLED + if (value.is_num()) { + ERR_PRINT("Rotation key at time " + rtos(p_time) + " in Animation Track '" + String(pa->owner->path) + "' not numerical. Animation '" + a->get_name() + "' at node '" + get_path() + "'."); + } +#endif + + static_cast(pa->object)->set_rotation(Math::deg2rad((double)value)); + } break; + case SP_NODE2D_SCALE: { +#ifdef DEBUG_ENABLED + if (value.get_type() != Variant::VECTOR2) { + ERR_PRINT("Scale key at time " + rtos(p_time) + " in Animation Track '" + String(pa->owner->path) + "' not of type Vector2()." + a->get_name() + "' at node '" + get_path() + "'."); + } +#endif + + static_cast(pa->object)->set_scale(value); + } break; +#endif + } + } + } + + } break; + case Animation::TYPE_METHOD: { +#if 0 + if (!nc->node) { + continue; + } +#endif + if (p_delta == 0) { + continue; + } + if (!p_is_current) { + break; + } + + List indices; + + a->method_track_get_key_indices(i, p_time, p_delta, &indices); + + for (List::Element *E = indices.front(); E; E = E->next()) { + StringName method = a->method_track_get_name(i, E->get()); + Vector params = a->method_track_get_params(i, E->get()); + + int s = params.size(); + + ERR_CONTINUE(s > VARIANT_ARG_MAX); +#if 0 +#ifdef DEBUG_ENABLED + if (!nc->node->has_method(method)) { + ERR_PRINT("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'."); + } +#endif +#endif + + static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8"); +#if 0 + if (player->can_call) { + if (method_call_mode == ANIMATION_METHOD_CALL_DEFERRED) { + MessageQueue::get_singleton()->push_call( + nc->node, + method, + s >= 1 ? params[0] : Variant(), + s >= 2 ? params[1] : Variant(), + s >= 3 ? params[2] : Variant(), + s >= 4 ? params[3] : Variant(), + s >= 5 ? params[4] : Variant(), + s >= 6 ? params[5] : Variant(), + s >= 7 ? params[6] : Variant(), + s >= 8 ? params[7] : Variant()); + } else { + nc->node->call( + method, + s >= 1 ? params[0] : Variant(), + s >= 2 ? params[1] : Variant(), + s >= 3 ? params[2] : Variant(), + s >= 4 ? params[3] : Variant(), + s >= 5 ? params[4] : Variant(), + s >= 6 ? params[5] : Variant(), + s >= 7 ? params[6] : Variant(), + s >= 8 ? params[7] : Variant()); + } + } +#endif + } + + } break; + case Animation::TYPE_BEZIER: { +#if 0 + if (!nc->node) { + continue; + } +#endif + + Map::Element *E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames()); + ERR_CONTINUE(!E); //should it continue, or create a new one? + + TrackNodeCache::BezierAnim *ba = &E->get(); + + float bezier = a->bezier_track_interpolate(i, p_time); + if (ba->accum_pass != player->accum_pass) { + ERR_CONTINUE(player->cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX); + player->cache_update_bezier[player->cache_update_bezier_size++] = ba; + ba->bezier_accum = bezier; + ba->accum_pass = player->accum_pass; + } else { + ba->bezier_accum = Math::lerp(ba->bezier_accum, bezier, p_interp); + } + + } break; + case Animation::TYPE_AUDIO: { + /* Convert audio support to "server" */ +#if 0 + if (!nc->node) { + continue; + } +#endif + if (p_delta == 0) { + continue; + } + + if (p_seeked) { + //find whatever should be playing + int idx = a->track_find_key(i, p_time); + if (idx < 0) { + continue; + } + + Ref stream = a->audio_track_get_key_stream(i, idx); + if (!stream.is_valid()) { +#if 0 + nc->node->call("stop"); +#endif + nc->audio_playing = false; + player->playing_caches.erase(nc); + } else { + float start_ofs = a->audio_track_get_key_start_offset(i, idx); + start_ofs += p_time - a->track_get_key_time(i, idx); + float end_ofs = a->audio_track_get_key_end_offset(i, idx); + float len = stream->get_length(); + + if (start_ofs > len - end_ofs) { +#if 0 + nc->node->call("stop"); +#endif + nc->audio_playing = false; + player->playing_caches.erase(nc); + continue; + } + +#if 0 + nc->node->call("set_stream", stream); + nc->node->call("play", start_ofs); +#endif + + nc->audio_playing = true; + player->playing_caches.insert(nc); + if (len && end_ofs > 0) { //force a end at a time + nc->audio_len = len - start_ofs - end_ofs; + } else { + nc->audio_len = 0; + } + + nc->audio_start = p_time; + } + + } else { + //find stuff to play + List to_play; + a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play); + if (to_play.size()) { + int idx = to_play.back()->get(); + + Ref stream = a->audio_track_get_key_stream(i, idx); + if (!stream.is_valid()) { +#if 0 + nc->node->call("stop"); +#endif + nc->audio_playing = false; + player->playing_caches.erase(nc); + } else { + float start_ofs = a->audio_track_get_key_start_offset(i, idx); + float end_ofs = a->audio_track_get_key_end_offset(i, idx); + float len = stream->get_length(); + +#if 0 + nc->node->call("set_stream", stream); + nc->node->call("play", start_ofs); +#endif + + nc->audio_playing = true; + player->playing_caches.insert(nc); + if (len && end_ofs > 0) { //force a end at a time + nc->audio_len = len - start_ofs - end_ofs; + } else { + nc->audio_len = 0; + } + + nc->audio_start = p_time; + } + } else if (nc->audio_playing) { + bool loop = a->has_loop(); + + bool stop = false; + + if (!loop && p_time < nc->audio_start) { + stop = true; + } else if (nc->audio_len > 0) { + float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start; + + if (len > nc->audio_len) { + stop = true; + } + } + + if (stop) { + //time to stop +#if 0 + nc->node->call("stop"); +#endif + nc->audio_playing = false; + player->playing_caches.erase(nc); + } + } + } + + } break; + case Animation::TYPE_ANIMATION: { + /* animations of animations - need rework */ +#if 0 + AnimationPlayer *player = Object::cast_to(nc->node); + if (!player) { + continue; + } + + if (p_delta == 0 || p_seeked) { + //seek + int idx = a->track_find_key(i, p_time); + if (idx < 0) { + continue; + } + + float pos = a->track_get_key_time(i, idx); + + StringName anim_name = a->animation_track_get_key_animation(i, idx); + if (String(anim_name) == "[stop]" || !player->has_animation(anim_name)) { + continue; + } + + Ref anim = player->get_animation(anim_name); + + float at_anim_pos; + + if (anim->has_loop()) { + at_anim_pos = Math::fposmod(p_time - pos, anim->get_length()); //seek to loop + } else { + at_anim_pos = MIN(anim->get_length(), p_time - pos); //seek to end + } + + if (player->is_playing() || p_seeked) { + player->play(anim_name); + player->seek(at_anim_pos); + nc->animation_playing = true; + playing_caches.insert(nc); + } else { + player->set_assigned_animation(anim_name); + player->seek(at_anim_pos, true); + } + } else { + //find stuff to play + List to_play; + a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play); + if (to_play.size()) { + int idx = to_play.back()->get(); + + StringName anim_name = a->animation_track_get_key_animation(i, idx); + if (String(anim_name) == "[stop]" || !player->has_animation(anim_name)) { + if (playing_caches.has(nc)) { + playing_caches.erase(nc); + player->stop(); + nc->animation_playing = false; + } + } else { + player->play(anim_name); + player->seek(0.0, true); + nc->animation_playing = true; + playing_caches.insert(nc); + } + } + } +#endif + } break; + } + } +} +static void ECS::_animation_process_data(struct ECS::AnimationPlayerData *player, + 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 next_pos = cd.pos + delta; + float len = cd.from->animation->get_length(); + bool loop = cd.from->animation->has_loop(); + if (!loop) { + if (next_pos < 0) + next_pos = 0; + else if (next_pos > len) + next_pos = len; + } + bool backwards = signbit(delta); // Negative zero means playing backwards too + delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here) + if (&cd == &player->playback.current) { + if (!backwards && cd.pos <= len && next_pos == len) { + //playback finished + player->end_reached = true; + player->end_notify = cd.pos < len; // Notify only if not already at the end + } + if (backwards && cd.pos >= 0 && next_pos == 0) { + //playback finished + player->end_reached = true; + player->end_notify = cd.pos > 0; // Notify only if not already at the beginning + } + } else { + float looped_next_pos = Math::fposmod(next_pos, len); + if (looped_next_pos == 0 && next_pos != 0) + // Loop multiples of the length to it, rather than 0 + // so state at time=length is previewable in the editor + next_pos = len; + else + next_pos = looped_next_pos; + } + cd.pos = next_pos; + _animation_process_animation(player, cd.from, + cd.pos, delta, p_blend, &cd == &player->playback.current, + p_seeked, p_started); +} +static void ECS::_animation_process2(struct ECS::AnimationPlayerData *player, + float p_delta, bool p_started) +{ + ECS::Playback &c = player->playback; + player->accum_pass++; + + _animation_process_data(player, c.current, + p_delta, 1.0f, + c.seeked && p_delta != 0, p_started); + if (p_delta != 0) + c.seeked = false; + List::Element *prev = nullptr; + for (List::Element *E = c.blend.back(); E; E = prev) { + Blend &b = E->get(); + float blend = b.blend_left / b.blend_time; + _animation_process_data(player, b.data, p_delta, blend, false, false); + b.blend_left -= Math::absf(player->speed_scale * p_delta); + prev = E->prev(); + if (b.blend_left < 0) + c.blend.erase(E); + } +} +static void ECS::_animation_update_transforms(struct ECS::AnimationPlayerData *player) +{ + int i; + { + Transform t; + for (i = 0; i < player->cache_update_size; i++) { + TrackNodeCache *nc = player->cache_update[i]; + t.origin = nc->loc_accum; + t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum); +#if 0 + /* update transform on skeleton or node */ + if (nc->skeleton && nc->bone_idx >= 0) + nc->skeleton->set_bone_pose(nc->bone_idx, t); + else if (nc->spatial) + nc->spatial->set_transform(t); +#endif + } + } + player->cache_update_size = 0; + for (i = 0; i < player->cache_update_prop_size; i++) { + TrackNodeCache::PropertyAnim *pa = player->cache_update_prop[i]; + switch (pa->special) { + case SP_NONE: { + bool valid; + pa->object->set_indexed(pa->subpath, pa->value_accum, &valid); + if (!valid) { + /* error setting key + ERR_PRINT("Failed setting key at time " + rtos(playback.current.pos) + " in Animation '" + get_current_animation() + "' at Node '" + get_path() + "', Track '" + String(pa->owner->path) + "'. Check if property exists or the type of key is right for the property"); + */ + } + } break; + case SP_NODE2D_POS: +// static_cast(pa->object)->set_position(pa->value_accum); + break; + case SP_NODE2D_ROT: +// static_cast(pa->object)->set_rotation(Math::deg2rad((double)pa->value_accum)); + break; + case SP_NODE2D_SCALE: +// static_cast(pa->object)->set_scale(pa->value_accum); + break; + } + } + player->cache_update_prop_size = 0; + for (i = 0; i < player->cache_update_bezier_size; i++) { + TrackNodeCache::BezierAnim *ba = player->cache_update_bezier[i]; + ba->object->set_indexed(ba->bezier_property, ba->bezier_accum); + } + player->cache_update_bezier_size = 0; +} +static void ECS::_set_process(struct ECS::AnimationPlayerData *player, bool p_process, bool p_force) +{ + /* Nothing */ +} +static void ECS::_animation_process(struct ECS::AnimationPlayerData *player, float p_time) +{ + if (player->playback.current.from) { + player->end_reached = false; + player->end_notify = false; + _animation_process2(player, p_time, player->playback.started); + if (player->playback.started) + player->playback.started = false; + _animation_update_transforms(player); + if (player->end_reached) { + if (player->queued.size()) { + String old = player->playback.assigned; + play(player, player->queued.front()->get()); + String new_name = player->playback.assigned; + player->queued.pop_front(); + if (player->end_notify) { + /* emit animation_changed */ + } + } else { + player->playing = false; + _set_process(player, false); + if (player->end_notify) { + /* emit animation_finished */ + } + } + player->end_reached = false; + } + } else + _set_process(player, false); +} +void ECS::_stop_playing_caches(struct ECS::AnimationPlayerData *player) { +#if 0 + for (Set::Element *E = player->playing_caches.front(); E; E = E->next()) { + if (E->get()->node && E->get()->audio_playing) { + E->get()->node->call("stop"); + } + if (E->get()->node && E->get()->animation_playing) { + AnimationPlayer *player = Object::cast_to(E->get()->node); + if (!player) { + continue; + } + player->stop(); + } + } +#endif + player->playing_caches.clear(); +} + +void ECS::queue(struct ECS::AnimationPlayerData *player, + const StringName &p_name) +{ + if (!player->playing) { + play(player, p_name); + } else { + player->queued.push_back(p_name); + } +} +StringName ECS::animation_get_next(struct ECS::AnimationPlayerData *player, + const StringName &p_animation) +{ + if (!player->animation_set.has(p_animation)) { + return StringName(); + } + return player->animation_set[p_animation].next; +} + +String ECS::get_current_animation(struct ECS::AnimationPlayerData *player) +{ + return player->playing ? player->playback.assigned : ""; +} +Error ECS::add_animation(struct ECS::AnimationPlayerData *player, + const StringName &p_name, const Ref &p_animation) +{ + ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); + + if (player->animation_set.has(p_name)) { + _unref_anim(player, player->animation_set[p_name].animation); + player->animation_set[p_name].animation = p_animation; + clear_caches(player); + } else { + AnimationData ad; + ad.animation = p_animation; + ad.name = p_name; + player->animation_set[p_name] = ad; + } + + _ref_anim(player, p_animation); + return OK; +} +void ECS::remove_animation(struct ECS::AnimationPlayerData *player, + const StringName &p_name) +{ + ERR_FAIL_COND(!player->animation_set.has(p_name)); + +#if 0 + stop(); +#endif + _unref_anim(player, player->animation_set[p_name].animation); + player->animation_set.erase(p_name); + + clear_caches(player); +} +static void ECS::_ref_anim(struct ECS::AnimationPlayerData *player, + const Ref &p_anim) +{ +} + +static void ECS::_unref_anim(struct ECS::AnimationPlayerData *player, + const Ref &p_anim) +{ +} + +void ECS::clear_caches(struct ECS::AnimationPlayerData *player) +{ + _stop_playing_caches(player); + player->node_cache_map.clear(); + for (Map::Element *E = player->animation_set.front(); E; E = E->next()) + E->get().node_cache.clear(); + player->cache_update_size = 0; + player->cache_update_prop_size = 0; + player->cache_update_bezier_size = 0; +} + +void ECS::advance(struct ECS::AnimationPlayerData *player, float p_time) +{ + ECS::_animation_process(player, p_time); +} diff --git a/src/modules/character/animation_system.h b/src/modules/character/animation_system.h new file mode 100644 index 0000000..8e9d6ed --- /dev/null +++ b/src/modules/character/animation_system.h @@ -0,0 +1,251 @@ +#ifndef ANIMATION_SYSTEM_H +#define ANIMATION_SYSTEM_H +namespace ECS { +struct AnimationPlayer; +struct AnimationTreee; +enum AnimationProcessMode { + ANIMATION_PROCESS_PHYSICS, + ANIMATION_PROCESS_IDLE, + ANIMATION_PROCESS_MANUAL, +}; +enum AnimationMethodCallMode { + ANIMATION_METHOD_CALL_DEFERRED, + ANIMATION_METHOD_CALL_IMMEDIATE, +}; +enum SpecialProperty { + SP_NONE, + SP_NODE2D_POS, + SP_NODE2D_ROT, + SP_NODE2D_SCALE, +}; +struct TrackNodeCache { + NodePath path; + uint32_t id; + RES resource; +#if 0 + Node *node; + Spatial *spatial; + Node2D *node_2d; + Skeleton *skeleton; +#endif + int bone_idx; + // accumulated transforms + Vector3 loc_accum; + Quat rot_accum; + Vector3 scale_accum; + uint64_t accum_pass; + bool audio_playing; + float audio_start; + float audio_len; + bool animation_playing; + struct PropertyAnim { + TrackNodeCache *owner; + SpecialProperty special; + Vector subpath; + Object *object; + Variant value_accum; + uint64_t accum_pass; + Variant capture; + PropertyAnim() : + owner(nullptr), + special(SP_NONE), + object(nullptr), + accum_pass(0) + { + } + }; + Map property_anim; + struct BezierAnim { + Vector bezier_property; + TrackNodeCache *owner; + float bezier_accum; + Object *object; + uint64_t accum_pass; + + BezierAnim() : + owner(nullptr), + bezier_accum(0.0), + object(nullptr), + accum_pass(0) {} + }; + Map bezier_anim; + TrackNodeCache() : + id(0), +#if 0 + node(nullptr), + spatial(nullptr), + node_2d(nullptr), + skeleton(nullptr), +#endif + bone_idx(-1), + accum_pass(0), + audio_playing(false), + audio_start(0.0), + audio_len(0.0), + animation_playing(false) {} +}; +struct AnimationData { + String name; + StringName next; + Vector node_cache; + Ref animation; +}; +struct PlaybackData { + AnimationData *from; + float pos; + float speed_scale; + + PlaybackData() { + pos = 0; + speed_scale = 1.0; + from = nullptr; + } +}; +struct Blend { + PlaybackData data; + float blend_time; + float blend_left; + Blend() { + blend_left = 0.0f; + blend_time = 0.0f; + } +}; +struct Playback { + List blend; + PlaybackData current; + StringName assigned; + bool seeked; + bool started; +}; +struct BlendKey { + StringName from; + StringName to; + bool operator<(const BlendKey &bk) const { return from == bk.from ? String(to) < String(bk.to) : String(from) < String(bk.from); } +}; +struct TrackNodeCacheKey { + uint32_t id; + int bone_idx; + + inline bool operator<(const TrackNodeCacheKey &p_right) const + { + if (id < p_right.id) { + return true; + } else if (id > p_right.id) { + return false; + } else { + return bone_idx < p_right.bone_idx; + } + } +}; +enum { + NODE_CACHE_UPDATE_MAX = 1024, + BLEND_FROM_MAX = 3 +}; +struct AnimationPlayerData { + Map node_cache_map; + TrackNodeCache *cache_update[NODE_CACHE_UPDATE_MAX]; + int cache_update_size; + TrackNodeCache::PropertyAnim *cache_update_prop[NODE_CACHE_UPDATE_MAX]; + int cache_update_prop_size; + TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX]; + int cache_update_bezier_size; + Set playing_caches; + uint64_t accum_pass; + float speed_scale; + float default_blend_time; + Map animation_set; + + Map blend_times; + Playback playback; + List queued; + bool end_reached; + bool end_notify; + String autoplay; + bool reset_on_save; + AnimationProcessMode animation_process_mode; + AnimationMethodCallMode method_call_mode; + bool processing; + bool active; + NodePath root; + bool playing; +}; +/* public functions */ +StringName find_animation(struct AnimationPlayerData *player, + const Ref &p_animation); +Error add_animation(struct AnimationPlayerData *player, + const StringName &p_name, const Ref &p_animation); +void remove_animation(struct AnimationPlayerData *player, + const StringName &p_name); +void rename_animation(struct AnimationPlayerData *player, + const StringName &p_name, const StringName &p_new_name); +bool has_animation(struct AnimationPlayerData *player, + const StringName &p_name); +Ref get_animation(struct AnimationPlayerData *player, + const StringName &p_name); +void get_animation_list(struct AnimationPlayerData *player, + List *p_animations); +void set_blend_time(struct AnimationPlayerData *player, + const StringName &p_animation1, const StringName &p_animation2, float p_time); +float get_blend_time(struct AnimationPlayerData *player, + const StringName &p_animation1, const StringName &p_animation2); +void animation_set_next(struct AnimationPlayerData *player, + const StringName &p_animation, const StringName &p_next); +StringName animation_get_next(struct AnimationPlayerData *player, + const StringName &p_animation); +void set_default_blend_time(struct AnimationPlayerData *player, + float p_default); +float get_default_blend_time(struct AnimationPlayerData *player); +void play(struct AnimationPlayerData *player, + const StringName &p_name = StringName(), + float p_custom_blend = -1, float p_custom_scale = 1.0, + bool p_from_end = false); +void play_backwards(struct AnimationPlayerData *player, + const StringName &p_name = StringName(), float p_custom_blend = -1); +void queue(struct AnimationPlayerData *player, + const StringName &p_name); +PoolVector get_queue(struct AnimationPlayerData *player); +void clear_queue(struct AnimationPlayerData *player); +void stop(struct AnimationPlayerData *player, + bool p_reset = true); +bool is_playing(struct AnimationPlayerData *player); +String get_current_animation(struct AnimationPlayerData *player); +void set_current_animation(struct AnimationPlayerData *player, + const String &p_anim); +String get_assigned_animation(struct AnimationPlayerData *player); +void set_assigned_animation(struct AnimationPlayerData *player, + const String &p_anim); +void set_active(struct AnimationPlayerData *player, + bool p_active); +bool is_active(struct AnimationPlayerData *player); +bool is_valid(struct AnimationPlayerData *player); +void set_speed_scale(struct AnimationPlayerData *player, + float p_speed); +float get_speed_scale(struct AnimationPlayerData *player); +float get_playing_speed(struct AnimationPlayerData *player); +void set_autoplay(struct AnimationPlayerData *player, + const String &p_name); +String get_autoplay(struct AnimationPlayerData *player); +void set_reset_on_save_enabled(struct AnimationPlayerData *player, + bool p_enabled); +bool is_reset_on_save_enabled(struct AnimationPlayerData *player); +void set_animation_process_mode(struct AnimationPlayerData *player, + AnimationProcessMode p_mode); +AnimationProcessMode get_animation_process_mode(struct AnimationPlayerData *player); +void set_method_call_mode(struct AnimationPlayerData *player, + AnimationMethodCallMode p_mode); +AnimationMethodCallMode get_method_call_mode(struct AnimationPlayerData *player); + +void seek(struct AnimationPlayerData *player, + float p_time, bool p_update = false); +void seek_delta(struct AnimationPlayerData *player, + float p_time, float p_delta); +float get_current_animation_position(struct AnimationPlayerData *player); +float get_current_animation_length(struct AnimationPlayerData *player); +void advance(struct AnimationPlayerData *player, float p_time); +void set_root(struct AnimationPlayerData *player, const NodePath &p_root); +NodePath get_root(struct AnimationPlayerData *player); +void clear_caches(struct AnimationPlayerData *player); +void get_argument_options(struct AnimationPlayerData *player, + const StringName &p_function, int p_idx, List *r_options); +} +#endif diff --git a/src/modules/character/character.cpp b/src/modules/character/character.cpp index c4ef909..1df9692 100644 --- a/src/modules/character/character.cpp +++ b/src/modules/character/character.cpp @@ -15,6 +15,7 @@ #include #include "character.h" #include "skeleton-prep.h" +#include "animation_system.h" namespace ECS { Skeleton::Skeleton() @@ -33,20 +34,10 @@ ECS::RigidBody::RigidBody(RID rid, enum type type) { this->body = rid; this->type = type; - // body = ps->body_create(PhysicsServer::BODY_MODE_KINEMATIC, true); - // print_line("created body"); } ECS::RigidBody::~RigidBody() { - /* - if (initialized && body != RID()) { - PhysicsServer *ps = PhysicsServer::get_singleton(); - ps->free(body); - body = RID(); - } - */ -// print_line("destroyed body"); } ECS::CShape::CShape(RID body, const Transform &offset, Ref shape) @@ -68,10 +59,6 @@ ECS::CharacterSlot::CharacterSlot(Ref mesh, Ref skin) this->name = ""; this->mesh = mesh; this->skin = skin; -#if 0 - if (!skin.is_null()) - this->skin_internal = skin; -#endif this->bound = false; } @@ -129,13 +116,6 @@ int Character::create_character(int id) .child_of(root) .emplace(scene_data[id].meshes[i].mesh, scene_data[id].meshes[i].skin); -#if 0 - flecs::log::trace("created slot - "); - if (entity.get()->instance.is_valid()) - print_line("instance is valid 1"); - else - print_line("instance is invalid 1"); -#endif } // print_line("character created"); int xid = entities.size(); @@ -169,20 +149,6 @@ void Character::destroy_character(int xid) pb->body = RID(); entity.modified(); } -#if 0 - entity.children([&](flecs::entity child) { - if (child.has()) { - ECS::CharacterSlot *cs = child.get_mut(); - print_line("entity is CharacterSlot"); - if (cs->instance.is_valid()) - print_line("instance is valid 2"); - else - print_line("instance is invalid 2"); - } else if (child.has()) { - print_line("entity is CShape"); - } - }); -#endif entity.destruct(); print_line("character destroyed"); } @@ -230,22 +196,6 @@ void Character::set_scene(int id, const Ref &scene) md.mesh = mi->get_mesh(); md.offset = xform; scene_data[id].meshes.push_back(md); -#if 0 - Ref skin = mi->get_skin(); - bool found = false; - for (Set::Element *e = - scene_data[id].skin_bindings.front(); e; e = e->next()) { - if (e->get()->skin == skin) { - found = true; - break; - } - } - if (!found) { - if (skin.is_null()) { - #error GENERATE SKIN - } - } -#endif } CollisionShape *cshape = Object::cast_to(tmp); if (cshape) { @@ -319,7 +269,7 @@ void Character::init_bone() ecs.component() .on_add([](flecs::entity e, ECS::BoneComponent &s) { - flecs::log::trace("added bonecomponent %s", e.name().c_str()); + // flecs::log::trace("added bonecomponent %s", e.name().c_str()); flecs::entity parent = e.parent(); parent.add(); if (parent.has()) { @@ -346,7 +296,7 @@ void Character::init_bone() skel->dirty = true; String out; VariantWriter::write_to_string(s.rest, out); - flecs::log::trace("bonecomponent set %s (%d) [%s]", e.name().c_str(), skel->bone_count, out.ascii().get_data()); + // flecs::log::trace("bonecomponent set %s (%d) [%s]", e.name().c_str(), skel->bone_count, out.ascii().get_data()); parent.remove(); parent.add(); }); @@ -357,11 +307,13 @@ void Character::init_bone() }) .on_set([](flecs::entity e, ECS::BonePose &s) { +#if 0 const Transform &xform = s.pose; String out; VariantWriter::write_to_string(xform, out); flecs::log::trace("bone pose set %s: %d %s", e.name().c_str(), s.index, out.ascii().get_data()); +#endif e.add(); }); ecs.system() @@ -381,13 +333,23 @@ void Character::init_bone() skel->dirty = true; String out; VariantWriter::write_to_string(s.pose, out); - flecs::log::trace("set bonepose %s %d %s", e.name().c_str(), s.index, out.ascii().get_data()); + // flecs::log::trace("set bonepose %s %d %s", e.name().c_str(), s.index, out.ascii().get_data()); parent.modified(); parent.add(); parent.remove(); e.remove(); }); } +void Character::animation_system_init() +{ + ecs.component(); + ecs.system("UpdateAnimation") + .kind(flecs::OnUpdate) + .each([](flecs::entity e, ECS::AnimationPlayerData &player) + { + ECS::advance(&player, SceneTree::get_singleton()->get_physics_process_time()); + }); +} void Character::initialize() { Error err; @@ -420,193 +382,6 @@ void Character::initialize() if (err == OK && scene2.is_valid()) set_scene(1, scene2); initialized = true; -#if 0 - ecs.system("BindSkin") - .without() - .kind(flecs::OnUpdate) - .each([this](flecs::entity e, ECS::CharacterSlot &slot) - { - flecs::entity parent = e.parent(); - ECS::Skeleton *skel = parent.get_mut(); - const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr(); - const int *order = skel->process_order.ptr(); - if (skel->skin.is_null() { - - } - } -#endif -#if 0 -#endif -#if 0 - ecs.system("BindSkeleton") - .without() - .kind(flecs::PreUpdate) - .each([this](flecs::entity e, ECS::CharacterSlot &slot) - { - flecs::entity parent = e.parent(); - if (!parent.get()) - return; - if (parent.get()) - return; - flecs::log::trace("unbound skeleton at %s", e.name().c_str()); - int i; - ECS::Skeleton *skel = parent.get_mut(); - if (!skel->skin.is_null()) - return; - const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr(); - const int *order = skel->process_order.ptr(); - VisualServer *vs = VS::get_singleton(); - int len = skel->bones.size(); - if (slot.skin.is_null() || slot.skin_internal.is_null()) { - flecs::log::err("skin is null for %s", e.name().c_str()); - return; - } -#if 0 - if (!skel->skin.is_null()) { - slot.skin = skel->skin; - vs->instance_attach_skeleton(slot.instance, skel->skeleton); - parent.add(); - e.add(); - return; - } -#endif - assert(skel->skeleton.is_valid()); - uint32_t bind_count = 0U; - bind_count = skel->skin->get_bind_count(); - skel->skin_bone_indices.resize(bind_count); - skel->skin_bone_indices_ptrs = skel->skin_bone_indices.ptrw(); - for (i = 0; i < bind_count; i++) { - StringName bind_name = skel->skin->get_bind_name(i); - if (bind_name != StringName()) { - int j; - bool found = false; - for (j = 0; j < len; j++) { - if (bonesptr[j].name == bind_name) { - skel->skin_bone_indices_ptrs[i] = j; - found = true; - break; - } - } - if (!found) { - skel->skin_bone_indices_ptrs[i] = 0; - flecs::log::err("no bind name %s in skeleton", String(bind_name).ascii().get_data()); - } - } else if (skel->skin->get_bind_bone(i) >= 0) { - uint32_t bind_index = skel->skin->get_bind_bone(i); - if (bind_index >= len) { - skel->skin_bone_indices_ptrs[i] = 0; - flecs::log::err("bind %d bad index %d", i, bind_index); - } else - skel->skin_bone_indices_ptrs[i] = bind_index; - } else { - flecs::log::err("no bind name for %d", i); - skel->skin_bone_indices_ptrs[i] = 0; - } - } - assert(bind_count > 0); - vs->skeleton_allocate(skel->skeleton, bind_count); - flecs::log::trace("skeltton is prepared and bound for %s", e.name().c_str()); - parent.modified(); - parent.add(); - assert(false); - }); - ecs.system("Check2") - .kind(flecs::PostUpdate) - .each([this](flecs::entity e, ECS::Skeleton &skel) - { - if (e.has()) - flecs::log::trace("is dirty"); - if (e.has()) - flecs::log::trace("is bound"); - if (e.has()) - flecs::log::trace("transform ready"); - flecs::log::trace("end of frame systems"); - }); -#endif -#if 0 - ecs.system("PrepareSkins2") - .kind(flecs::OnUpdate) - .each([this](flecs::entity e, ECS::CharacterSlot &slot) - { - int i; - flecs::entity parent = e.parent(); - ECS::Skeleton *skel = parent.get_mut(); - if (slot.bound && !skel->dirty) - return; - const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr(); - const int *order = skel->process_order.ptr(); - VisualServer *vs = VS::get_singleton(); - uint32_t bind_count = 0U; - if (!slot.bound) { - int len = skel->bones.size(); - if (slot.skin.is_null()) - flecs::log::trace("skin is null for %s", e.name().c_str()); - if (slot.skin_internal.is_null() && skel->skin.is_null()) { - flecs::log::trace("creating skin for %s", e.name().c_str()); - slot.skin_internal.instance(); - slot.skin_internal->set_bind_count(skel->bones.size()); - for (i = 0; i < len; i++) { - const struct ECS::Skeleton::bone_data &b = bonesptr[order[i]]; - if (b.parent >= 0) - slot.skin_internal->set_bind_pose(order[i], slot.skin_internal->get_bind_pose(b.parent) * b.rest); - else - slot.skin_internal->set_bind_pose(order[i], b.rest); - } - for (i = 0; i < len; i++) { - slot.skin_internal->set_bind_bone(i, i); - slot.skin_internal->set_bind_pose(i, slot.skin_internal->get_bind_pose(i).affine_inverse()); - } - flecs::log::trace("created skin for %s: %d", e.name().c_str(), len); - } - assert(skel->skeleton.is_valid()); - skel->skin = slot.skin_internal; - bind_count = skel->skin->get_bind_count(); - assert(bind_count > 0); - skel->skin_bone_indices.resize(bind_count); - skel->skin_bone_indices_ptrs = skel->skin_bone_indices.ptrw(); - for (i = 0; i < bind_count; i++) { - StringName bind_name = skel->skin->get_bind_name(i); - if (bind_name != StringName()) { - int j; - bool found = false; - for (j = 0; j < len; j++) { - if (bonesptr[j].name == bind_name) { - skel->skin_bone_indices_ptrs[i] = j; - found = true; - break; - } - } - if (!found) { - skel->skin_bone_indices_ptrs[i] = 0; - flecs::log::err("no bind name %s in skeleton", String(bind_name).ascii().get_data()); - } - } else if (skel->skin->get_bind_bone(i) >= 0) { - uint32_t bind_index = skel->skin->get_bind_bone(i); - if (bind_index >= len) { - skel->skin_bone_indices_ptrs[i] = 0; - flecs::log::err("bind %d bad index %d", i, bind_index); - } else - skel->skin_bone_indices_ptrs[i] = bind_index; - } else { - flecs::log::err("no bind name for %d", i); - skel->skin_bone_indices_ptrs[i] = 0; - } - } - vs->skeleton_allocate(skel->skeleton, bind_count); - vs->instance_attach_skeleton(slot.instance, skel->skeleton); - slot.bound = true; - flecs::log::trace("skeltton is prepared and bound for %s", e.name().c_str()); - e.modified(); - } - bind_count = skel->skin->get_bind_count(); - for (i = 0; i < (int)bind_count; i++) { - uint32_t bone_index = skel->skin_bone_indices_ptrs[i]; - vs->skeleton_bone_set_transform(skel->skeleton, - i, bonesptr[bone_index].pose_global *skel->skin->get_bind_pose(i)); - } - skel->dirty = false; - }); -#endif ecs.system("UpdateBone") .with() .kind(flecs::OnUpdate) @@ -620,9 +395,10 @@ void Character::initialize() float delta = SceneTree::get_singleton()->get_physics_process_time(); struct ECS::BonePose *bp = root.get_mut(); // bp->pose.basis = bp->pose.basis.rotated(Vector3(0, 1, 0), 0.016f); - bp->pose.origin += Vector3(0.1f * delta, 0.0f, 0.0f); + bp->pose.origin += Vector3(0.2f * delta, 0.0f, 0.0f); root.modified(); }); + animation_system_init(); if (!Engine::get_singleton()->is_editor_hint()) { err = st->connect("physics_frame", this, "_progress"); assert(err == OK); diff --git a/src/modules/character/character.h b/src/modules/character/character.h index da8e5e0..ba5f404 100644 --- a/src/modules/character/character.h +++ b/src/modules/character/character.h @@ -142,6 +142,7 @@ protected: void init_skeleton(); void init_bone(); void init_slot(); + void animation_system_init(); void initialize(); HashMap entities; bool initialized; diff --git a/src/modules/character/character_skeleton.cpp b/src/modules/character/character_skeleton.cpp index 54df42e..90bc161 100644 --- a/src/modules/character/character_skeleton.cpp +++ b/src/modules/character/character_skeleton.cpp @@ -79,6 +79,7 @@ void Character::init_skeleton() if (pass_count >= len * len) flecs::log::err("Skeleton parenthood graph is cyclic"); sk.process_order_dirty = false; + flecs::log::trace("skeletons: %d dirty: sorting bones done", sk.bone_count); } e.add(); }); @@ -94,7 +95,7 @@ void Character::init_skeleton() // flecs::log::trace("skeletons: %d dirty: update try", sk.bone_count); if (sk.process_order_dirty) return; - flecs::log::trace("skeletons: %d dirty: update", sk.bone_count); +// flecs::log::trace("skeletons: %d dirty: update", sk.bone_count); struct ECS::Skeleton::bone_data *bonesptr = sk.bones.ptrw(); int len = sk.bones.size(); const int *order = sk.process_order.ptr(); @@ -145,13 +146,18 @@ void Character::init_skeleton() } } } - flecs::log::trace("transform should be ready"); +// flecs::log::trace("transform should be ready"); e.add(); assert(sk.skin_bone_indices_ptrs); uint32_t bind_count = sk.skin->get_bind_count(); + if (bind_count == 0) + return; + if (!e.has()) + return; for (i = 0; i < (int)bind_count; i++) { uint32_t bone_index = sk.skin_bone_indices_ptrs[i]; Transform xform = bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i); +#if 0 if (i == 0) { String out; VariantWriter::write_to_string(xform, out); @@ -159,12 +165,14 @@ void Character::init_skeleton() i, bonesptr[bone_index].name.ascii().get_data(), out.ascii().get_data()); } +#endif vs->skeleton_bone_set_transform(sk.skeleton, i, bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i)); } sk.dirty = false; e.remove(); }); +#if 0 ecs.system("c1") .with() .kind(flecs::OnUpdate) @@ -193,6 +201,7 @@ void Character::init_skeleton() { flecs::log::trace("order prepared"); }); +#endif #if 0 ecs.system("Check_") .kind(flecs::PreUpdate) diff --git a/src/modules/character/character_slot.cpp b/src/modules/character/character_slot.cpp index b03f018..c8ec1d6 100644 --- a/src/modules/character/character_slot.cpp +++ b/src/modules/character/character_slot.cpp @@ -30,9 +30,9 @@ void Character::init_slot() }) .on_set([st](flecs::entity e, ECS::CharacterSlot &s) { - flecs::log::trace("set slot %s", e.name().c_str()); +// flecs::log::trace("set slot %s", e.name().c_str()); if (s.mesh.is_null() || !s.mesh.is_valid()) - flecs::log::trace("invalid mesh %s", e.name().c_str()); + flecs::log::err("invalid mesh %s", e.name().c_str()); }); ecs.system("UpdateSlot") .without() @@ -47,7 +47,7 @@ void Character::init_slot() slot.instance = vs->instance_create(); vs->instance_set_scenario(slot.instance, scenario); vs->instance_set_base(slot.instance, slot.mesh->get_rid()); - flecs::log::trace("created instance for %s", e.name().c_str()); +// flecs::log::trace("created instance for %s", e.name().c_str()); } }); ecs.system("AttachToSkeleton") @@ -63,7 +63,7 @@ void Character::init_slot() assert(slot.instance.is_valid()); const ECS::Skeleton *skel = parent.get(); assert(skel->skeleton.is_valid()); - flecs::log::trace("binding slot for %s", e.name().c_str()); +// flecs::log::trace("binding slot for %s", e.name().c_str()); if (skel->skeleton.is_valid()) slot.skeleton = skel->skeleton; if (slot.skin.is_null() && !skel->skin.is_null()) @@ -75,7 +75,7 @@ void Character::init_slot() slot.bound = true; e.add(); parent.add(); - flecs::log::trace("bound slot for %s", e.name().c_str()); +// flecs::log::trace("bound slot for %s", e.name().c_str()); } }); ecs.system("BindSlotsToSkeleton") @@ -93,11 +93,11 @@ void Character::init_slot() if (skel->skin.is_null() && slot.skin_internal.is_null()) { const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr(); const int *order = skel->process_order.ptr(); - flecs::log::trace("binding slot for %s", e.name().c_str()); +// flecs::log::trace("binding slot for %s", e.name().c_str()); slot.skin_internal.instance(); slot.skin_internal->set_bind_count(skel->bones.size()); int len = skel->bones.size(); - flecs::log::trace("creating skin for %s", e.name().c_str()); +// flecs::log::trace("creating skin for %s", e.name().c_str()); for (i = 0; i < len; i++) { const struct ECS::Skeleton::bone_data &b = bonesptr[order[i]]; if (b.parent >= 0) @@ -110,7 +110,7 @@ void Character::init_slot() slot.skin_internal->set_bind_pose(i, slot.skin_internal->get_bind_pose(i).affine_inverse()); } skel->skin = slot.skin_internal; - flecs::log::trace("created skin for %s: %d", e.name().c_str(), len); +// flecs::log::trace("created skin for %s: %d", e.name().c_str(), len); assert(skel->skeleton.is_valid()); skel->skin = slot.skin_internal; uint32_t bind_count = skel->skin->get_bind_count();