# HG changeset patch # User John Tsiombikas # Date 1388409631 -7200 # Node ID 09e267e7ed4af17098c4f7faba21dc4bf5b1e8e9 # Parent 203c11299586e7d97070250c41da2a9c0fe61013 implemented high-level animation blending interface diff -r 203c11299586 -r 09e267e7ed4a example/test.c --- a/example/test.c Fri Dec 27 11:29:42 2013 +0200 +++ b/example/test.c Mon Dec 30 15:20:31 2013 +0200 @@ -23,7 +23,7 @@ {{0, -2.1, 0}, {0, 1, 0}, {0.8, 2, 0.8}, {1, 0.5, 0.5}}, /* left-lower leg */ {{0, -2.1, 0}, {0, 1, 0}, {0.8, 2, 0.8}, {0.5, 1, 0.5}}, /* right-lower leg */ - {{0, 2.6, 0}, {0, -0.5, 0}, {1.2, 1.2, 1.2},{0, 1, 1}}, /* head */ + {{0, 2.6, 0}, {0, -0.5, 0}, {1.2, 1.2, 1.2},{0, 1, 1}}, /* head */ {{-1.3, 0.4, 0}, {0, 1, 0}, {0.8, 2, 0.8}, {0, 0, 1}}, /* left-upper arm */ {{1.3, 0.4, 0}, {0, 1, 0}, {0.8, 2, 0.8}, {1, 1, 0}}, /* right-upper arm */ @@ -253,29 +253,6 @@ glRotatef(cam_phi, 1, 0, 0); glRotatef(cam_theta, 0, 1, 0); - /* animation blending if we're in transition */ - if(cur_anim != next_anim) { - float t = (msec - trans_start_tm) / 1500.0; - - struct anm_animation *from, *to; - from = anm_get_animation(root, cur_anim); - to = anm_get_animation(root, next_anim); - - if(t >= 1.0) { - t = 1.0; - cur_anim = next_anim; - anm_use_animation(root, cur_anim); - } else { - anm_use_animations(root, cur_anim, next_anim, t); - } - - printf("transitioning from \"%s\" to \"%s\": %.2f \r", from->name, to->name, t); - if(cur_anim == next_anim) { - putchar('\n'); - } - fflush(stdout); - } - /* first render a character with bottom-up lazy matrix calculation */ glPushMatrix(); glTranslatef(-2.5, 0, 0); @@ -358,8 +335,10 @@ exit(0); case ' ': + cur_anim = anm_get_active_animation_index(root, 0); next_anim = (cur_anim + 1) % 2; trans_start_tm = glutGet(GLUT_ELAPSED_TIME); + anm_transition(root, next_anim, trans_start_tm, 1500); break; } } diff -r 203c11299586 -r 09e267e7ed4a src/anim.c --- a/src/anim.c Fri Dec 27 11:29:42 2013 +0200 +++ b/src/anim.c Mon Dec 30 15:20:31 2013 +0200 @@ -57,7 +57,6 @@ memset(node, 0, sizeof *node); node->cur_anim[1] = -1; - node->cur_mix = 0; if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) { return -1; @@ -222,6 +221,7 @@ node->cur_anim[0] = aidx; node->cur_anim[1] = -1; node->cur_mix = 0; + node->blend_dur = -1; invalidate_cache(node); return 0; @@ -288,13 +288,40 @@ } -int anm_get_active_animation_index(struct anm_node *node, int which) +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which) +{ + if(which < 0 || which >= 2) { + return; + } + node->cur_anim_offset[which] = offs; +} + +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which) +{ + if(which < 0 || which >= 2) { + return 0; + } + return node->cur_anim_offset[which]; +} + +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which) +{ + struct anm_node *c = node->child; + while(c) { + anm_set_animation_offset(c, offs, which); + c = c->next; + } + + anm_set_node_animation_offset(node, offs, which); +} + +int anm_get_active_animation_index(const struct anm_node *node, int which) { if(which < 0 || which >= 2) return -1; return node->cur_anim[which]; } -struct anm_animation *anm_get_active_animation(struct anm_node *node, int which) +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which) { int idx = anm_get_active_animation_index(node, which); if(idx < 0 || idx >= anm_get_animation_count(node)) { @@ -303,12 +330,12 @@ return node->animations + idx; } -float anm_get_active_animation_mix(struct anm_node *node) +float anm_get_active_animation_mix(const struct anm_node *node) { return node->cur_mix; } -int anm_get_animation_count(struct anm_node *node) +int anm_get_animation_count(const struct anm_node *node) { return dynarr_size(node->animations); } @@ -445,6 +472,50 @@ return 0; } +/* ---- high level animation blending ---- */ +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) +{ + struct anm_node *c = node->child; + while(c) { + anm_transition(c, anmidx, start, dur); + c = c->next; + } + + anm_node_transition(node, anmidx, start, dur); +} + +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur) +{ + node->cur_anim[1] = anmidx; + node->cur_anim_offset[1] = start; + node->blend_dur = dur; +} + + +#define BLEND_START_TM node->cur_anim_offset[1] + +static anm_time_t animation_time(struct anm_node *node, anm_time_t tm, int which) +{ + float t; + + if(node->blend_dur >= 0) { + /* we're in transition... */ + t = (float)(tm - BLEND_START_TM) / (float)node->blend_dur; + if(t < 0.0) t = 0.0; + + node->cur_mix = t; + + if(t > 1.0) { + /* switch completely over to the target animation and stop blending */ + anm_use_node_animation(node, node->cur_anim[1]); + node->cur_anim_offset[0] = node->cur_anim_offset[1]; + } + } + + return tm - node->cur_anim_offset[which]; +} + + void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) { struct anm_animation *anim = anm_get_active_animation(node, 0); @@ -456,9 +527,11 @@ invalidate_cache(node); } + vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) { vec3_t v; + anm_time_t tm0 = animation_time(node, tm, 0); struct anm_animation *anim0 = anm_get_active_animation(node, 0); struct anm_animation *anim1 = anm_get_active_animation(node, 1); @@ -466,15 +539,16 @@ return v3_cons(0, 0, 0); } - v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm); - v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm); - v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm); + v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm0); + v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm0); + v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm0); if(anim1) { vec3_t v1; - v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm); - v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm); - v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm); + anm_time_t tm1 = animation_time(node, tm, 1); + v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm1); + v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm1); + v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm1); v.x = v.x + (v1.x - v.x) * node->cur_mix; v.y = v.y + (v1.y - v.y) * node->cur_mix; @@ -575,6 +649,7 @@ quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) { quat_t q; + anm_time_t tm0 = animation_time(node, tm, 0); struct anm_animation *anim0 = anm_get_active_animation(node, 0); struct anm_animation *anim1 = anm_get_active_animation(node, 1); @@ -582,10 +657,11 @@ return quat_identity(); } - q = get_node_rotation(node, tm, anim0); + q = get_node_rotation(node, tm0, anim0); if(anim1) { - quat_t q1 = get_node_rotation(node, tm, anim1); + anm_time_t tm1 = animation_time(node, tm, 1); + quat_t q1 = get_node_rotation(node, tm1, anim1); q = quat_slerp(q, q1, node->cur_mix); } @@ -606,6 +682,7 @@ vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) { vec3_t v; + anm_time_t tm0 = animation_time(node, tm, 0); struct anm_animation *anim0 = anm_get_active_animation(node, 0); struct anm_animation *anim1 = anm_get_active_animation(node, 1); @@ -613,15 +690,16 @@ return v3_cons(1, 1, 1); } - v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm); - v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm); - v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm); + v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm0); + v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm0); + v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm0); if(anim1) { vec3_t v1; - v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm); - v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm); - v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm); + anm_time_t tm1 = animation_time(node, tm, 1); + v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm1); + v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm1); + v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm1); v.x = v.x + (v1.x - v.x) * node->cur_mix; v.y = v.y + (v1.y - v.y) * node->cur_mix; diff -r 203c11299586 -r 09e267e7ed4a src/anim.h --- a/src/anim.h Fri Dec 27 11:29:42 2013 +0200 +++ b/src/anim.h Mon Dec 30 15:20:31 2013 +0200 @@ -36,8 +36,12 @@ char *name; int cur_anim[2]; + anm_time_t cur_anim_offset[2]; float cur_mix; + /* high-level animation blending transition duration */ + anm_time_t blend_dur; + struct anm_animation *animations; vec3_t pivot; @@ -68,6 +72,8 @@ void anm_set_animation_name(struct anm_animation *anim, const char *name); +/* ---- node/hierarchy management ---- */ + /* node constructor and destructor */ int anm_init_node(struct anm_node *node); void anm_destroy_node(struct anm_node *node); @@ -95,6 +101,8 @@ void anm_set_pivot(struct anm_node *node, vec3_t pivot); vec3_t anm_get_pivot(struct anm_node *node); +/* ---- multiple animations and animation blending ---- */ + /* set active animation(s) */ int anm_use_node_animation(struct anm_node *node, int aidx); int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t); @@ -102,13 +110,19 @@ int anm_use_animation(struct anm_node *node, int aidx); int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t); +/* set/get current animation offset(s) */ +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which); +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which); +/* recursive variant */ +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which); + /* returns the requested current animation index, which can be 0 or 1 */ -int anm_get_active_animation_index(struct anm_node *node, int which); +int anm_get_active_animation_index(const struct anm_node *node, int which); /* returns the requested current animation, which can be 0 or 1 */ -struct anm_animation *anm_get_active_animation(struct anm_node *node, int which); -float anm_get_active_animation_mix(struct anm_node *node); +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which); +float anm_get_active_animation_mix(const struct anm_node *node); -int anm_get_animation_count(struct anm_node *node); +int anm_get_animation_count(const struct anm_node *node); /* add/remove an animation to the specified node */ int anm_add_node_animation(struct anm_node *node); @@ -136,6 +150,26 @@ /* get the name of the currently active animation of this node */ const char *anm_get_active_animation_name(struct anm_node *node); + +/* ---- high level animation blending interface ---- */ +/* XXX this convenience interface assumes monotonically increasing time values + * in all subsequent calls to anm_get_* and anm_eval_* functions. + * + * anmidx: index of the animation to transition to + * start: when to start the transition + * dur: transition duration + * + * sets up a transition from the current animation (cur_anim[0]) to another animation. + * at time start + dur, the transition will be completed, cur_anim[0] will be the new + * animation and cur_anim_offset[0] will be equal to start. + */ +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur); +/* non-recursive variant, acts on a single node (you probably DON'T want to use this) */ +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur); + + +/* ---- keyframes / PRS interpolation ---- */ + void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm); vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm); @@ -154,6 +188,9 @@ anm_time_t anm_get_start_time(struct anm_node *node); anm_time_t anm_get_end_time(struct anm_node *node); + +/* ---- transformation matrices ---- */ + /* these calculate the matrix and inverse matrix of this node alone */ void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);