Added animation system

This commit is contained in:
2023-09-14 12:21:26 +03:00
parent 22482cc9b2
commit 2ac6c4afe1
6 changed files with 1260 additions and 252 deletions

View File

@@ -0,0 +1,971 @@
#include <scene/resources/animation.h>
#include <servers/audio/audio_stream.h>
#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<Animation> &p_anim);
static void _unref_anim(struct AnimationPlayerData *player,
const Ref<Animation> &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<PropertyInfo> *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<StringName> 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<Skeleton>(child) */) {
/* use entities */
#if 0
Skeleton *sk = Object::cast_to<Skeleton>(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<Node2D>(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<Spatial>(child);
// cache skeleton
p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton>(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<StringName, TrackNodeCache::PropertyAnim>::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<int> indices;
a->value_track_get_key_indices(i, p_time, p_delta, &indices);
for (List<int>::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<Node2D *>(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<Node2D *>(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<Node2D *>(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<int> indices;
a->method_track_get_key_indices(i, p_time, p_delta, &indices);
for (List<int>::Element *E = indices.front(); E; E = E->next()) {
StringName method = a->method_track_get_name(i, E->get());
Vector<Variant> 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<StringName, TrackNodeCache::BezierAnim>::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<AudioStream> 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<int> 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<AudioStream> 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<AnimationPlayer>(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<Animation> 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<int> 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<Blend>::Element *prev = nullptr;
for (List<Blend>::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<Node2D *>(pa->object)->set_position(pa->value_accum);
break;
case SP_NODE2D_ROT:
// static_cast<Node2D *>(pa->object)->set_rotation(Math::deg2rad((double)pa->value_accum));
break;
case SP_NODE2D_SCALE:
// static_cast<Node2D *>(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<TrackNodeCache *>::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<AnimationPlayer>(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<Animation> &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<Animation> &p_anim)
{
}
static void ECS::_unref_anim(struct ECS::AnimationPlayerData *player,
const Ref<Animation> &p_anim)
{
}
void ECS::clear_caches(struct ECS::AnimationPlayerData *player)
{
_stop_playing_caches(player);
player->node_cache_map.clear();
for (Map<StringName, AnimationData>::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);
}

View File

@@ -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<StringName> subpath;
Object *object;
Variant value_accum;
uint64_t accum_pass;
Variant capture;
PropertyAnim() :
owner(nullptr),
special(SP_NONE),
object(nullptr),
accum_pass(0)
{
}
};
Map<StringName, PropertyAnim> property_anim;
struct BezierAnim {
Vector<StringName> 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<StringName, BezierAnim> 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<TrackNodeCache *> node_cache;
Ref<Animation> 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> 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<TrackNodeCacheKey, TrackNodeCache> 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<TrackNodeCache *> playing_caches;
uint64_t accum_pass;
float speed_scale;
float default_blend_time;
Map<StringName, AnimationData> animation_set;
Map<BlendKey, float> blend_times;
Playback playback;
List<StringName> 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<Animation> &p_animation);
Error add_animation(struct AnimationPlayerData *player,
const StringName &p_name, const Ref<Animation> &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<Animation> get_animation(struct AnimationPlayerData *player,
const StringName &p_name);
void get_animation_list(struct AnimationPlayerData *player,
List<StringName> *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<String> 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<String> *r_options);
}
#endif

View File

@@ -15,6 +15,7 @@
#include <scene/main/viewport.h> #include <scene/main/viewport.h>
#include "character.h" #include "character.h"
#include "skeleton-prep.h" #include "skeleton-prep.h"
#include "animation_system.h"
namespace ECS { namespace ECS {
Skeleton::Skeleton() Skeleton::Skeleton()
@@ -33,20 +34,10 @@ ECS::RigidBody::RigidBody(RID rid, enum type type)
{ {
this->body = rid; this->body = rid;
this->type = type; this->type = type;
// body = ps->body_create(PhysicsServer::BODY_MODE_KINEMATIC, true);
// print_line("created body");
} }
ECS::RigidBody::~RigidBody() 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> shape) ECS::CShape::CShape(RID body, const Transform &offset, Ref<Shape> shape)
@@ -68,10 +59,6 @@ ECS::CharacterSlot::CharacterSlot(Ref<ArrayMesh> mesh, Ref<Skin> skin)
this->name = ""; this->name = "";
this->mesh = mesh; this->mesh = mesh;
this->skin = skin; this->skin = skin;
#if 0
if (!skin.is_null())
this->skin_internal = skin;
#endif
this->bound = false; this->bound = false;
} }
@@ -129,13 +116,6 @@ int Character::create_character(int id)
.child_of(root) .child_of(root)
.emplace<ECS::CharacterSlot>(scene_data[id].meshes[i].mesh, .emplace<ECS::CharacterSlot>(scene_data[id].meshes[i].mesh,
scene_data[id].meshes[i].skin); scene_data[id].meshes[i].skin);
#if 0
flecs::log::trace("created slot - ");
if (entity.get<ECS::CharacterSlot>()->instance.is_valid())
print_line("instance is valid 1");
else
print_line("instance is invalid 1");
#endif
} }
// print_line("character created"); // print_line("character created");
int xid = entities.size(); int xid = entities.size();
@@ -169,20 +149,6 @@ void Character::destroy_character(int xid)
pb->body = RID(); pb->body = RID();
entity.modified<ECS::RigidBody>(); entity.modified<ECS::RigidBody>();
} }
#if 0
entity.children([&](flecs::entity child) {
if (child.has<ECS::CharacterSlot>()) {
ECS::CharacterSlot *cs = child.get_mut<ECS::CharacterSlot>();
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<ECS::CShape>()) {
print_line("entity is CShape");
}
});
#endif
entity.destruct(); entity.destruct();
print_line("character destroyed"); print_line("character destroyed");
} }
@@ -230,22 +196,6 @@ void Character::set_scene(int id, const Ref<PackedScene> &scene)
md.mesh = mi->get_mesh(); md.mesh = mi->get_mesh();
md.offset = xform; md.offset = xform;
scene_data[id].meshes.push_back(md); scene_data[id].meshes.push_back(md);
#if 0
Ref<Skin> skin = mi->get_skin();
bool found = false;
for (Set<SkinReference *>::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<CollisionShape>(tmp); CollisionShape *cshape = Object::cast_to<CollisionShape>(tmp);
if (cshape) { if (cshape) {
@@ -319,7 +269,7 @@ void Character::init_bone()
ecs.component<ECS::BoneComponent>() ecs.component<ECS::BoneComponent>()
.on_add([](flecs::entity e, ECS::BoneComponent &s) .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(); flecs::entity parent = e.parent();
parent.add<ECS::Dirty>(); parent.add<ECS::Dirty>();
if (parent.has<ECS::Bound>()) { if (parent.has<ECS::Bound>()) {
@@ -346,7 +296,7 @@ void Character::init_bone()
skel->dirty = true; skel->dirty = true;
String out; String out;
VariantWriter::write_to_string(s.rest, 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<ECS::SkeletonOrderPrepared>(); parent.remove<ECS::SkeletonOrderPrepared>();
parent.add<ECS::Dirty>(); parent.add<ECS::Dirty>();
}); });
@@ -357,11 +307,13 @@ void Character::init_bone()
}) })
.on_set([](flecs::entity e, ECS::BonePose &s) .on_set([](flecs::entity e, ECS::BonePose &s)
{ {
#if 0
const Transform &xform = s.pose; const Transform &xform = s.pose;
String out; String out;
VariantWriter::write_to_string(xform, out); VariantWriter::write_to_string(xform, out);
flecs::log::trace("bone pose set %s: %d %s", flecs::log::trace("bone pose set %s: %d %s",
e.name().c_str(), s.index, out.ascii().get_data()); e.name().c_str(), s.index, out.ascii().get_data());
#endif
e.add<ECS::Dirty>(); e.add<ECS::Dirty>();
}); });
ecs.system<ECS::BonePose, ECS::Dirty>() ecs.system<ECS::BonePose, ECS::Dirty>()
@@ -381,13 +333,23 @@ void Character::init_bone()
skel->dirty = true; skel->dirty = true;
String out; String out;
VariantWriter::write_to_string(s.pose, 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<ECS::Skeleton>(); parent.modified<ECS::Skeleton>();
parent.add<ECS::Dirty>(); parent.add<ECS::Dirty>();
parent.remove<ECS::SkeletonTransformReady>(); parent.remove<ECS::SkeletonTransformReady>();
e.remove<ECS::Dirty>(); e.remove<ECS::Dirty>();
}); });
} }
void Character::animation_system_init()
{
ecs.component<ECS::AnimationPlayerData>();
ecs.system<ECS::AnimationPlayerData>("UpdateAnimation")
.kind(flecs::OnUpdate)
.each([](flecs::entity e, ECS::AnimationPlayerData &player)
{
ECS::advance(&player, SceneTree::get_singleton()->get_physics_process_time());
});
}
void Character::initialize() void Character::initialize()
{ {
Error err; Error err;
@@ -420,193 +382,6 @@ void Character::initialize()
if (err == OK && scene2.is_valid()) if (err == OK && scene2.is_valid())
set_scene(1, scene2); set_scene(1, scene2);
initialized = true; initialized = true;
#if 0
ecs.system<ECS::CharacterSlot>("BindSkin")
.without<ECS::Bound>()
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::CharacterSlot &slot)
{
flecs::entity parent = e.parent();
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
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<ECS::CharacterSlot>("BindSkeleton")
.without<ECS::Bound>()
.kind(flecs::PreUpdate)
.each([this](flecs::entity e, ECS::CharacterSlot &slot)
{
flecs::entity parent = e.parent();
if (!parent.get<ECS::SkeletonTransformReady>())
return;
if (parent.get<ECS::Bound>())
return;
flecs::log::trace("unbound skeleton at %s", e.name().c_str());
int i;
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
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<ECS::Bound>();
e.add<ECS::Bound>();
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<ECS::Skeleton>();
parent.add<ECS::Bound>();
assert(false);
});
ecs.system<ECS::Skeleton>("Check2")
.kind(flecs::PostUpdate)
.each([this](flecs::entity e, ECS::Skeleton &skel)
{
if (e.has<ECS::Dirty>())
flecs::log::trace("is dirty");
if (e.has<ECS::Bound>())
flecs::log::trace("is bound");
if (e.has<ECS::SkeletonTransformReady>())
flecs::log::trace("transform ready");
flecs::log::trace("end of frame systems");
});
#endif
#if 0
ecs.system<ECS::CharacterSlot>("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<ECS::Skeleton>();
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<ECS::Skeleton>();
}
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<ECS::Skeleton>("UpdateBone") ecs.system<ECS::Skeleton>("UpdateBone")
.with<ECS::Bound>() .with<ECS::Bound>()
.kind(flecs::OnUpdate) .kind(flecs::OnUpdate)
@@ -620,9 +395,10 @@ void Character::initialize()
float delta = SceneTree::get_singleton()->get_physics_process_time(); float delta = SceneTree::get_singleton()->get_physics_process_time();
struct ECS::BonePose *bp = root.get_mut<ECS::BonePose>(); struct ECS::BonePose *bp = root.get_mut<ECS::BonePose>();
// bp->pose.basis = bp->pose.basis.rotated(Vector3(0, 1, 0), 0.016f); // 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<ECS::BonePose>(); root.modified<ECS::BonePose>();
}); });
animation_system_init();
if (!Engine::get_singleton()->is_editor_hint()) { if (!Engine::get_singleton()->is_editor_hint()) {
err = st->connect("physics_frame", this, "_progress"); err = st->connect("physics_frame", this, "_progress");
assert(err == OK); assert(err == OK);

View File

@@ -142,6 +142,7 @@ protected:
void init_skeleton(); void init_skeleton();
void init_bone(); void init_bone();
void init_slot(); void init_slot();
void animation_system_init();
void initialize(); void initialize();
HashMap<int, flecs::entity_t> entities; HashMap<int, flecs::entity_t> entities;
bool initialized; bool initialized;

View File

@@ -79,6 +79,7 @@ void Character::init_skeleton()
if (pass_count >= len * len) if (pass_count >= len * len)
flecs::log::err("Skeleton parenthood graph is cyclic"); flecs::log::err("Skeleton parenthood graph is cyclic");
sk.process_order_dirty = false; sk.process_order_dirty = false;
flecs::log::trace("skeletons: %d dirty: sorting bones done", sk.bone_count);
} }
e.add<ECS::SkeletonOrderPrepared>(); e.add<ECS::SkeletonOrderPrepared>();
}); });
@@ -94,7 +95,7 @@ void Character::init_skeleton()
// flecs::log::trace("skeletons: %d dirty: update try", sk.bone_count); // flecs::log::trace("skeletons: %d dirty: update try", sk.bone_count);
if (sk.process_order_dirty) if (sk.process_order_dirty)
return; 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(); struct ECS::Skeleton::bone_data *bonesptr = sk.bones.ptrw();
int len = sk.bones.size(); int len = sk.bones.size();
const int *order = sk.process_order.ptr(); 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<ECS::SkeletonTransformReady>(); e.add<ECS::SkeletonTransformReady>();
assert(sk.skin_bone_indices_ptrs); assert(sk.skin_bone_indices_ptrs);
uint32_t bind_count = sk.skin->get_bind_count(); uint32_t bind_count = sk.skin->get_bind_count();
if (bind_count == 0)
return;
if (!e.has<ECS::Bound>())
return;
for (i = 0; i < (int)bind_count; i++) { for (i = 0; i < (int)bind_count; i++) {
uint32_t bone_index = sk.skin_bone_indices_ptrs[i]; uint32_t bone_index = sk.skin_bone_indices_ptrs[i];
Transform xform = bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i); Transform xform = bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i);
#if 0
if (i == 0) { if (i == 0) {
String out; String out;
VariantWriter::write_to_string(xform, out); VariantWriter::write_to_string(xform, out);
@@ -159,12 +165,14 @@ void Character::init_skeleton()
i, bonesptr[bone_index].name.ascii().get_data(), i, bonesptr[bone_index].name.ascii().get_data(),
out.ascii().get_data()); out.ascii().get_data());
} }
#endif
vs->skeleton_bone_set_transform(sk.skeleton, vs->skeleton_bone_set_transform(sk.skeleton,
i, bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i)); i, bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i));
} }
sk.dirty = false; sk.dirty = false;
e.remove<ECS::Dirty>(); e.remove<ECS::Dirty>();
}); });
#if 0
ecs.system<ECS::Skeleton>("c1") ecs.system<ECS::Skeleton>("c1")
.with<ECS::Bound>() .with<ECS::Bound>()
.kind(flecs::OnUpdate) .kind(flecs::OnUpdate)
@@ -193,6 +201,7 @@ void Character::init_skeleton()
{ {
flecs::log::trace("order prepared"); flecs::log::trace("order prepared");
}); });
#endif
#if 0 #if 0
ecs.system<ECS::Skeleton>("Check_") ecs.system<ECS::Skeleton>("Check_")
.kind(flecs::PreUpdate) .kind(flecs::PreUpdate)

