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_ */