libanim
changeset 0:fad4701f484e
libanim mercurial repo
author | John Tsiombikas <nuclear@mutantstargoat.com> |
---|---|
date | Sun, 08 Jan 2012 05:13:13 +0200 |
parents | |
children | 69654793abc3 |
files | .hgignore Makefile.in configure src/anim.c src/anim.h src/config.h src/dynarr.c src/dynarr.h src/track.c src/track.h |
diffstat | 10 files changed, 1224 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/.hgignore Sun Jan 08 05:13:13 2012 +0200 1.3 @@ -0,0 +1,7 @@ 1.4 +\.d$ 1.5 +\.o$ 1.6 +\.swp$ 1.7 +^libanim.a$ 1.8 +^libanim.so 1.9 +^libanim.dylib 1.10 +^Makefile$
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Makefile.in Sun Jan 08 05:13:13 2012 +0200 2.3 @@ -0,0 +1,63 @@ 2.4 +src = $(wildcard src/*.c) 2.5 +hdr = src/track.h src/anim.h src/config.h 2.6 +obj = $(src:.c=.o) 2.7 +dep = $(obj:.o=.d) 2.8 +lib_a = libanim.a 2.9 + 2.10 +ifeq ($(shell uname -s), Darwin) 2.11 + lib_so = anim.dylib 2.12 + shared = -dynamiclib 2.13 +else 2.14 + somajor = 0 2.15 + sominor = 1 2.16 + soname = libanim.so.$(somajor) 2.17 + lib_so = $(soname).$(sominor) 2.18 + solink = libanim.so 2.19 + shared = -shared -Wl,-soname,$(soname) 2.20 +endif 2.21 + 2.22 + 2.23 +CC = gcc 2.24 +AR = ar 2.25 +CFLAGS = $(opt) $(dbg) -pedantic -Wall -fPIC -I$(PREFIX)/include 2.26 +LDFLAGS = -L$(PREFIX)/lib -lvmath -lm -lpthread 2.27 + 2.28 +.PHONY: all 2.29 +all: $(lib_a) $(lib_so) 2.30 + 2.31 +$(lib_a): $(obj) 2.32 + $(AR) rcs $@ $(obj) 2.33 + 2.34 +$(lib_so): $(obj) 2.35 + $(CC) $(shared) -o $@ $(obj) $(LDFLAGS) 2.36 + 2.37 +-include $(dep) 2.38 + 2.39 +%.d: %.c 2.40 + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ 2.41 + 2.42 +.PHONY: install 2.43 +install: $(lib_a) $(lib_so) 2.44 + mkdir -p $(PREFIX)/lib $(PREFIX)/include/anim 2.45 + cp $(lib_a) $(PREFIX)/lib/$(lib_a) 2.46 + cp $(lib_so) $(PREFIX)/lib/$(lib_so) 2.47 + [ -n "$(solink)" ] && rm -f $(PREFIX)/lib/$(soname) $(PREFIX)/lib/$(solink) \ 2.48 + && ln -s $(PREFIX)/lib/$(lib_so) $(PREFIX)/lib/$(soname) \ 2.49 + && ln -s $(PREFIX)/lib/$(soname) $(PREFIX)/lib/$(solink) \ 2.50 + || true 2.51 + cp $(hdr) $(PREFIX)/include/anim/ 2.52 + 2.53 +.PHONY: uninstall 2.54 +uninstall: 2.55 + rm -f $(PREFIX)/lib/$(lib_a) 2.56 + rm -f $(PREFIX)/lib/$(lib_so) 2.57 + rm -f $(PREFIX)/include/anim/*.h 2.58 + rmdir $(PREFIX)/include/anim 2.59 + 2.60 +.PHONY: clean 2.61 +clean: 2.62 + rm -f $(obj) $(lib_so) $(lib_a) 2.63 + 2.64 +.PHONY: distclean 2.65 +distclean: clean 2.66 + rm -f Makefile
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/configure Sun Jan 08 05:13:13 2012 +0200 3.3 @@ -0,0 +1,85 @@ 3.4 +#!/bin/sh 3.5 + 3.6 +PREFIX=/usr/local 3.7 +OPT=yes 3.8 +DBG=yes 3.9 +PTHREAD=no 3.10 + 3.11 +config_h=src/config.h 3.12 + 3.13 +#echo "configuring libanim $VERSION ..." 3.14 + 3.15 +for arg; do 3.16 + case "$arg" in 3.17 + --prefix=*) 3.18 + value=`echo $arg | sed 's/--prefix=//'` 3.19 + PREFIX=${value:-$prefix} 3.20 + ;; 3.21 + 3.22 + --enable-opt) 3.23 + OPT=yes;; 3.24 + --disable-opt) 3.25 + OPT=no;; 3.26 + 3.27 + --enable-debug) 3.28 + DBG=yes;; 3.29 + --disable-debug) 3.30 + DBG=no;; 3.31 + 3.32 + --thread-safe) 3.33 + PTHREAD=yes;; 3.34 + --thread-unsafe) 3.35 + PTHREAD=no;; 3.36 + 3.37 + --help) 3.38 + echo 'usage: ./configure [options]' 3.39 + echo 'options:' 3.40 + echo ' --prefix=<path>: installation path (default: /usr/local)' 3.41 + echo ' --enable-opt: enable speed optimizations (default)' 3.42 + echo ' --disable-opt: disable speed optimizations' 3.43 + echo ' --enable-debug: include debugging symbols (default)' 3.44 + echo ' --disable-debug: do not include debugging symbols' 3.45 + echo ' --thread-safe: protect concurrent access to matrix cache' 3.46 + echo ' --thread-unsafe: assume only single-threaded operation (default)' 3.47 + echo 'all invalid options are silently ignored' 3.48 + exit 0 3.49 + ;; 3.50 + esac 3.51 +done 3.52 + 3.53 +echo "prefix: $PREFIX" 3.54 +echo "optimize for speed: $OPT" 3.55 +echo "include debugging symbols: $DBG" 3.56 + 3.57 +echo 'creating makefile ...' 3.58 +echo "PREFIX = $PREFIX" >Makefile 3.59 +if [ "$DBG" = 'yes' ]; then 3.60 + echo 'dbg = -g' >>Makefile 3.61 +fi 3.62 +if [ "$OPT" = 'yes' ]; then 3.63 + echo 'opt = -O3' >>Makefile 3.64 +fi 3.65 +if [ "$PTHREAD" = yes ]; then 3.66 + echo 'pthr = -lpthread' >>Makefile 3.67 +fi 3.68 + 3.69 +cat Makefile.in >>Makefile 3.70 + 3.71 +echo 'creating config.h ...' 3.72 +echo '#ifndef ANIM_CONFIG_H_' >src/config.h 3.73 +echo '#define ANIM_CONFIG_H_' >>src/config.h 3.74 +echo >>src/config.h 3.75 +if [ "$PTHREAD" = yes ]; then 3.76 + echo '#define ANIM_THREAD_SAFE' >>src/config.h 3.77 +else 3.78 + echo '#undef ANIM_THREAD_SAFE' >>src/config.h 3.79 +fi 3.80 +echo >>src/config.h 3.81 +echo '#endif /* ANIM_CONFIG_H_ */'>>src/config.h 3.82 + 3.83 +#echo 'creating pkg-config file ...' 3.84 +#echo "prefix=$PREFIX" >vmath.pc 3.85 +#echo "ver=$VERSION" >>vmath.pc 3.86 +#cat vmath.pc.in >>vmath.pc 3.87 + 3.88 +echo 'configuration completed, type make (or gmake) to build.'
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/anim.c Sun Jan 08 05:13:13 2012 +0200 4.3 @@ -0,0 +1,451 @@ 4.4 +#include <limits.h> 4.5 +#include <assert.h> 4.6 +#include "anim.h" 4.7 +#include "dynarr.h" 4.8 + 4.9 +static void invalidate_cache(struct anm_node *node); 4.10 + 4.11 +int anm_init_node(struct anm_node *node) 4.12 +{ 4.13 + int i, j; 4.14 + static const float defaults[] = { 4.15 + 0.0f, 0.0f, 0.0f, /* default position */ 4.16 + 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */ 4.17 + 1.0f, 1.0f, 1.0f /* default scale factor */ 4.18 + }; 4.19 + 4.20 + memset(node, 0, sizeof *node); 4.21 + 4.22 + /* initialize thread-local matrix cache */ 4.23 + pthread_key_create(&node->cache_key, 0); 4.24 + 4.25 + for(i=0; i<ANM_NUM_TRACKS; i++) { 4.26 + if(anm_init_track(node->tracks + i) == -1) { 4.27 + for(j=0; j<i; j++) { 4.28 + anm_destroy_track(node->tracks + i); 4.29 + } 4.30 + } 4.31 + anm_set_track_default(node->tracks + i, defaults[i]); 4.32 + } 4.33 + return 0; 4.34 +} 4.35 + 4.36 +void anm_destroy_node(struct anm_node *node) 4.37 +{ 4.38 + int i; 4.39 + free(node->name); 4.40 + 4.41 + for(i=0; i<ANM_NUM_TRACKS; i++) { 4.42 + anm_destroy_track(node->tracks + i); 4.43 + } 4.44 + 4.45 + /* destroy thread-specific cache */ 4.46 + pthread_key_delete(node->cache_key); 4.47 + 4.48 + while(node->cache_list) { 4.49 + struct mat_cache *tmp = node->cache_list; 4.50 + node->cache_list = tmp->next; 4.51 + free(tmp); 4.52 + } 4.53 +} 4.54 + 4.55 +void anm_destroy_node_tree(struct anm_node *tree) 4.56 +{ 4.57 + struct anm_node *c, *tmp; 4.58 + 4.59 + if(!tree) return; 4.60 + 4.61 + c = tree->child; 4.62 + while(c) { 4.63 + tmp = c; 4.64 + c = c->next; 4.65 + 4.66 + anm_destroy_node_tree(tmp); 4.67 + } 4.68 + anm_destroy_node(tree); 4.69 +} 4.70 + 4.71 +struct anm_node *anm_create_node(void) 4.72 +{ 4.73 + struct anm_node *n; 4.74 + 4.75 + if((n = malloc(sizeof *n))) { 4.76 + if(anm_init_node(n) == -1) { 4.77 + free(n); 4.78 + return 0; 4.79 + } 4.80 + } 4.81 + return n; 4.82 +} 4.83 + 4.84 +void anm_free_node(struct anm_node *node) 4.85 +{ 4.86 + anm_destroy_node(node); 4.87 + free(node); 4.88 +} 4.89 + 4.90 +void anm_free_node_tree(struct anm_node *tree) 4.91 +{ 4.92 + struct anm_node *c, *tmp; 4.93 + 4.94 + if(!tree) return; 4.95 + 4.96 + c = tree->child; 4.97 + while(c) { 4.98 + tmp = c; 4.99 + c = c->next; 4.100 + 4.101 + anm_free_node_tree(tmp); 4.102 + } 4.103 + 4.104 + anm_free_node(tree); 4.105 +} 4.106 + 4.107 +int anm_set_node_name(struct anm_node *node, const char *name) 4.108 +{ 4.109 + char *str; 4.110 + 4.111 + if(!(str = malloc(strlen(name) + 1))) { 4.112 + return -1; 4.113 + } 4.114 + strcpy(str, name); 4.115 + free(node->name); 4.116 + node->name = str; 4.117 + return 0; 4.118 +} 4.119 + 4.120 +const char *anm_get_node_name(struct anm_node *node) 4.121 +{ 4.122 + return node->name ? node->name : ""; 4.123 +} 4.124 + 4.125 +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) 4.126 +{ 4.127 + int i; 4.128 + 4.129 + for(i=0; i<ANM_NUM_TRACKS; i++) { 4.130 + anm_set_track_interpolator(node->tracks + i, in); 4.131 + } 4.132 + invalidate_cache(node); 4.133 +} 4.134 + 4.135 +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) 4.136 +{ 4.137 + int i; 4.138 + 4.139 + for(i=0; i<ANM_NUM_TRACKS; i++) { 4.140 + anm_set_track_extrapolator(node->tracks + i, ex); 4.141 + } 4.142 + invalidate_cache(node); 4.143 +} 4.144 + 4.145 +void anm_link_node(struct anm_node *p, struct anm_node *c) 4.146 +{ 4.147 + c->next = p->child; 4.148 + p->child = c; 4.149 + 4.150 + c->parent = p; 4.151 + invalidate_cache(c); 4.152 +} 4.153 + 4.154 +int anm_unlink_node(struct anm_node *p, struct anm_node *c) 4.155 +{ 4.156 + struct anm_node *iter; 4.157 + 4.158 + if(p->child == c) { 4.159 + p->child = c->next; 4.160 + c->next = 0; 4.161 + invalidate_cache(c); 4.162 + return 0; 4.163 + } 4.164 + 4.165 + iter = p->child; 4.166 + while(iter->next) { 4.167 + if(iter->next == c) { 4.168 + iter->next = c->next; 4.169 + c->next = 0; 4.170 + invalidate_cache(c); 4.171 + return 0; 4.172 + } 4.173 + } 4.174 + return -1; 4.175 +} 4.176 + 4.177 +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) 4.178 +{ 4.179 + anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x); 4.180 + anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y); 4.181 + anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z); 4.182 + invalidate_cache(node); 4.183 +} 4.184 + 4.185 +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) 4.186 +{ 4.187 + vec3_t v; 4.188 + v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm); 4.189 + v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm); 4.190 + v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm); 4.191 + return v; 4.192 +} 4.193 + 4.194 +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) 4.195 +{ 4.196 + anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x); 4.197 + anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y); 4.198 + anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z); 4.199 + anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w); 4.200 + invalidate_cache(node); 4.201 +} 4.202 + 4.203 +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) 4.204 +{ 4.205 + int idx0, idx1, last_idx; 4.206 + anm_time_t tstart, tend; 4.207 + float t, dt; 4.208 + struct anm_track *track_x, *track_y, *track_z, *track_w; 4.209 + quat_t q, q1, q2; 4.210 + 4.211 + track_x = node->tracks + ANM_TRACK_ROT_X; 4.212 + track_y = node->tracks + ANM_TRACK_ROT_Y; 4.213 + track_z = node->tracks + ANM_TRACK_ROT_Z; 4.214 + track_w = node->tracks + ANM_TRACK_ROT_W; 4.215 + 4.216 + if(!track_x->count) { 4.217 + q.x = track_x->def_val; 4.218 + q.y = track_y->def_val; 4.219 + q.z = track_z->def_val; 4.220 + q.w = track_w->def_val; 4.221 + return q; 4.222 + } 4.223 + 4.224 + last_idx = track_x->count - 1; 4.225 + 4.226 + tstart = track_x->keys[0].time; 4.227 + tend = track_x->keys[last_idx].time; 4.228 + tm = anm_remap_time(track_x, tm, tstart, tend); 4.229 + 4.230 + idx0 = anm_get_key_interval(track_x, tm); 4.231 + assert(idx0 >= 0 && idx0 < track_x->count); 4.232 + idx1 = idx0 + 1; 4.233 + 4.234 + dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time); 4.235 + t = (float)(tm - track_x->keys[idx0].time) / dt; 4.236 + 4.237 + q1.x = track_x->keys[idx0].val; 4.238 + q1.y = track_y->keys[idx0].val; 4.239 + q1.z = track_z->keys[idx0].val; 4.240 + q1.w = track_w->keys[idx0].val; 4.241 + 4.242 + q2.x = track_x->keys[idx1].val; 4.243 + q2.y = track_y->keys[idx1].val; 4.244 + q2.z = track_z->keys[idx1].val; 4.245 + q2.w = track_w->keys[idx1].val; 4.246 + 4.247 + return quat_slerp(q1, q2, t); 4.248 +} 4.249 + 4.250 +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) 4.251 +{ 4.252 + anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x); 4.253 + anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y); 4.254 + anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z); 4.255 + invalidate_cache(node); 4.256 +} 4.257 + 4.258 +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) 4.259 +{ 4.260 + vec3_t v; 4.261 + v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm); 4.262 + v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm); 4.263 + v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm); 4.264 + return v; 4.265 +} 4.266 + 4.267 + 4.268 +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm) 4.269 +{ 4.270 + mat4_t xform; 4.271 + vec3_t pos = {0.0, 0.0, 0.0}; 4.272 + 4.273 + if(!node->parent) { 4.274 + return anm_get_node_position(node, tm); 4.275 + } 4.276 + 4.277 + anm_get_matrix(node, xform, tm); 4.278 + return v3_transform(pos, xform); 4.279 +} 4.280 + 4.281 +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm) 4.282 +{ 4.283 + quat_t rot, prot; 4.284 + rot = anm_get_node_rotation(node, tm); 4.285 + 4.286 + if(!node->parent) { 4.287 + return rot; 4.288 + } 4.289 + 4.290 + prot = anm_get_rotation(node->parent, tm); 4.291 + return quat_mul(prot, rot); 4.292 +} 4.293 + 4.294 +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm) 4.295 +{ 4.296 + vec3_t s, ps; 4.297 + s = anm_get_node_scaling(node, tm); 4.298 + 4.299 + if(!node->parent) { 4.300 + return s; 4.301 + } 4.302 + 4.303 + ps = anm_get_scaling(node->parent, tm); 4.304 + return v3_mul(s, ps); 4.305 +} 4.306 + 4.307 +void anm_set_pivot(struct anm_node *node, vec3_t piv) 4.308 +{ 4.309 + node->pivot = piv; 4.310 +} 4.311 + 4.312 +vec3_t anm_get_pivot(struct anm_node *node) 4.313 +{ 4.314 + return node->pivot; 4.315 +} 4.316 + 4.317 +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) 4.318 +{ 4.319 + struct mat_cache *cache = pthread_getspecific(node->cache_key); 4.320 + if(!cache) { 4.321 + cache = malloc(sizeof *cache); 4.322 + assert(cache); 4.323 + 4.324 + pthread_mutex_lock(&node->cache_list_lock); 4.325 + cache->next = node->cache_list; 4.326 + node->cache_list = cache; 4.327 + pthread_mutex_unlock(&node->cache_list_lock); 4.328 + 4.329 + cache->time = ANM_TIME_INVAL; 4.330 + pthread_setspecific(node->cache_key, cache); 4.331 + } 4.332 + 4.333 + if(cache->time != tm) { 4.334 + mat4_t tmat, rmat, smat, pivmat, neg_pivmat; 4.335 + vec3_t pos, scale; 4.336 + quat_t rot; 4.337 + 4.338 + m4_identity(tmat); 4.339 + /*no need to m4_identity(rmat); quat_to_mat4 sets this properly */ 4.340 + m4_identity(smat); 4.341 + m4_identity(pivmat); 4.342 + m4_identity(neg_pivmat); 4.343 + 4.344 + pos = anm_get_node_position(node, tm); 4.345 + rot = anm_get_node_rotation(node, tm); 4.346 + scale = anm_get_node_scaling(node, tm); 4.347 + 4.348 + m4_translate(pivmat, node->pivot.x, node->pivot.y, node->pivot.z); 4.349 + m4_translate(neg_pivmat, -node->pivot.x, -node->pivot.y, -node->pivot.z); 4.350 + 4.351 + m4_translate(tmat, pos.x, pos.y, pos.z); 4.352 + quat_to_mat4(rmat, rot); 4.353 + m4_translate(smat, scale.x, scale.y, scale.z); 4.354 + 4.355 + /* ok this would look nicer in C++ */ 4.356 + m4_mult(cache->matrix, pivmat, tmat); 4.357 + m4_mult(cache->matrix, cache->matrix, rmat); 4.358 + m4_mult(cache->matrix, cache->matrix, smat); 4.359 + m4_mult(cache->matrix, cache->matrix, neg_pivmat); 4.360 + 4.361 + if(node->parent) { 4.362 + mat4_t parent_mat; 4.363 + 4.364 + anm_get_matrix(node->parent, mat, tm); 4.365 + m4_mult(cache->matrix, parent_mat, cache->matrix); 4.366 + } 4.367 + cache->time = tm; 4.368 + } 4.369 + m4_copy(mat, cache->matrix); 4.370 +} 4.371 + 4.372 +void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) 4.373 +{ 4.374 + struct mat_cache *cache = pthread_getspecific(node->cache_key); 4.375 + if(!cache) { 4.376 + cache = malloc(sizeof *cache); 4.377 + assert(cache); 4.378 + 4.379 + pthread_mutex_lock(&node->cache_list_lock); 4.380 + cache->next = node->cache_list; 4.381 + node->cache_list = cache; 4.382 + pthread_mutex_unlock(&node->cache_list_lock); 4.383 + 4.384 + cache->inv_time = ANM_TIME_INVAL; 4.385 + pthread_setspecific(node->cache_key, cache); 4.386 + } 4.387 + 4.388 + if(cache->inv_time != tm) { 4.389 + anm_get_matrix(node, mat, tm); 4.390 + m4_inverse(cache->inv_matrix, mat); 4.391 + cache->inv_time = tm; 4.392 + } 4.393 + m4_copy(mat, cache->inv_matrix); 4.394 +} 4.395 + 4.396 +anm_time_t anm_get_start_time(struct anm_node *node) 4.397 +{ 4.398 + int i; 4.399 + struct anm_node *c; 4.400 + anm_time_t res = LONG_MAX; 4.401 + 4.402 + for(i=0; i<ANM_NUM_TRACKS; i++) { 4.403 + if(node->tracks[i].count) { 4.404 + anm_time_t tm = node->tracks[i].keys[0].time; 4.405 + if(tm < res) { 4.406 + res = tm; 4.407 + } 4.408 + } 4.409 + } 4.410 + 4.411 + c = node->child; 4.412 + while(c) { 4.413 + anm_time_t tm = anm_get_start_time(c); 4.414 + if(tm < res) { 4.415 + res = tm; 4.416 + } 4.417 + c = c->next; 4.418 + } 4.419 + return res; 4.420 +} 4.421 + 4.422 +anm_time_t anm_get_end_time(struct anm_node *node) 4.423 +{ 4.424 + int i; 4.425 + struct anm_node *c; 4.426 + anm_time_t res = LONG_MIN; 4.427 + 4.428 + for(i=0; i<ANM_NUM_TRACKS; i++) { 4.429 + if(node->tracks[i].count) { 4.430 + anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time; 4.431 + if(tm > res) { 4.432 + res = tm; 4.433 + } 4.434 + } 4.435 + } 4.436 + 4.437 + c = node->child; 4.438 + while(c) { 4.439 + anm_time_t tm = anm_get_end_time(c); 4.440 + if(tm > res) { 4.441 + res = tm; 4.442 + } 4.443 + c = c->next; 4.444 + } 4.445 + return res; 4.446 +} 4.447 + 4.448 +static void invalidate_cache(struct anm_node *node) 4.449 +{ 4.450 + struct mat_cache *cache = pthread_getspecific(node->cache_key); 4.451 + if(cache) { 4.452 + cache->time = ANM_TIME_INVAL; 4.453 + } 4.454 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/anim.h Sun Jan 08 05:13:13 2012 +0200 5.3 @@ -0,0 +1,107 @@ 5.4 +#ifndef LIBANIM_H_ 5.5 +#define LIBANIM_H_ 5.6 + 5.7 +#include "config.h" 5.8 + 5.9 +#include <pthread.h> 5.10 + 5.11 +#include <vmath/vmath.h> 5.12 +#include "track.h" 5.13 + 5.14 +enum { 5.15 + ANM_TRACK_POS_X, 5.16 + ANM_TRACK_POS_Y, 5.17 + ANM_TRACK_POS_Z, 5.18 + 5.19 + ANM_TRACK_ROT_X, 5.20 + ANM_TRACK_ROT_Y, 5.21 + ANM_TRACK_ROT_Z, 5.22 + ANM_TRACK_ROT_W, 5.23 + 5.24 + ANM_TRACK_SCL_X, 5.25 + ANM_TRACK_SCL_Y, 5.26 + ANM_TRACK_SCL_Z, 5.27 + 5.28 + ANM_NUM_TRACKS 5.29 +}; 5.30 + 5.31 +struct anm_node { 5.32 + char *name; 5.33 + 5.34 + struct anm_track tracks[ANM_NUM_TRACKS]; 5.35 + vec3_t pivot; 5.36 + 5.37 + /* matrix cache */ 5.38 + struct mat_cache { 5.39 + mat4_t matrix, inv_matrix; 5.40 + anm_time_t time, inv_time; 5.41 + struct mat_cache *next; 5.42 + } *cache_list; 5.43 + pthread_key_t cache_key; 5.44 + pthread_mutex_t cache_list_lock; 5.45 + 5.46 + struct anm_node *parent; 5.47 + struct anm_node *child; 5.48 + struct anm_node *next; 5.49 +}; 5.50 + 5.51 +#ifdef __cplusplus 5.52 +extern "C" { 5.53 +#endif 5.54 + 5.55 +/* node constructor and destructor */ 5.56 +int anm_init_node(struct anm_node *node); 5.57 +void anm_destroy_node(struct anm_node *node); 5.58 + 5.59 +/* recursively destroy an animation node tree */ 5.60 +void anm_destroy_node_tree(struct anm_node *tree); 5.61 + 5.62 +/* helper functions to allocate/construct and destroy/free with 5.63 + * a single call. They call anm_init_node and anm_destroy_node 5.64 + * internally. 5.65 + */ 5.66 +struct anm_node *anm_create_node(void); 5.67 +void anm_free_node(struct anm_node *node); 5.68 + 5.69 +/* recursively destroy and free the nodes of a node tree */ 5.70 +void anm_free_node_tree(struct anm_node *tree); 5.71 + 5.72 +int anm_set_node_name(struct anm_node *node, const char *name); 5.73 +const char *anm_get_node_name(struct anm_node *node); 5.74 + 5.75 +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in); 5.76 +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex); 5.77 + 5.78 +/* link and unlink nodes with parent/child relations */ 5.79 +void anm_link_node(struct anm_node *parent, struct anm_node *child); 5.80 +int anm_unlink_node(struct anm_node *parent, struct anm_node *child); 5.81 + 5.82 +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm); 5.83 +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm); 5.84 + 5.85 +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm); 5.86 +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm); 5.87 + 5.88 +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm); 5.89 +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm); 5.90 + 5.91 +/* these three return the full p/r/s taking hierarchy into account */ 5.92 +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm); 5.93 +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm); 5.94 +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm); 5.95 + 5.96 +void anm_set_pivot(struct anm_node *node, vec3_t pivot); 5.97 +vec3_t anm_get_pivot(struct anm_node *node); 5.98 + 5.99 +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); 5.100 +void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); 5.101 + 5.102 +/* those return the start and end times of the whole tree */ 5.103 +anm_time_t anm_get_start_time(struct anm_node *node); 5.104 +anm_time_t anm_get_end_time(struct anm_node *node); 5.105 + 5.106 +#ifdef __cplusplus 5.107 +} 5.108 +#endif 5.109 + 5.110 +#endif /* LIBANIM_H_ */
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/config.h Sun Jan 08 05:13:13 2012 +0200 6.3 @@ -0,0 +1,6 @@ 6.4 +#ifndef ANIM_CONFIG_H_ 6.5 +#define ANIM_CONFIG_H_ 6.6 + 6.7 +#undef ANIM_THREAD_SAFE 6.8 + 6.9 +#endif /* ANIM_CONFIG_H_ */
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/dynarr.c Sun Jan 08 05:13:13 2012 +0200 7.3 @@ -0,0 +1,122 @@ 7.4 +#include <stdio.h> 7.5 +#include <stdlib.h> 7.6 +#include <string.h> 7.7 +#include "dynarr.h" 7.8 + 7.9 +/* The array descriptor keeps auxilliary information needed to manipulate 7.10 + * the dynamic array. It's allocated adjacent to the array buffer. 7.11 + */ 7.12 +struct arrdesc { 7.13 + int nelem, szelem; 7.14 + int max_elem; 7.15 + int bufsz; /* not including the descriptor */ 7.16 +}; 7.17 + 7.18 +#define DESC(x) ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc))) 7.19 + 7.20 +void *dynarr_alloc(int elem, int szelem) 7.21 +{ 7.22 + struct arrdesc *desc; 7.23 + 7.24 + if(!(desc = malloc(elem * szelem + sizeof *desc))) { 7.25 + return 0; 7.26 + } 7.27 + desc->nelem = desc->max_elem = elem; 7.28 + desc->szelem = szelem; 7.29 + desc->bufsz = elem * szelem; 7.30 + return (char*)desc + sizeof *desc; 7.31 +} 7.32 + 7.33 +void dynarr_free(void *da) 7.34 +{ 7.35 + if(da) { 7.36 + free(DESC(da)); 7.37 + } 7.38 +} 7.39 + 7.40 +void *dynarr_resize(void *da, int elem) 7.41 +{ 7.42 + int newsz; 7.43 + void *tmp; 7.44 + struct arrdesc *desc; 7.45 + 7.46 + if(!da) return 0; 7.47 + desc = DESC(da); 7.48 + 7.49 + newsz = desc->szelem * elem; 7.50 + 7.51 + if(!(tmp = realloc(desc, newsz + sizeof *desc))) { 7.52 + return 0; 7.53 + } 7.54 + desc = tmp; 7.55 + 7.56 + desc->nelem = desc->max_elem = elem; 7.57 + desc->bufsz = newsz; 7.58 + return (char*)desc + sizeof *desc; 7.59 +} 7.60 + 7.61 +int dynarr_empty(void *da) 7.62 +{ 7.63 + return DESC(da)->nelem ? 0 : 1; 7.64 +} 7.65 + 7.66 +int dynarr_size(void *da) 7.67 +{ 7.68 + return DESC(da)->nelem; 7.69 +} 7.70 + 7.71 + 7.72 +/* stack semantics */ 7.73 +void *dynarr_push(void *da, void *item) 7.74 +{ 7.75 + struct arrdesc *desc; 7.76 + int nelem; 7.77 + 7.78 + desc = DESC(da); 7.79 + nelem = desc->nelem; 7.80 + 7.81 + if(nelem >= desc->max_elem) { 7.82 + /* need to resize */ 7.83 + struct arrdesc *tmp; 7.84 + int newsz = desc->max_elem ? desc->max_elem * 2 : 1; 7.85 + 7.86 + if(!(tmp = dynarr_resize(da, newsz))) { 7.87 + fprintf(stderr, "failed to resize\n"); 7.88 + return da; 7.89 + } 7.90 + da = tmp; 7.91 + desc = DESC(da); 7.92 + desc->nelem = nelem; 7.93 + } 7.94 + 7.95 + memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem); 7.96 + return da; 7.97 +} 7.98 + 7.99 +void *dynarr_pop(void *da) 7.100 +{ 7.101 + struct arrdesc *desc; 7.102 + int nelem; 7.103 + 7.104 + desc = DESC(da); 7.105 + nelem = desc->nelem; 7.106 + 7.107 + if(!nelem) return da; 7.108 + 7.109 + if(nelem <= desc->max_elem / 3) { 7.110 + /* reclaim space */ 7.111 + struct arrdesc *tmp; 7.112 + int newsz = desc->max_elem / 2; 7.113 + 7.114 + if(!(tmp = dynarr_resize(da, newsz))) { 7.115 + fprintf(stderr, "failed to resize\n"); 7.116 + return da; 7.117 + } 7.118 + da = tmp; 7.119 + desc = DESC(da); 7.120 + desc->nelem = nelem; 7.121 + } 7.122 + desc->nelem--; 7.123 + 7.124 + return da; 7.125 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/src/dynarr.h Sun Jan 08 05:13:13 2012 +0200 8.3 @@ -0,0 +1,16 @@ 8.4 +#ifndef DYNARR_H_ 8.5 +#define DYNARR_H_ 8.6 + 8.7 +void *dynarr_alloc(int elem, int szelem); 8.8 +void dynarr_free(void *da); 8.9 +void *dynarr_resize(void *da, int elem); 8.10 + 8.11 +int dynarr_empty(void *da); 8.12 +int dynarr_size(void *da); 8.13 + 8.14 +/* stack semantics */ 8.15 +void *dynarr_push(void *da, void *item); 8.16 +void *dynarr_pop(void *da); 8.17 + 8.18 + 8.19 +#endif /* DYNARR_H_ */
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/track.c Sun Jan 08 05:13:13 2012 +0200 9.3 @@ -0,0 +1,272 @@ 9.4 +#include <stdlib.h> 9.5 +#include <string.h> 9.6 +#include <assert.h> 9.7 +#include "track.h" 9.8 +#include "dynarr.h" 9.9 + 9.10 +static int keycmp(const void *a, const void *b); 9.11 +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm); 9.12 + 9.13 +static float interp_step(float v0, float v1, float v2, float v3, float t); 9.14 +static float interp_linear(float v0, float v1, float v2, float v3, float t); 9.15 +static float interp_cubic(float v0, float v1, float v2, float v3, float t); 9.16 + 9.17 +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end); 9.18 +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end); 9.19 +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end); 9.20 + 9.21 +/* XXX keep this in sync with enum anm_interpolator at track.h */ 9.22 +static float (*interp[])(float, float, float, float, float) = { 9.23 + interp_step, 9.24 + interp_linear, 9.25 + interp_cubic, 9.26 + 0 9.27 +}; 9.28 + 9.29 +/* XXX keep this in sync with enum anm_extrapolator at track.h */ 9.30 +static anm_time_t (*remap_time[])(anm_time_t, anm_time_t, anm_time_t) = { 9.31 + remap_extend, 9.32 + remap_clamp, 9.33 + remap_repeat, 9.34 + 0 9.35 +}; 9.36 + 9.37 +int anm_init_track(struct anm_track *track) 9.38 +{ 9.39 + memset(track, 0, sizeof *track); 9.40 + 9.41 + if(!(track->keys = dynarr_alloc(0, sizeof *track->keys))) { 9.42 + return -1; 9.43 + } 9.44 + track->interp = ANM_INTERP_LINEAR; 9.45 + track->extrap = ANM_EXTRAP_CLAMP; 9.46 + return 0; 9.47 +} 9.48 + 9.49 +void anm_destroy_track(struct anm_track *track) 9.50 +{ 9.51 + dynarr_free(track->keys); 9.52 +} 9.53 + 9.54 +struct anm_track *anm_create_track(void) 9.55 +{ 9.56 + struct anm_track *track; 9.57 + 9.58 + if((track = malloc(sizeof *track))) { 9.59 + if(anm_init_track(track) == -1) { 9.60 + free(track); 9.61 + return 0; 9.62 + } 9.63 + } 9.64 + return track; 9.65 +} 9.66 + 9.67 +void anm_free_track(struct anm_track *track) 9.68 +{ 9.69 + anm_destroy_track(track); 9.70 + free(track); 9.71 +} 9.72 + 9.73 +int anm_set_track_name(struct anm_track *track, const char *name) 9.74 +{ 9.75 + char *tmp; 9.76 + 9.77 + if(!(tmp = malloc(strlen(name) + 1))) { 9.78 + return -1; 9.79 + } 9.80 + free(track->name); 9.81 + track->name = tmp; 9.82 + return 0; 9.83 +} 9.84 + 9.85 +const char *anm_get_track_name(struct anm_track *track) 9.86 +{ 9.87 + return track->name; 9.88 +} 9.89 + 9.90 +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in) 9.91 +{ 9.92 + track->interp = in; 9.93 +} 9.94 + 9.95 +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex) 9.96 +{ 9.97 + track->extrap = ex; 9.98 +} 9.99 + 9.100 +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end) 9.101 +{ 9.102 + return remap_time[track->extrap](tm, start, end); 9.103 +} 9.104 + 9.105 +void anm_set_track_default(struct anm_track *track, float def) 9.106 +{ 9.107 + track->def_val = def; 9.108 +} 9.109 + 9.110 +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key) 9.111 +{ 9.112 + int idx = anm_get_key_interval(track, key->time); 9.113 + 9.114 + /* if we got a valid keyframe index, compare them... */ 9.115 + if(idx >= 0 && idx < track->count && keycmp(key, track->keys + idx) == 0) { 9.116 + /* ... it's the same key, just update the value */ 9.117 + track->keys[idx].val = key->val; 9.118 + } else { 9.119 + /* ... it's a new key, add it and re-sort them */ 9.120 + void *tmp; 9.121 + if(!(tmp = dynarr_push(track->keys, key))) { 9.122 + return -1; 9.123 + } 9.124 + track->keys = tmp; 9.125 + /* TODO lazy qsort */ 9.126 + qsort(track->keys, ++track->count, sizeof *track->keys, keycmp); 9.127 + } 9.128 + return 0; 9.129 +} 9.130 + 9.131 +static int keycmp(const void *a, const void *b) 9.132 +{ 9.133 + return ((struct anm_keyframe*)a)->time - ((struct anm_keyframe*)b)->time; 9.134 +} 9.135 + 9.136 +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx) 9.137 +{ 9.138 + if(idx < 0 || idx >= track->count) { 9.139 + return 0; 9.140 + } 9.141 + return track->keys + idx; 9.142 +} 9.143 + 9.144 +int anm_get_key_interval(struct anm_track *track, anm_time_t tm) 9.145 +{ 9.146 + int last; 9.147 + 9.148 + if(!track->count || tm < track->keys[0].time) { 9.149 + return -1; 9.150 + } 9.151 + 9.152 + last = track->count - 1; 9.153 + if(tm > track->keys[last].time) { 9.154 + return last; 9.155 + } 9.156 + 9.157 + return find_prev_key(track->keys, 0, last, tm); 9.158 +} 9.159 + 9.160 +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm) 9.161 +{ 9.162 + int mid; 9.163 + 9.164 + if(end - start <= 1) { 9.165 + return start; 9.166 + } 9.167 + 9.168 + mid = (start + end) / 2; 9.169 + if(tm < arr[mid].time) { 9.170 + return find_prev_key(arr, start, mid, tm); 9.171 + } 9.172 + if(tm > arr[mid].time) { 9.173 + return find_prev_key(arr, mid, end, tm); 9.174 + } 9.175 + return mid; 9.176 +} 9.177 + 9.178 +int anm_set_value(struct anm_track *track, anm_time_t tm, float val) 9.179 +{ 9.180 + struct anm_keyframe key; 9.181 + key.time = tm; 9.182 + key.val = val; 9.183 + 9.184 + return anm_set_keyframe(track, &key); 9.185 +} 9.186 + 9.187 +float anm_get_value(struct anm_track *track, anm_time_t tm) 9.188 +{ 9.189 + int idx0, idx1, last_idx; 9.190 + anm_time_t tstart, tend; 9.191 + float t, dt; 9.192 + float v0, v1, v2, v3; 9.193 + 9.194 + if(!track->count) { 9.195 + return track->def_val; 9.196 + } 9.197 + 9.198 + last_idx = track->count - 1; 9.199 + 9.200 + tstart = track->keys[0].time; 9.201 + tend = track->keys[last_idx].time; 9.202 + 9.203 + if(tstart == tend) { 9.204 + return track->keys[0].val; 9.205 + } 9.206 + 9.207 + tm = remap_time[track->extrap](tm, tstart, tend); 9.208 + 9.209 + idx0 = anm_get_key_interval(track, tm); 9.210 + assert(idx0 >= 0 && idx0 < track->count); 9.211 + idx1 = idx0 + 1; 9.212 + 9.213 + if(idx0 == last_idx) { 9.214 + return track->keys[idx0].val; 9.215 + } 9.216 + 9.217 + dt = (float)(track->keys[idx1].time - track->keys[idx0].time); 9.218 + t = (float)(tm - track->keys[idx0].time) / dt; 9.219 + 9.220 + v1 = track->keys[idx0].val; 9.221 + v2 = track->keys[idx1].val; 9.222 + 9.223 + /* get the neigboring values to allow for cubic interpolation */ 9.224 + v0 = idx0 > 0 ? track->keys[idx0 - 1].val : v1; 9.225 + v3 = idx1 < last_idx ? track->keys[idx1 + 1].val : v2; 9.226 + 9.227 + return interp[track->interp](v0, v1, v2, v3, t); 9.228 +} 9.229 + 9.230 + 9.231 +static float interp_step(float v0, float v1, float v2, float v3, float t) 9.232 +{ 9.233 + return v1; 9.234 +} 9.235 + 9.236 +static float interp_linear(float v0, float v1, float v2, float v3, float t) 9.237 +{ 9.238 + return v1 + (v2 - v1) * t; 9.239 +} 9.240 + 9.241 +static float interp_cubic(float a, float b, float c, float d, float t) 9.242 +{ 9.243 + float x, y, z, w; 9.244 + float tsq = t * t; 9.245 + 9.246 + x = -a + 3.0 * b - 3.0 * c + d; 9.247 + y = 2.0 * a - 5.0 * b + 4.0 * c - d; 9.248 + z = c - a; 9.249 + w = 2.0 * b; 9.250 + 9.251 + return 0.5 * (x * tsq * t + y * tsq + z * t + w); 9.252 +} 9.253 + 9.254 +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end) 9.255 +{ 9.256 + return remap_repeat(tm, start, end); 9.257 +} 9.258 + 9.259 +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end) 9.260 +{ 9.261 + return tm < start ? start : (tm >= end ? end - 1 : tm); 9.262 +} 9.263 + 9.264 +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end) 9.265 +{ 9.266 + anm_time_t interv = end - start; 9.267 + 9.268 + if(tm < start) { 9.269 + while(tm < start) { 9.270 + tm += interv; 9.271 + } 9.272 + return tm; 9.273 + } 9.274 + return (tm - start) % interv + start; 9.275 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/src/track.h Sun Jan 08 05:13:13 2012 +0200 10.3 @@ -0,0 +1,95 @@ 10.4 +/* An animation track defines the values of a single scalar over time 10.5 + * and supports various interpolation and extrapolation modes. 10.6 + */ 10.7 +#ifndef LIBANIM_TRACK_H_ 10.8 +#define LIBANIM_TRACK_H_ 10.9 + 10.10 +#include <limits.h> 10.11 +#include "config.h" 10.12 + 10.13 +enum anm_interpolator { 10.14 + ANM_INTERP_STEP, 10.15 + ANM_INTERP_LINEAR, 10.16 + ANM_INTERP_CUBIC 10.17 +}; 10.18 + 10.19 +enum anm_extrapolator { 10.20 + ANM_EXTRAP_EXTEND, /* extend to infinity */ 10.21 + ANM_EXTRAP_CLAMP, /* clamp to last value */ 10.22 + ANM_EXTRAP_REPEAT /* repeat motion */ 10.23 +}; 10.24 + 10.25 +typedef long anm_time_t; 10.26 +#define ANM_TIME_INVAL LONG_MIN 10.27 + 10.28 +#define ANM_SEC2TM(x) ((anm_time_t)((x) * 1000)) 10.29 +#define ANM_MSEC2TM(x) ((anm_time_t)(x)) 10.30 +#define ANM_TM2SEC(x) ((x) / 1000.0) 10.31 +#define ANM_TM2MSEC(x) (x) 10.32 + 10.33 +struct anm_keyframe { 10.34 + anm_time_t time; 10.35 + float val; 10.36 +}; 10.37 + 10.38 +struct anm_track { 10.39 + char *name; 10.40 + int count; 10.41 + struct anm_keyframe *keys; 10.42 + 10.43 + float def_val; 10.44 + 10.45 + enum anm_interpolator interp; 10.46 + enum anm_extrapolator extrap; 10.47 +}; 10.48 + 10.49 +#ifdef __cplusplus 10.50 +extern "C" { 10.51 +#endif 10.52 + 10.53 +/* track constructor and destructor */ 10.54 +int anm_init_track(struct anm_track *track); 10.55 +void anm_destroy_track(struct anm_track *track); 10.56 + 10.57 +/* helper functions that use anm_init_track and anm_destroy_track internally */ 10.58 +struct anm_track *anm_create_track(void); 10.59 +void anm_free_track(struct anm_track *track); 10.60 + 10.61 +int anm_set_track_name(struct anm_track *track, const char *name); 10.62 +const char *anm_get_track_name(struct anm_track *track); 10.63 + 10.64 +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in); 10.65 +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex); 10.66 + 10.67 +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end); 10.68 + 10.69 +void anm_set_track_default(struct anm_track *track, float def); 10.70 + 10.71 +/* set or update a keyframe */ 10.72 +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key); 10.73 + 10.74 +/* get the idx-th keyframe, returns null if it doesn't exist */ 10.75 +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx); 10.76 + 10.77 +/* Finds the 0-based index of the intra-keyframe interval which corresponds 10.78 + * to the specified time. If the time falls exactly onto the N-th keyframe 10.79 + * the function returns N. 10.80 + * 10.81 + * Special cases: 10.82 + * - if the time is before the first keyframe -1 is returned. 10.83 + * - if the time is after the last keyframe, the index of the last keyframe 10.84 + * is returned. 10.85 + */ 10.86 +int anm_get_key_interval(struct anm_track *track, anm_time_t tm); 10.87 + 10.88 +int anm_set_value(struct anm_track *track, anm_time_t tm, float val); 10.89 + 10.90 +/* evaluates and returns the value of the track for a particular time */ 10.91 +float anm_get_value(struct anm_track *track, anm_time_t tm); 10.92 + 10.93 +#ifdef __cplusplus 10.94 +} 10.95 +#endif 10.96 + 10.97 + 10.98 +#endif /* LIBANIM_TRACK_H_ */