View File

@@ -30,9 +30,9 @@ void Character::init_slot()
}) })
.on_set([st](flecs::entity e, ECS::CharacterSlot &s) .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()) 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<ECS::CharacterSlot>("UpdateSlot") ecs.system<ECS::CharacterSlot>("UpdateSlot")
.without<ECS::Bound>() .without<ECS::Bound>()
@@ -47,7 +47,7 @@ void Character::init_slot()
slot.instance = vs->instance_create(); slot.instance = vs->instance_create();
vs->instance_set_scenario(slot.instance, scenario); vs->instance_set_scenario(slot.instance, scenario);
vs->instance_set_base(slot.instance, slot.mesh->get_rid()); 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<ECS::CharacterSlot>("AttachToSkeleton") ecs.system<ECS::CharacterSlot>("AttachToSkeleton")
@@ -63,7 +63,7 @@ void Character::init_slot()
assert(slot.instance.is_valid()); assert(slot.instance.is_valid());
const ECS::Skeleton *skel = parent.get<ECS::Skeleton>(); const ECS::Skeleton *skel = parent.get<ECS::Skeleton>();
assert(skel->skeleton.is_valid()); 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()) if (skel->skeleton.is_valid())
slot.skeleton = skel->skeleton; slot.skeleton = skel->skeleton;
if (slot.skin.is_null() && !skel->skin.is_null()) if (slot.skin.is_null() && !skel->skin.is_null())
@@ -75,7 +75,7 @@ void Character::init_slot()
slot.bound = true; slot.bound = true;
e.add<ECS::Bound>(); e.add<ECS::Bound>();
parent.add<ECS::Bound>(); parent.add<ECS::Bound>();
flecs::log::trace("bound slot for %s", e.name().c_str()); // flecs::log::trace("bound slot for %s", e.name().c_str());
} }
}); });
ecs.system<ECS::CharacterSlot>("BindSlotsToSkeleton") ecs.system<ECS::CharacterSlot>("BindSlotsToSkeleton")
@@ -93,11 +93,11 @@ void Character::init_slot()
if (skel->skin.is_null() && slot.skin_internal.is_null()) { if (skel->skin.is_null() && slot.skin_internal.is_null()) {
const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr(); const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr();
const int *order = skel->process_order.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.instance();
slot.skin_internal->set_bind_count(skel->bones.size()); slot.skin_internal->set_bind_count(skel->bones.size());
int len = 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++) { for (i = 0; i < len; i++) {
const struct ECS::Skeleton::bone_data &b = bonesptr[order[i]]; const struct ECS::Skeleton::bone_data &b = bonesptr[order[i]];
if (b.parent >= 0) 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()); slot.skin_internal->set_bind_pose(i, slot.skin_internal->get_bind_pose(i).affine_inverse());
} }
skel->skin = slot.skin_internal; 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()); assert(skel->skeleton.is_valid());
skel->skin = slot.skin_internal; skel->skin = slot.skin_internal;
uint32_t bind_count = skel->skin->get_bind_count(); uint32_t bind_count = skel->skin->get_bind_count();