Added animation system
This commit is contained in:
971
src/modules/character/animation_system.cpp
Normal file
971
src/modules/character/animation_system.cpp
Normal 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);
|
||||
}
|
||||
251
src/modules/character/animation_system.h
Normal file
251
src/modules/character/animation_system.h
Normal 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
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <scene/main/viewport.h>
|
||||
#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> shape)
|
||||
@@ -68,10 +59,6 @@ ECS::CharacterSlot::CharacterSlot(Ref<ArrayMesh> mesh, Ref<Skin> 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<ECS::CharacterSlot>(scene_data[id].meshes[i].mesh,
|
||||
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");
|
||||
int xid = entities.size();
|
||||
@@ -169,20 +149,6 @@ void Character::destroy_character(int xid)
|
||||
pb->body = RID();
|
||||
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();
|
||||
print_line("character destroyed");
|
||||
}
|
||||
@@ -230,22 +196,6 @@ void Character::set_scene(int id, const Ref<PackedScene> &scene)
|
||||
md.mesh = mi->get_mesh();
|
||||
md.offset = xform;
|
||||
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);
|
||||
if (cshape) {
|
||||
@@ -319,7 +269,7 @@ void Character::init_bone()
|
||||
ecs.component<ECS::BoneComponent>()
|
||||
.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<ECS::Dirty>();
|
||||
if (parent.has<ECS::Bound>()) {
|
||||
@@ -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<ECS::SkeletonOrderPrepared>();
|
||||
parent.add<ECS::Dirty>();
|
||||
});
|
||||
@@ -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::Dirty>();
|
||||
});
|
||||
ecs.system<ECS::BonePose, ECS::Dirty>()
|
||||
@@ -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<ECS::Skeleton>();
|
||||
parent.add<ECS::Dirty>();
|
||||
parent.remove<ECS::SkeletonTransformReady>();
|
||||
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()
|
||||
{
|
||||
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<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")
|
||||
.with<ECS::Bound>()
|
||||
.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<ECS::BonePose>();
|
||||
// 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>();
|
||||
});
|
||||
animation_system_init();
|
||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
||||
err = st->connect("physics_frame", this, "_progress");
|
||||
assert(err == OK);
|
||||
|
||||
@@ -142,6 +142,7 @@ protected:
|
||||
void init_skeleton();
|
||||
void init_bone();
|
||||
void init_slot();
|
||||
void animation_system_init();
|
||||
void initialize();
|
||||
HashMap<int, flecs::entity_t> entities;
|
||||
bool initialized;
|
||||
|
||||
@@ -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<ECS::SkeletonOrderPrepared>();
|
||||
});
|
||||
@@ -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<ECS::SkeletonTransformReady>();
|
||||
assert(sk.skin_bone_indices_ptrs);
|
||||
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++) {
|
||||
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<ECS::Dirty>();
|
||||
});
|
||||
#if 0
|
||||
ecs.system<ECS::Skeleton>("c1")
|
||||
.with<ECS::Bound>()
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -193,6 +201,7 @@ void Character::init_skeleton()
|
||||
{
|
||||
flecs::log::trace("order prepared");
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<ECS::Skeleton>("Check_")
|
||||
.kind(flecs::PreUpdate)
|
||||
|
||||
@@ -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<ECS::CharacterSlot>("UpdateSlot")
|
||||
.without<ECS::Bound>()
|
||||
@@ -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<ECS::CharacterSlot>("AttachToSkeleton")
|
||||
@@ -63,7 +63,7 @@ void Character::init_slot()
|
||||
assert(slot.instance.is_valid());
|
||||
const ECS::Skeleton *skel = parent.get<ECS::Skeleton>();
|
||||
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<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")
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user