libanim

changeset 24:09e267e7ed4a

implemented high-level animation blending interface
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 30 Dec 2013 15:20:31 +0200
parents 203c11299586
children ebead35c9eb1
files example/test.c src/anim.c src/anim.h
diffstat 3 files changed, 141 insertions(+), 47 deletions(-) [+]
line diff
     1.1 --- a/example/test.c	Fri Dec 27 11:29:42 2013 +0200
     1.2 +++ b/example/test.c	Mon Dec 30 15:20:31 2013 +0200
     1.3 @@ -23,7 +23,7 @@
     1.4  	{{0, -2.1, 0},		{0, 1, 0},		{0.8, 2, 0.8},	{1, 0.5, 0.5}},	/* left-lower leg */
     1.5  	{{0, -2.1, 0},		{0, 1, 0},		{0.8, 2, 0.8},	{0.5, 1, 0.5}},	/* right-lower leg */
     1.6  
     1.7 -	{{0, 2.6, 0},		{0, -0.5, 0},	{1.2, 1.2, 1.2},{0, 1, 1}},	/* head */
     1.8 +	{{0, 2.6, 0},		{0, -0.5, 0},	{1.2, 1.2, 1.2},{0, 1, 1}},		/* head */
     1.9  
    1.10  	{{-1.3, 0.4, 0},		{0, 1, 0},		{0.8, 2, 0.8},	{0, 0, 1}},	/* left-upper arm */
    1.11  	{{1.3, 0.4, 0},		{0, 1, 0},		{0.8, 2, 0.8},	{1, 1, 0}},		/* right-upper arm */
    1.12 @@ -253,29 +253,6 @@
    1.13  	glRotatef(cam_phi, 1, 0, 0);
    1.14  	glRotatef(cam_theta, 0, 1, 0);
    1.15  
    1.16 -	/* animation blending if we're in transition */
    1.17 -	if(cur_anim != next_anim) {
    1.18 -		float t = (msec - trans_start_tm) / 1500.0;
    1.19 -
    1.20 -		struct anm_animation *from, *to;
    1.21 -		from = anm_get_animation(root, cur_anim);
    1.22 -		to = anm_get_animation(root, next_anim);
    1.23 -
    1.24 -		if(t >= 1.0) {
    1.25 -			t = 1.0;
    1.26 -			cur_anim = next_anim;
    1.27 -			anm_use_animation(root, cur_anim);
    1.28 -		} else {
    1.29 -			anm_use_animations(root, cur_anim, next_anim, t);
    1.30 -		}
    1.31 -
    1.32 -		printf("transitioning from \"%s\" to \"%s\": %.2f    \r", from->name, to->name, t);
    1.33 -		if(cur_anim == next_anim) {
    1.34 -			putchar('\n');
    1.35 -		}
    1.36 -		fflush(stdout);
    1.37 -	}
    1.38 -
    1.39  	/* first render a character with bottom-up lazy matrix calculation */
    1.40  	glPushMatrix();
    1.41  	glTranslatef(-2.5, 0, 0);
    1.42 @@ -358,8 +335,10 @@
    1.43  		exit(0);
    1.44  
    1.45  	case ' ':
    1.46 +		cur_anim = anm_get_active_animation_index(root, 0);
    1.47  		next_anim = (cur_anim + 1) % 2;
    1.48  		trans_start_tm = glutGet(GLUT_ELAPSED_TIME);
    1.49 +		anm_transition(root, next_anim, trans_start_tm, 1500);
    1.50  		break;
    1.51  	}
    1.52  }
     2.1 --- a/src/anim.c	Fri Dec 27 11:29:42 2013 +0200
     2.2 +++ b/src/anim.c	Mon Dec 30 15:20:31 2013 +0200
     2.3 @@ -57,7 +57,6 @@
     2.4  	memset(node, 0, sizeof *node);
     2.5  
     2.6  	node->cur_anim[1] = -1;
     2.7 -	node->cur_mix = 0;
     2.8  
     2.9  	if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) {
    2.10  		return -1;
    2.11 @@ -222,6 +221,7 @@
    2.12  	node->cur_anim[0] = aidx;
    2.13  	node->cur_anim[1] = -1;
    2.14  	node->cur_mix = 0;
    2.15 +	node->blend_dur = -1;
    2.16  
    2.17  	invalidate_cache(node);
    2.18  	return 0;
    2.19 @@ -288,13 +288,40 @@
    2.20  
    2.21  }
    2.22  
    2.23 -int anm_get_active_animation_index(struct anm_node *node, int which)
    2.24 +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which)
    2.25 +{
    2.26 +	if(which < 0 || which >= 2) {
    2.27 +		return;
    2.28 +	}
    2.29 +	node->cur_anim_offset[which] = offs;
    2.30 +}
    2.31 +
    2.32 +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which)
    2.33 +{
    2.34 +	if(which < 0 || which >= 2) {
    2.35 +		return 0;
    2.36 +	}
    2.37 +	return node->cur_anim_offset[which];
    2.38 +}
    2.39 +
    2.40 +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which)
    2.41 +{
    2.42 +	struct anm_node *c = node->child;
    2.43 +	while(c) {
    2.44 +		anm_set_animation_offset(c, offs, which);
    2.45 +		c = c->next;
    2.46 +	}
    2.47 +
    2.48 +	anm_set_node_animation_offset(node, offs, which);
    2.49 +}
    2.50 +
    2.51 +int anm_get_active_animation_index(const struct anm_node *node, int which)
    2.52  {
    2.53  	if(which < 0 || which >= 2) return -1;
    2.54  	return node->cur_anim[which];
    2.55  }
    2.56  
    2.57 -struct anm_animation *anm_get_active_animation(struct anm_node *node, int which)
    2.58 +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which)
    2.59  {
    2.60  	int idx = anm_get_active_animation_index(node, which);
    2.61  	if(idx < 0 || idx >= anm_get_animation_count(node)) {
    2.62 @@ -303,12 +330,12 @@
    2.63  	return node->animations + idx;
    2.64  }
    2.65  
    2.66 -float anm_get_active_animation_mix(struct anm_node *node)
    2.67 +float anm_get_active_animation_mix(const struct anm_node *node)
    2.68  {
    2.69  	return node->cur_mix;
    2.70  }
    2.71  
    2.72 -int anm_get_animation_count(struct anm_node *node)
    2.73 +int anm_get_animation_count(const struct anm_node *node)
    2.74  {
    2.75  	return dynarr_size(node->animations);
    2.76  }
    2.77 @@ -445,6 +472,50 @@
    2.78  	return 0;
    2.79  }
    2.80  
    2.81 +/* ---- high level animation blending ---- */
    2.82 +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur)
    2.83 +{
    2.84 +	struct anm_node *c = node->child;
    2.85 +	while(c) {
    2.86 +		anm_transition(c, anmidx, start, dur);
    2.87 +		c = c->next;
    2.88 +	}
    2.89 +
    2.90 +	anm_node_transition(node, anmidx, start, dur);
    2.91 +}
    2.92 +
    2.93 +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur)
    2.94 +{
    2.95 +	node->cur_anim[1] = anmidx;
    2.96 +	node->cur_anim_offset[1] = start;
    2.97 +	node->blend_dur = dur;
    2.98 +}
    2.99 +
   2.100 +
   2.101 +#define BLEND_START_TM	node->cur_anim_offset[1]
   2.102 +
   2.103 +static anm_time_t animation_time(struct anm_node *node, anm_time_t tm, int which)
   2.104 +{
   2.105 +	float t;
   2.106 +
   2.107 +	if(node->blend_dur >= 0) {
   2.108 +		/* we're in transition... */
   2.109 +		t = (float)(tm - BLEND_START_TM) / (float)node->blend_dur;
   2.110 +		if(t < 0.0) t = 0.0;
   2.111 +
   2.112 +		node->cur_mix = t;
   2.113 +
   2.114 +		if(t > 1.0) {
   2.115 +			/* switch completely over to the target animation and stop blending */
   2.116 +			anm_use_node_animation(node, node->cur_anim[1]);
   2.117 +			node->cur_anim_offset[0] = node->cur_anim_offset[1];
   2.118 +		}
   2.119 +	}
   2.120 +
   2.121 +	return tm - node->cur_anim_offset[which];
   2.122 +}
   2.123 +
   2.124 +
   2.125  void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
   2.126  {
   2.127  	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.128 @@ -456,9 +527,11 @@
   2.129  	invalidate_cache(node);
   2.130  }
   2.131  
   2.132 +
   2.133  vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
   2.134  {
   2.135  	vec3_t v;
   2.136 +	anm_time_t tm0 = animation_time(node, tm, 0);
   2.137  	struct anm_animation *anim0 = anm_get_active_animation(node, 0);
   2.138  	struct anm_animation *anim1 = anm_get_active_animation(node, 1);
   2.139  
   2.140 @@ -466,15 +539,16 @@
   2.141  		return v3_cons(0, 0, 0);
   2.142  	}
   2.143  
   2.144 -	v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm);
   2.145 -	v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm);
   2.146 -	v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm);
   2.147 +	v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm0);
   2.148 +	v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm0);
   2.149 +	v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm0);
   2.150  
   2.151  	if(anim1) {
   2.152  		vec3_t v1;
   2.153 -		v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm);
   2.154 -		v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm);
   2.155 -		v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm);
   2.156 +		anm_time_t tm1 = animation_time(node, tm, 1);
   2.157 +		v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm1);
   2.158 +		v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm1);
   2.159 +		v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm1);
   2.160  
   2.161  		v.x = v.x + (v1.x - v.x) * node->cur_mix;
   2.162  		v.y = v.y + (v1.y - v.y) * node->cur_mix;
   2.163 @@ -575,6 +649,7 @@
   2.164  quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
   2.165  {
   2.166  	quat_t q;
   2.167 +	anm_time_t tm0 = animation_time(node, tm, 0);
   2.168  	struct anm_animation *anim0 = anm_get_active_animation(node, 0);
   2.169  	struct anm_animation *anim1 = anm_get_active_animation(node, 1);
   2.170  
   2.171 @@ -582,10 +657,11 @@
   2.172  		return quat_identity();
   2.173  	}
   2.174  
   2.175 -	q = get_node_rotation(node, tm, anim0);
   2.176 +	q = get_node_rotation(node, tm0, anim0);
   2.177  
   2.178  	if(anim1) {
   2.179 -		quat_t q1 = get_node_rotation(node, tm, anim1);
   2.180 +		anm_time_t tm1 = animation_time(node, tm, 1);
   2.181 +		quat_t q1 = get_node_rotation(node, tm1, anim1);
   2.182  
   2.183  		q = quat_slerp(q, q1, node->cur_mix);
   2.184  	}
   2.185 @@ -606,6 +682,7 @@
   2.186  vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
   2.187  {
   2.188  	vec3_t v;
   2.189 +	anm_time_t tm0 = animation_time(node, tm, 0);
   2.190  	struct anm_animation *anim0 = anm_get_active_animation(node, 0);
   2.191  	struct anm_animation *anim1 = anm_get_active_animation(node, 1);
   2.192  
   2.193 @@ -613,15 +690,16 @@
   2.194  		return v3_cons(1, 1, 1);
   2.195  	}
   2.196  
   2.197 -	v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm);
   2.198 -	v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm);
   2.199 -	v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm);
   2.200 +	v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm0);
   2.201 +	v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm0);
   2.202 +	v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm0);
   2.203  
   2.204  	if(anim1) {
   2.205  		vec3_t v1;
   2.206 -		v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm);
   2.207 -		v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm);
   2.208 -		v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm);
   2.209 +		anm_time_t tm1 = animation_time(node, tm, 1);
   2.210 +		v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm1);
   2.211 +		v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm1);
   2.212 +		v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm1);
   2.213  
   2.214  		v.x = v.x + (v1.x - v.x) * node->cur_mix;
   2.215  		v.y = v.y + (v1.y - v.y) * node->cur_mix;
     3.1 --- a/src/anim.h	Fri Dec 27 11:29:42 2013 +0200
     3.2 +++ b/src/anim.h	Mon Dec 30 15:20:31 2013 +0200
     3.3 @@ -36,8 +36,12 @@
     3.4  	char *name;
     3.5  
     3.6  	int cur_anim[2];
     3.7 +	anm_time_t cur_anim_offset[2];
     3.8  	float cur_mix;
     3.9  
    3.10 +	/* high-level animation blending transition duration */
    3.11 +	anm_time_t blend_dur;
    3.12 +
    3.13  	struct anm_animation *animations;
    3.14  	vec3_t pivot;
    3.15  
    3.16 @@ -68,6 +72,8 @@
    3.17  void anm_set_animation_name(struct anm_animation *anim, const char *name);
    3.18  
    3.19  
    3.20 +/* ---- node/hierarchy management ---- */
    3.21 +
    3.22  /* node constructor and destructor */
    3.23  int anm_init_node(struct anm_node *node);
    3.24  void anm_destroy_node(struct anm_node *node);
    3.25 @@ -95,6 +101,8 @@
    3.26  void anm_set_pivot(struct anm_node *node, vec3_t pivot);
    3.27  vec3_t anm_get_pivot(struct anm_node *node);
    3.28  
    3.29 +/* ---- multiple animations and animation blending ---- */
    3.30 +
    3.31  /* set active animation(s) */
    3.32  int anm_use_node_animation(struct anm_node *node, int aidx);
    3.33  int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t);
    3.34 @@ -102,13 +110,19 @@
    3.35  int anm_use_animation(struct anm_node *node, int aidx);
    3.36  int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t);
    3.37  
    3.38 +/* set/get current animation offset(s) */
    3.39 +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which);
    3.40 +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which);
    3.41 +/* recursive variant */
    3.42 +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which);
    3.43 +
    3.44  /* returns the requested current animation index, which can be 0 or 1 */
    3.45 -int anm_get_active_animation_index(struct anm_node *node, int which);
    3.46 +int anm_get_active_animation_index(const struct anm_node *node, int which);
    3.47  /* returns the requested current animation, which can be 0 or 1 */
    3.48 -struct anm_animation *anm_get_active_animation(struct anm_node *node, int which);
    3.49 -float anm_get_active_animation_mix(struct anm_node *node);
    3.50 +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which);
    3.51 +float anm_get_active_animation_mix(const struct anm_node *node);
    3.52  
    3.53 -int anm_get_animation_count(struct anm_node *node);
    3.54 +int anm_get_animation_count(const struct anm_node *node);
    3.55  
    3.56  /* add/remove an animation to the specified node */
    3.57  int anm_add_node_animation(struct anm_node *node);
    3.58 @@ -136,6 +150,26 @@
    3.59  /* get the name of the currently active animation of this node */
    3.60  const char *anm_get_active_animation_name(struct anm_node *node);
    3.61  
    3.62 +
    3.63 +/* ---- high level animation blending interface ---- */
    3.64 +/* XXX this convenience interface assumes monotonically increasing time values
    3.65 + *     in all subsequent calls to anm_get_* and anm_eval_* functions.
    3.66 + *
    3.67 + * anmidx: index of the animation to transition to
    3.68 + * start: when to start the transition
    3.69 + * dur: transition duration
    3.70 + *
    3.71 + * sets up a transition from the current animation (cur_anim[0]) to another animation.
    3.72 + * at time start + dur, the transition will be completed, cur_anim[0] will be the new
    3.73 + * animation and cur_anim_offset[0] will be equal to start.
    3.74 + */
    3.75 +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur);
    3.76 +/* non-recursive variant, acts on a single node (you probably DON'T want to use this) */
    3.77 +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur);
    3.78 +
    3.79 +
    3.80 +/* ---- keyframes / PRS interpolation ---- */
    3.81 +
    3.82  void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm);
    3.83  vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm);
    3.84  
    3.85 @@ -154,6 +188,9 @@
    3.86  anm_time_t anm_get_start_time(struct anm_node *node);
    3.87  anm_time_t anm_get_end_time(struct anm_node *node);
    3.88  
    3.89 +
    3.90 +/* ---- transformation matrices ---- */
    3.91 +
    3.92  /* these calculate the matrix and inverse matrix of this node alone */
    3.93  void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);
    3.94  void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);