goat3d

changeset 52:cb5414f406eb

merged
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 17 Jan 2014 18:27:47 +0200
parents fa5c52ea9d59 0be413ac2e0a
children 6d514398a728
files src/goat3d.cc src/goat3d_readxml.cc src/goat3d_write.cc
diffstat 10 files changed, 729 insertions(+), 110 deletions(-) [+]
line diff
     1.1 --- a/Makefile	Fri Jan 17 18:16:09 2014 +0200
     1.2 +++ b/Makefile	Fri Jan 17 18:27:47 2014 +0200
     1.3 @@ -37,7 +37,7 @@
     1.4  CC = clang
     1.5  CXX = clang++
     1.6  CXXFLAGS = -pedantic -Wall $(dbg) $(opt) $(pic) $(extinc)
     1.7 -LDFLAGS = $(extlibs)
     1.8 +LDFLAGS = $(extlibs) -lpthread
     1.9  
    1.10  .PHONY: all
    1.11  all: $(lib_so) $(lib_a)
    1.12 @@ -48,15 +48,19 @@
    1.13  $(lib_a): $(obj) $(extlibs)
    1.14  	$(AR) rcs $@ $(obj) $(extlibs)
    1.15  
    1.16 +.PHONY: $(openctm)
    1.17  $(openctm):
    1.18  	$(MAKE) -C libs/openctm
    1.19  
    1.20 +.PHONY: $(tinyxml2)
    1.21  $(tinyxml2):
    1.22  	$(MAKE) -C libs/tinyxml2
    1.23  
    1.24 +.PHONY: $(vmath)
    1.25  $(vmath):
    1.26  	$(MAKE) -C libs/vmath
    1.27  
    1.28 +.PHONY: $(anim)
    1.29  $(anim):
    1.30  	$(MAKE) -C libs/anim
    1.31  
     2.1 --- a/libs/anim/anim.c	Fri Jan 17 18:16:09 2014 +0200
     2.2 +++ b/libs/anim/anim.c	Fri Jan 17 18:27:47 2014 +0200
     2.3 @@ -8,7 +8,7 @@
     2.4  
     2.5  static void invalidate_cache(struct anm_node *node);
     2.6  
     2.7 -int anm_init_node(struct anm_node *node)
     2.8 +int anm_init_animation(struct anm_animation *anim)
     2.9  {
    2.10  	int i, j;
    2.11  	static const float defaults[] = {
    2.12 @@ -17,19 +17,59 @@
    2.13  		1.0f, 1.0f, 1.0f		/* default scale factor */
    2.14  	};
    2.15  
    2.16 +	anim->name = 0;
    2.17 +
    2.18 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
    2.19 +		if(anm_init_track(anim->tracks + i) == -1) {
    2.20 +			for(j=0; j<i; j++) {
    2.21 +				anm_destroy_track(anim->tracks + i);
    2.22 +			}
    2.23 +		}
    2.24 +		anm_set_track_default(anim->tracks + i, defaults[i]);
    2.25 +	}
    2.26 +	return 0;
    2.27 +}
    2.28 +
    2.29 +void anm_destroy_animation(struct anm_animation *anim)
    2.30 +{
    2.31 +	int i;
    2.32 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
    2.33 +		anm_destroy_track(anim->tracks + i);
    2.34 +	}
    2.35 +	free(anim->name);
    2.36 +}
    2.37 +
    2.38 +void anm_set_animation_name(struct anm_animation *anim, const char *name)
    2.39 +{
    2.40 +	char *newname = malloc(strlen(name) + 1);
    2.41 +	if(!newname) return;
    2.42 +
    2.43 +	strcpy(newname, name);
    2.44 +
    2.45 +	free(anim->name);
    2.46 +	anim->name = newname;
    2.47 +}
    2.48 +
    2.49 +/* ---- node implementation ----- */
    2.50 +
    2.51 +int anm_init_node(struct anm_node *node)
    2.52 +{
    2.53  	memset(node, 0, sizeof *node);
    2.54  
    2.55 -	for(i=0; i<ANM_NUM_TRACKS; i++) {
    2.56 -		if(anm_init_track(node->tracks + i) == -1) {
    2.57 -			for(j=0; j<i; j++) {
    2.58 -				anm_destroy_track(node->tracks + i);
    2.59 -			}
    2.60 -		}
    2.61 -		anm_set_track_default(node->tracks + i, defaults[i]);
    2.62 +	node->cur_anim[1] = -1;
    2.63 +
    2.64 +	if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) {
    2.65 +		return -1;
    2.66 +	}
    2.67 +	if(anm_init_animation(node->animations) == -1) {
    2.68 +		dynarr_free(node->animations);
    2.69 +		return -1;
    2.70  	}
    2.71  
    2.72 -	node->cache.time = ANM_TIME_INVAL;
    2.73 -	node->cache.inv_time = ANM_TIME_INVAL;
    2.74 +	/* initialize thread-local matrix cache */
    2.75 +	pthread_key_create(&node->cache_key, 0);
    2.76 +	pthread_mutex_init(&node->cache_list_lock, 0);
    2.77 +
    2.78  	return 0;
    2.79  }
    2.80  
    2.81 @@ -39,7 +79,17 @@
    2.82  	free(node->name);
    2.83  
    2.84  	for(i=0; i<ANM_NUM_TRACKS; i++) {
    2.85 -		anm_destroy_track(node->tracks + i);
    2.86 +		anm_destroy_animation(node->animations + i);
    2.87 +	}
    2.88 +	dynarr_free(node->animations);
    2.89 +
    2.90 +	/* destroy thread-specific cache */
    2.91 +	pthread_key_delete(node->cache_key);
    2.92 +
    2.93 +	while(node->cache_list) {
    2.94 +		struct mat_cache *tmp = node->cache_list;
    2.95 +		node->cache_list = tmp->next;
    2.96 +		free(tmp);
    2.97  	}
    2.98  }
    2.99  
   2.100 @@ -113,26 +163,6 @@
   2.101  	return node->name ? node->name : "";
   2.102  }
   2.103  
   2.104 -void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
   2.105 -{
   2.106 -	int i;
   2.107 -
   2.108 -	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.109 -		anm_set_track_interpolator(node->tracks + i, in);
   2.110 -	}
   2.111 -	invalidate_cache(node);
   2.112 -}
   2.113 -
   2.114 -void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
   2.115 -{
   2.116 -	int i;
   2.117 -
   2.118 -	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.119 -		anm_set_track_extrapolator(node->tracks + i, ex);
   2.120 -	}
   2.121 -	invalidate_cache(node);
   2.122 -}
   2.123 -
   2.124  void anm_link_node(struct anm_node *p, struct anm_node *c)
   2.125  {
   2.126  	c->next = p->child;
   2.127 @@ -165,40 +195,398 @@
   2.128  	return -1;
   2.129  }
   2.130  
   2.131 +void anm_set_pivot(struct anm_node *node, vec3_t piv)
   2.132 +{
   2.133 +	node->pivot = piv;
   2.134 +}
   2.135 +
   2.136 +vec3_t anm_get_pivot(struct anm_node *node)
   2.137 +{
   2.138 +	return node->pivot;
   2.139 +}
   2.140 +
   2.141 +
   2.142 +/* animation management */
   2.143 +
   2.144 +int anm_use_node_animation(struct anm_node *node, int aidx)
   2.145 +{
   2.146 +	if(aidx == node->cur_anim[0] && node->cur_anim[1] == -1) {
   2.147 +		return 0;	/* no change, no invalidation */
   2.148 +	}
   2.149 +
   2.150 +	if(aidx < 0 || aidx >= anm_get_animation_count(node)) {
   2.151 +		return -1;
   2.152 +	}
   2.153 +
   2.154 +	node->cur_anim[0] = aidx;
   2.155 +	node->cur_anim[1] = -1;
   2.156 +	node->cur_mix = 0;
   2.157 +	node->blend_dur = -1;
   2.158 +
   2.159 +	invalidate_cache(node);
   2.160 +	return 0;
   2.161 +}
   2.162 +
   2.163 +int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t)
   2.164 +{
   2.165 +	int num_anim;
   2.166 +
   2.167 +	if(node->cur_anim[0] == aidx && node->cur_anim[1] == bidx &&
   2.168 +			fabs(t - node->cur_mix) < 1e-6) {
   2.169 +		return 0;	/* no change, no invalidation */
   2.170 +	}
   2.171 +
   2.172 +	num_anim = anm_get_animation_count(node);
   2.173 +	if(aidx < 0 || aidx >= num_anim) {
   2.174 +		return anm_use_animation(node, bidx);
   2.175 +	}
   2.176 +	if(bidx < 0 || bidx >= num_anim) {
   2.177 +		return anm_use_animation(node, aidx);
   2.178 +	}
   2.179 +	node->cur_anim[0] = aidx;
   2.180 +	node->cur_anim[1] = bidx;
   2.181 +	node->cur_mix = t;
   2.182 +
   2.183 +	invalidate_cache(node);
   2.184 +	return 0;
   2.185 +}
   2.186 +
   2.187 +int anm_use_animation(struct anm_node *node, int aidx)
   2.188 +{
   2.189 +	struct anm_node *child;
   2.190 +
   2.191 +	if(anm_use_node_animation(node, aidx) == -1) {
   2.192 +		return -1;
   2.193 +	}
   2.194 +
   2.195 +	child = node->child;
   2.196 +	while(child) {
   2.197 +		if(anm_use_animation(child, aidx) == -1) {
   2.198 +			return -1;
   2.199 +		}
   2.200 +		child = child->next;
   2.201 +	}
   2.202 +	return 0;
   2.203 +}
   2.204 +
   2.205 +int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t)
   2.206 +{
   2.207 +	struct anm_node *child;
   2.208 +
   2.209 +	if(anm_use_node_animations(node, aidx, bidx, t) == -1) {
   2.210 +		return -1;
   2.211 +	}
   2.212 +
   2.213 +	child = node->child;
   2.214 +	while(child) {
   2.215 +		if(anm_use_animations(child, aidx, bidx, t) == -1) {
   2.216 +			return -1;
   2.217 +		}
   2.218 +		child = child->next;
   2.219 +	}
   2.220 +	return 0;
   2.221 +
   2.222 +}
   2.223 +
   2.224 +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which)
   2.225 +{
   2.226 +	if(which < 0 || which >= 2) {
   2.227 +		return;
   2.228 +	}
   2.229 +	node->cur_anim_offset[which] = offs;
   2.230 +}
   2.231 +
   2.232 +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which)
   2.233 +{
   2.234 +	if(which < 0 || which >= 2) {
   2.235 +		return 0;
   2.236 +	}
   2.237 +	return node->cur_anim_offset[which];
   2.238 +}
   2.239 +
   2.240 +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which)
   2.241 +{
   2.242 +	struct anm_node *c = node->child;
   2.243 +	while(c) {
   2.244 +		anm_set_animation_offset(c, offs, which);
   2.245 +		c = c->next;
   2.246 +	}
   2.247 +
   2.248 +	anm_set_node_animation_offset(node, offs, which);
   2.249 +}
   2.250 +
   2.251 +int anm_get_active_animation_index(const struct anm_node *node, int which)
   2.252 +{
   2.253 +	if(which < 0 || which >= 2) return -1;
   2.254 +	return node->cur_anim[which];
   2.255 +}
   2.256 +
   2.257 +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which)
   2.258 +{
   2.259 +	int idx = anm_get_active_animation_index(node, which);
   2.260 +	if(idx < 0 || idx >= anm_get_animation_count(node)) {
   2.261 +		return 0;
   2.262 +	}
   2.263 +	return node->animations + idx;
   2.264 +}
   2.265 +
   2.266 +float anm_get_active_animation_mix(const struct anm_node *node)
   2.267 +{
   2.268 +	return node->cur_mix;
   2.269 +}
   2.270 +
   2.271 +int anm_get_animation_count(const struct anm_node *node)
   2.272 +{
   2.273 +	return dynarr_size(node->animations);
   2.274 +}
   2.275 +
   2.276 +int anm_add_node_animation(struct anm_node *node)
   2.277 +{
   2.278 +	struct anm_animation newanim;
   2.279 +	anm_init_animation(&newanim);
   2.280 +
   2.281 +	node->animations = dynarr_push(node->animations, &newanim);
   2.282 +	return 0;
   2.283 +}
   2.284 +
   2.285 +int anm_remove_node_animation(struct anm_node *node, int idx)
   2.286 +{
   2.287 +	fprintf(stderr, "anm_remove_animation: unimplemented!");
   2.288 +	abort();
   2.289 +	return 0;
   2.290 +}
   2.291 +
   2.292 +int anm_add_animation(struct anm_node *node)
   2.293 +{
   2.294 +	struct anm_node *child;
   2.295 +
   2.296 +	if(anm_add_node_animation(node) == -1) {
   2.297 +		return -1;
   2.298 +	}
   2.299 +
   2.300 +	child = node->child;
   2.301 +	while(child) {
   2.302 +		if(anm_add_animation(child)) {
   2.303 +			return -1;
   2.304 +		}
   2.305 +		child = child->next;
   2.306 +	}
   2.307 +	return 0;
   2.308 +}
   2.309 +
   2.310 +int anm_remove_animation(struct anm_node *node, int idx)
   2.311 +{
   2.312 +	struct anm_node *child;
   2.313 +
   2.314 +	if(anm_remove_node_animation(node, idx) == -1) {
   2.315 +		return -1;
   2.316 +	}
   2.317 +
   2.318 +	child = node->child;
   2.319 +	while(child) {
   2.320 +		if(anm_remove_animation(child, idx) == -1) {
   2.321 +			return -1;
   2.322 +		}
   2.323 +		child = child->next;
   2.324 +	}
   2.325 +	return 0;
   2.326 +}
   2.327 +
   2.328 +struct anm_animation *anm_get_animation(struct anm_node *node, int idx)
   2.329 +{
   2.330 +	if(idx < 0 || idx > anm_get_animation_count(node)) {
   2.331 +		return 0;
   2.332 +	}
   2.333 +	return node->animations + idx;
   2.334 +}
   2.335 +
   2.336 +struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name)
   2.337 +{
   2.338 +	return anm_get_animation(node, anm_find_animation(node, name));
   2.339 +}
   2.340 +
   2.341 +int anm_find_animation(struct anm_node *node, const char *name)
   2.342 +{
   2.343 +	int i, count = anm_get_animation_count(node);
   2.344 +	for(i=0; i<count; i++) {
   2.345 +		if(strcmp(node->animations[i].name, name) == 0) {
   2.346 +			return i;
   2.347 +		}
   2.348 +	}
   2.349 +	return -1;
   2.350 +}
   2.351 +
   2.352 +/* all the rest act on the current animation(s) */
   2.353 +
   2.354 +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
   2.355 +{
   2.356 +	int i;
   2.357 +	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.358 +	if(!anim) return;
   2.359 +
   2.360 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.361 +		anm_set_track_interpolator(anim->tracks + i, in);
   2.362 +	}
   2.363 +	invalidate_cache(node);
   2.364 +}
   2.365 +
   2.366 +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
   2.367 +{
   2.368 +	int i;
   2.369 +	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.370 +	if(!anim) return;
   2.371 +
   2.372 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.373 +		anm_set_track_extrapolator(anim->tracks + i, ex);
   2.374 +	}
   2.375 +	invalidate_cache(node);
   2.376 +}
   2.377 +
   2.378 +void anm_set_node_active_animation_name(struct anm_node *node, const char *name)
   2.379 +{
   2.380 +	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.381 +	if(!anim) return;
   2.382 +
   2.383 +	anm_set_animation_name(anim, name);
   2.384 +}
   2.385 +
   2.386 +void anm_set_active_animation_name(struct anm_node *node, const char *name)
   2.387 +{
   2.388 +	struct anm_node *child;
   2.389 +
   2.390 +	anm_set_node_active_animation_name(node, name);
   2.391 +
   2.392 +	child = node->child;
   2.393 +	while(child) {
   2.394 +		anm_set_active_animation_name(child, name);
   2.395 +		child = child->next;
   2.396 +	}
   2.397 +}
   2.398 +
   2.399 +const char *anm_get_active_animation_name(struct anm_node *node)
   2.400 +{
   2.401 +	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.402 +	if(anim) {
   2.403 +		return anim->name;
   2.404 +	}
   2.405 +	return 0;
   2.406 +}
   2.407 +
   2.408 +/* ---- high level animation blending ---- */
   2.409 +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur)
   2.410 +{
   2.411 +	struct anm_node *c = node->child;
   2.412 +
   2.413 +	if(anmidx == node->cur_anim[0]) {
   2.414 +		return;
   2.415 +	}
   2.416 +
   2.417 +	while(c) {
   2.418 +		anm_transition(c, anmidx, start, dur);
   2.419 +		c = c->next;
   2.420 +	}
   2.421 +
   2.422 +	anm_node_transition(node, anmidx, start, dur);
   2.423 +}
   2.424 +
   2.425 +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur)
   2.426 +{
   2.427 +	if(anmidx == node->cur_anim[0]) {
   2.428 +		return;
   2.429 +	}
   2.430 +
   2.431 +	node->cur_anim[1] = anmidx;
   2.432 +	node->cur_anim_offset[1] = start;
   2.433 +	node->blend_dur = dur;
   2.434 +}
   2.435 +
   2.436 +
   2.437 +#define BLEND_START_TM	node->cur_anim_offset[1]
   2.438 +
   2.439 +static anm_time_t animation_time(struct anm_node *node, anm_time_t tm, int which)
   2.440 +{
   2.441 +	float t;
   2.442 +
   2.443 +	if(node->blend_dur >= 0) {
   2.444 +		/* we're in transition... */
   2.445 +		t = (float)(tm - BLEND_START_TM) / (float)node->blend_dur;
   2.446 +		if(t < 0.0) t = 0.0;
   2.447 +
   2.448 +		node->cur_mix = t;
   2.449 +
   2.450 +		if(t > 1.0) {
   2.451 +			/* switch completely over to the target animation and stop blending */
   2.452 +			anm_use_node_animation(node, node->cur_anim[1]);
   2.453 +			node->cur_anim_offset[0] = node->cur_anim_offset[1];
   2.454 +		}
   2.455 +	}
   2.456 +
   2.457 +	return tm - node->cur_anim_offset[which];
   2.458 +}
   2.459 +
   2.460 +
   2.461  void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
   2.462  {
   2.463 -	anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x);
   2.464 -	anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y);
   2.465 -	anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z);
   2.466 +	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.467 +	if(!anim) return;
   2.468 +
   2.469 +	anm_set_value(anim->tracks + ANM_TRACK_POS_X, tm, pos.x);
   2.470 +	anm_set_value(anim->tracks + ANM_TRACK_POS_Y, tm, pos.y);
   2.471 +	anm_set_value(anim->tracks + ANM_TRACK_POS_Z, tm, pos.z);
   2.472  	invalidate_cache(node);
   2.473  }
   2.474  
   2.475 +
   2.476  vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
   2.477  {
   2.478  	vec3_t v;
   2.479 -	v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm);
   2.480 -	v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm);
   2.481 -	v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm);
   2.482 +	anm_time_t tm0 = animation_time(node, tm, 0);
   2.483 +	struct anm_animation *anim0 = anm_get_active_animation(node, 0);
   2.484 +	struct anm_animation *anim1 = anm_get_active_animation(node, 1);
   2.485 +
   2.486 +	if(!anim0) {
   2.487 +		return v3_cons(0, 0, 0);
   2.488 +	}
   2.489 +
   2.490 +	v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm0);
   2.491 +	v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm0);
   2.492 +	v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm0);
   2.493 +
   2.494 +	if(anim1) {
   2.495 +		vec3_t v1;
   2.496 +		anm_time_t tm1 = animation_time(node, tm, 1);
   2.497 +		v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm1);
   2.498 +		v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm1);
   2.499 +		v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm1);
   2.500 +
   2.501 +		v.x = v.x + (v1.x - v.x) * node->cur_mix;
   2.502 +		v.y = v.y + (v1.y - v.y) * node->cur_mix;
   2.503 +		v.z = v.z + (v1.z - v.z) * node->cur_mix;
   2.504 +	}
   2.505 +
   2.506  	return v;
   2.507  }
   2.508  
   2.509  void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm)
   2.510  {
   2.511 -	anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x);
   2.512 -	anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
   2.513 -	anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
   2.514 -	anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w);
   2.515 +	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.516 +	if(!anim) return;
   2.517 +
   2.518 +	anm_set_value(anim->tracks + ANM_TRACK_ROT_X, tm, rot.x);
   2.519 +	anm_set_value(anim->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
   2.520 +	anm_set_value(anim->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
   2.521 +	anm_set_value(anim->tracks + ANM_TRACK_ROT_W, tm, rot.w);
   2.522  	invalidate_cache(node);
   2.523  }
   2.524  
   2.525 -quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
   2.526 +static quat_t get_node_rotation(struct anm_node *node, anm_time_t tm, struct anm_animation *anim)
   2.527  {
   2.528  #ifndef ROT_USE_SLERP
   2.529  	quat_t q;
   2.530 -	q.x = anm_get_value(node->tracks + ANM_TRACK_ROT_X, tm);
   2.531 -	q.y = anm_get_value(node->tracks + ANM_TRACK_ROT_Y, tm);
   2.532 -	q.z = anm_get_value(node->tracks + ANM_TRACK_ROT_Z, tm);
   2.533 -	q.w = anm_get_value(node->tracks + ANM_TRACK_ROT_W, tm);
   2.534 +	q.x = anm_get_value(anim->tracks + ANM_TRACK_ROT_X, tm);
   2.535 +	q.y = anm_get_value(anim->tracks + ANM_TRACK_ROT_Y, tm);
   2.536 +	q.z = anm_get_value(anim->tracks + ANM_TRACK_ROT_Z, tm);
   2.537 +	q.w = anm_get_value(anim->tracks + ANM_TRACK_ROT_W, tm);
   2.538  	return q;
   2.539  #else
   2.540  	int idx0, idx1, last_idx;
   2.541 @@ -207,10 +595,10 @@
   2.542  	struct anm_track *track_x, *track_y, *track_z, *track_w;
   2.543  	quat_t q, q1, q2;
   2.544  
   2.545 -	track_x = node->tracks + ANM_TRACK_ROT_X;
   2.546 -	track_y = node->tracks + ANM_TRACK_ROT_Y;
   2.547 -	track_z = node->tracks + ANM_TRACK_ROT_Z;
   2.548 -	track_w = node->tracks + ANM_TRACK_ROT_W;
   2.549 +	track_x = anim->tracks + ANM_TRACK_ROT_X;
   2.550 +	track_y = anim->tracks + ANM_TRACK_ROT_Y;
   2.551 +	track_z = anim->tracks + ANM_TRACK_ROT_Z;
   2.552 +	track_w = anim->tracks + ANM_TRACK_ROT_W;
   2.553  
   2.554  	if(!track_x->count) {
   2.555  		q.x = track_x->def_val;
   2.556 @@ -267,20 +655,66 @@
   2.557  #endif
   2.558  }
   2.559  
   2.560 +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
   2.561 +{
   2.562 +	quat_t q;
   2.563 +	anm_time_t tm0 = animation_time(node, tm, 0);
   2.564 +	struct anm_animation *anim0 = anm_get_active_animation(node, 0);
   2.565 +	struct anm_animation *anim1 = anm_get_active_animation(node, 1);
   2.566 +
   2.567 +	if(!anim0) {
   2.568 +		return quat_identity();
   2.569 +	}
   2.570 +
   2.571 +	q = get_node_rotation(node, tm0, anim0);
   2.572 +
   2.573 +	if(anim1) {
   2.574 +		anm_time_t tm1 = animation_time(node, tm, 1);
   2.575 +		quat_t q1 = get_node_rotation(node, tm1, anim1);
   2.576 +
   2.577 +		q = quat_slerp(q, q1, node->cur_mix);
   2.578 +	}
   2.579 +	return q;
   2.580 +}
   2.581 +
   2.582  void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm)
   2.583  {
   2.584 -	anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x);
   2.585 -	anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
   2.586 -	anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
   2.587 +	struct anm_animation *anim = anm_get_active_animation(node, 0);
   2.588 +	if(!anim) return;
   2.589 +
   2.590 +	anm_set_value(anim->tracks + ANM_TRACK_SCL_X, tm, scl.x);
   2.591 +	anm_set_value(anim->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
   2.592 +	anm_set_value(anim->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
   2.593  	invalidate_cache(node);
   2.594  }
   2.595  
   2.596  vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
   2.597  {
   2.598  	vec3_t v;
   2.599 -	v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm);
   2.600 -	v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm);
   2.601 -	v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm);
   2.602 +	anm_time_t tm0 = animation_time(node, tm, 0);
   2.603 +	struct anm_animation *anim0 = anm_get_active_animation(node, 0);
   2.604 +	struct anm_animation *anim1 = anm_get_active_animation(node, 1);
   2.605 +
   2.606 +	if(!anim0) {
   2.607 +		return v3_cons(1, 1, 1);
   2.608 +	}
   2.609 +
   2.610 +	v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm0);
   2.611 +	v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm0);
   2.612 +	v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm0);
   2.613 +
   2.614 +	if(anim1) {
   2.615 +		vec3_t v1;
   2.616 +		anm_time_t tm1 = animation_time(node, tm, 1);
   2.617 +		v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm1);
   2.618 +		v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm1);
   2.619 +		v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm1);
   2.620 +
   2.621 +		v.x = v.x + (v1.x - v.x) * node->cur_mix;
   2.622 +		v.y = v.y + (v1.y - v.y) * node->cur_mix;
   2.623 +		v.z = v.z + (v1.z - v.z) * node->cur_mix;
   2.624 +	}
   2.625 +
   2.626  	return v;
   2.627  }
   2.628  
   2.629 @@ -324,16 +758,6 @@
   2.630  	return v3_mul(s, ps);
   2.631  }
   2.632  
   2.633 -void anm_set_pivot(struct anm_node *node, vec3_t piv)
   2.634 -{
   2.635 -	node->pivot = piv;
   2.636 -}
   2.637 -
   2.638 -vec3_t anm_get_pivot(struct anm_node *node)
   2.639 -{
   2.640 -	return node->pivot;
   2.641 -}
   2.642 -
   2.643  void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
   2.644  {
   2.645  	int i;
   2.646 @@ -371,43 +795,102 @@
   2.647  	m4_inverse(mat, tmp);
   2.648  }
   2.649  
   2.650 +void anm_eval_node(struct anm_node *node, anm_time_t tm)
   2.651 +{
   2.652 +	anm_get_node_matrix(node, node->matrix, tm);
   2.653 +}
   2.654 +
   2.655 +void anm_eval(struct anm_node *node, anm_time_t tm)
   2.656 +{
   2.657 +	struct anm_node *c;
   2.658 +
   2.659 +	anm_eval_node(node, tm);
   2.660 +
   2.661 +	if(node->parent) {
   2.662 +		/* due to post-order traversal, the parent matrix is already evaluated */
   2.663 +		m4_mult(node->matrix, node->parent->matrix, node->matrix);
   2.664 +	}
   2.665 +
   2.666 +	/* recersively evaluate all children */
   2.667 +	c = node->child;
   2.668 +	while(c) {
   2.669 +		anm_eval(c, tm);
   2.670 +		c = c->next;
   2.671 +	}
   2.672 +}
   2.673 +
   2.674  void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
   2.675  {
   2.676 -	if(node->cache.time != tm) {
   2.677 -		anm_get_node_matrix(node, node->cache.matrix, tm);
   2.678 +	struct mat_cache *cache = pthread_getspecific(node->cache_key);
   2.679 +	if(!cache) {
   2.680 +		cache = malloc(sizeof *cache);
   2.681 +		assert(cache);
   2.682 +
   2.683 +		pthread_mutex_lock(&node->cache_list_lock);
   2.684 +		cache->next = node->cache_list;
   2.685 +		node->cache_list = cache;
   2.686 +		pthread_mutex_unlock(&node->cache_list_lock);
   2.687 +
   2.688 +		cache->time = ANM_TIME_INVAL;
   2.689 +		cache->inv_time = ANM_TIME_INVAL;
   2.690 +		pthread_setspecific(node->cache_key, cache);
   2.691 +	}
   2.692 +
   2.693 +	if(cache->time != tm) {
   2.694 +		anm_get_node_matrix(node, cache->matrix, tm);
   2.695  
   2.696  		if(node->parent) {
   2.697  			mat4_t parent_mat;
   2.698  
   2.699  			anm_get_matrix(node->parent, parent_mat, tm);
   2.700 -			m4_mult(node->cache.matrix, parent_mat, node->cache.matrix);
   2.701 +			m4_mult(cache->matrix, parent_mat, cache->matrix);
   2.702  		}
   2.703 -		node->cache.time = tm;
   2.704 +		cache->time = tm;
   2.705  	}
   2.706 -	m4_copy(mat, node->cache.matrix);
   2.707 +	m4_copy(mat, cache->matrix);
   2.708  }
   2.709  
   2.710  void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
   2.711  {
   2.712 -	if(node->cache.inv_time != tm) {
   2.713 +	struct mat_cache *cache = pthread_getspecific(node->cache_key);
   2.714 +	if(!cache) {
   2.715 +		cache = malloc(sizeof *cache);
   2.716 +		assert(cache);
   2.717 +
   2.718 +		pthread_mutex_lock(&node->cache_list_lock);
   2.719 +		cache->next = node->cache_list;
   2.720 +		node->cache_list = cache;
   2.721 +		pthread_mutex_unlock(&node->cache_list_lock);
   2.722 +
   2.723 +		cache->inv_time = ANM_TIME_INVAL;
   2.724 +		cache->inv_time = ANM_TIME_INVAL;
   2.725 +		pthread_setspecific(node->cache_key, cache);
   2.726 +	}
   2.727 +
   2.728 +	if(cache->inv_time != tm) {
   2.729  		anm_get_matrix(node, mat, tm);
   2.730 -		m4_inverse(node->cache.inv_matrix, mat);
   2.731 -		node->cache.inv_time = tm;
   2.732 +		m4_inverse(cache->inv_matrix, mat);
   2.733 +		cache->inv_time = tm;
   2.734  	}
   2.735 -	m4_copy(mat, node->cache.inv_matrix);
   2.736 +	m4_copy(mat, cache->inv_matrix);
   2.737  }
   2.738  
   2.739  anm_time_t anm_get_start_time(struct anm_node *node)
   2.740  {
   2.741 -	int i;
   2.742 +	int i, j;
   2.743  	struct anm_node *c;
   2.744  	anm_time_t res = LONG_MAX;
   2.745  
   2.746 -	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.747 -		if(node->tracks[i].count) {
   2.748 -			anm_time_t tm = node->tracks[i].keys[0].time;
   2.749 -			if(tm < res) {
   2.750 -				res = tm;
   2.751 +	for(j=0; j<2; j++) {
   2.752 +		struct anm_animation *anim = anm_get_active_animation(node, j);
   2.753 +		if(!anim) break;
   2.754 +
   2.755 +		for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.756 +			if(anim->tracks[i].count) {
   2.757 +				anm_time_t tm = anim->tracks[i].keys[0].time;
   2.758 +				if(tm < res) {
   2.759 +					res = tm;
   2.760 +				}
   2.761  			}
   2.762  		}
   2.763  	}
   2.764 @@ -425,15 +908,20 @@
   2.765  
   2.766  anm_time_t anm_get_end_time(struct anm_node *node)
   2.767  {
   2.768 -	int i;
   2.769 +	int i, j;
   2.770  	struct anm_node *c;
   2.771  	anm_time_t res = LONG_MIN;
   2.772  
   2.773 -	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.774 -		if(node->tracks[i].count) {
   2.775 -			anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time;
   2.776 -			if(tm > res) {
   2.777 -				res = tm;
   2.778 +	for(j=0; j<2; j++) {
   2.779 +		struct anm_animation *anim = anm_get_active_animation(node, j);
   2.780 +		if(!anim) break;
   2.781 +
   2.782 +		for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.783 +			if(anim->tracks[i].count) {
   2.784 +				anm_time_t tm = anim->tracks[i].keys[anim->tracks[i].count - 1].time;
   2.785 +				if(tm > res) {
   2.786 +					res = tm;
   2.787 +				}
   2.788  			}
   2.789  		}
   2.790  	}
   2.791 @@ -451,5 +939,8 @@
   2.792  
   2.793  static void invalidate_cache(struct anm_node *node)
   2.794  {
   2.795 -	node->cache.time = node->cache.inv_time = ANM_TIME_INVAL;
   2.796 +	struct mat_cache *cache = pthread_getspecific(node->cache_key);
   2.797 +	if(cache) {
   2.798 +	   cache->time = cache->inv_time = ANM_TIME_INVAL;
   2.799 +	}
   2.800  }
     3.1 --- a/libs/anim/anim.h	Fri Jan 17 18:16:09 2014 +0200
     3.2 +++ b/libs/anim/anim.h	Fri Jan 17 18:27:47 2014 +0200
     3.3 @@ -3,6 +3,8 @@
     3.4  
     3.5  #include "config.h"
     3.6  
     3.7 +#include <pthread.h>
     3.8 +
     3.9  #include <vmath/vector.h>
    3.10  #include <vmath/quat.h>
    3.11  #include <vmath/matrix.h>
    3.12 @@ -25,17 +27,35 @@
    3.13  	ANM_NUM_TRACKS
    3.14  };
    3.15  
    3.16 +struct anm_animation {
    3.17 +	char *name;
    3.18 +	struct anm_track tracks[ANM_NUM_TRACKS];
    3.19 +};
    3.20 +
    3.21  struct anm_node {
    3.22  	char *name;
    3.23  
    3.24 -	struct anm_track tracks[ANM_NUM_TRACKS];
    3.25 +	int cur_anim[2];
    3.26 +	anm_time_t cur_anim_offset[2];
    3.27 +	float cur_mix;
    3.28 +
    3.29 +	/* high-level animation blending transition duration */
    3.30 +	anm_time_t blend_dur;
    3.31 +
    3.32 +	struct anm_animation *animations;
    3.33  	vec3_t pivot;
    3.34  
    3.35  	/* matrix cache */
    3.36  	struct mat_cache {
    3.37  		mat4_t matrix, inv_matrix;
    3.38  		anm_time_t time, inv_time;
    3.39 -	} cache;
    3.40 +		struct mat_cache *next;
    3.41 +	} *cache_list;
    3.42 +	pthread_key_t cache_key;
    3.43 +	pthread_mutex_t cache_list_lock;
    3.44 +
    3.45 +	/* matrix calculated by anm_eval functions (no locking, meant as a pre-pass) */
    3.46 +	mat4_t matrix;
    3.47  
    3.48  	struct anm_node *parent;
    3.49  	struct anm_node *child;
    3.50 @@ -46,6 +66,14 @@
    3.51  extern "C" {
    3.52  #endif
    3.53  
    3.54 +int anm_init_animation(struct anm_animation *anim);
    3.55 +void anm_destroy_animation(struct anm_animation *anim);
    3.56 +
    3.57 +void anm_set_animation_name(struct anm_animation *anim, const char *name);
    3.58 +
    3.59 +
    3.60 +/* ---- node/hierarchy management ---- */
    3.61 +
    3.62  /* node constructor and destructor */
    3.63  int anm_init_node(struct anm_node *node);
    3.64  void anm_destroy_node(struct anm_node *node);
    3.65 @@ -66,13 +94,82 @@
    3.66  int anm_set_node_name(struct anm_node *node, const char *name);
    3.67  const char *anm_get_node_name(struct anm_node *node);
    3.68  
    3.69 -void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in);
    3.70 -void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex);
    3.71 -
    3.72  /* link and unlink nodes with parent/child relations */
    3.73  void anm_link_node(struct anm_node *parent, struct anm_node *child);
    3.74  int anm_unlink_node(struct anm_node *parent, struct anm_node *child);
    3.75  
    3.76 +void anm_set_pivot(struct anm_node *node, vec3_t pivot);
    3.77 +vec3_t anm_get_pivot(struct anm_node *node);
    3.78 +
    3.79 +/* ---- multiple animations and animation blending ---- */
    3.80 +
    3.81 +/* set active animation(s) */
    3.82 +int anm_use_node_animation(struct anm_node *node, int aidx);
    3.83 +int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t);
    3.84 +/* recursive variants */
    3.85 +int anm_use_animation(struct anm_node *node, int aidx);
    3.86 +int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t);
    3.87 +
    3.88 +/* set/get current animation offset(s) */
    3.89 +void anm_set_node_animation_offset(struct anm_node *node, anm_time_t offs, int which);
    3.90 +anm_time_t anm_get_animation_offset(const struct anm_node *node, int which);
    3.91 +/* recursive variant */
    3.92 +void anm_set_animation_offset(struct anm_node *node, anm_time_t offs, int which);
    3.93 +
    3.94 +/* returns the requested current animation index, which can be 0 or 1 */
    3.95 +int anm_get_active_animation_index(const struct anm_node *node, int which);
    3.96 +/* returns the requested current animation, which can be 0 or 1 */
    3.97 +struct anm_animation *anm_get_active_animation(const struct anm_node *node, int which);
    3.98 +float anm_get_active_animation_mix(const struct anm_node *node);
    3.99 +
   3.100 +int anm_get_animation_count(const struct anm_node *node);
   3.101 +
   3.102 +/* add/remove an animation to the specified node */
   3.103 +int anm_add_node_animation(struct anm_node *node);
   3.104 +int anm_remove_node_animation(struct anm_node *node, int idx);
   3.105 +
   3.106 +/* add/remove an animation to the specified node and all it's descendants */
   3.107 +int anm_add_animation(struct anm_node *node);
   3.108 +int anm_remove_animation(struct anm_node *node, int idx);
   3.109 +
   3.110 +struct anm_animation *anm_get_animation(struct anm_node *node, int idx);
   3.111 +struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name);
   3.112 +
   3.113 +int anm_find_animation(struct anm_node *node, const char *name);
   3.114 +
   3.115 +/* set the interpolator for the (first) currently active animation */
   3.116 +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in);
   3.117 +/* set the extrapolator for the (first) currently active animation */
   3.118 +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex);
   3.119 +
   3.120 +/* set the name of the currently active animation of this node only */
   3.121 +void anm_set_node_active_animation_name(struct anm_node *node, const char *name);
   3.122 +/* recursively set the name of the currently active animation for this node
   3.123 + * and all it's descendants */
   3.124 +void anm_set_active_animation_name(struct anm_node *node, const char *name);
   3.125 +/* get the name of the currently active animation of this node */
   3.126 +const char *anm_get_active_animation_name(struct anm_node *node);
   3.127 +
   3.128 +
   3.129 +/* ---- high level animation blending interface ---- */
   3.130 +/* XXX this convenience interface assumes monotonically increasing time values
   3.131 + *     in all subsequent calls to anm_get_* and anm_eval_* functions.
   3.132 + *
   3.133 + * anmidx: index of the animation to transition to
   3.134 + * start: when to start the transition
   3.135 + * dur: transition duration
   3.136 + *
   3.137 + * sets up a transition from the current animation (cur_anim[0]) to another animation.
   3.138 + * at time start + dur, the transition will be completed, cur_anim[0] will be the new
   3.139 + * animation and cur_anim_offset[0] will be equal to start.
   3.140 + */
   3.141 +void anm_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur);
   3.142 +/* non-recursive variant, acts on a single node (you probably DON'T want to use this) */
   3.143 +void anm_node_transition(struct anm_node *node, int anmidx, anm_time_t start, anm_time_t dur);
   3.144 +
   3.145 +
   3.146 +/* ---- keyframes / PRS interpolation ---- */
   3.147 +
   3.148  void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm);
   3.149  vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm);
   3.150  
   3.151 @@ -87,13 +184,27 @@
   3.152  quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm);
   3.153  vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm);
   3.154  
   3.155 -void anm_set_pivot(struct anm_node *node, vec3_t pivot);
   3.156 -vec3_t anm_get_pivot(struct anm_node *node);
   3.157 +/* those return the start and end times of the whole tree */
   3.158 +anm_time_t anm_get_start_time(struct anm_node *node);
   3.159 +anm_time_t anm_get_end_time(struct anm_node *node);
   3.160 +
   3.161 +
   3.162 +/* ---- transformation matrices ---- */
   3.163  
   3.164  /* these calculate the matrix and inverse matrix of this node alone */
   3.165  void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);
   3.166  void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);
   3.167  
   3.168 +/* ---- top-down matrix calculation interface ---- */
   3.169 +
   3.170 +/* calculate and set the matrix of this node */
   3.171 +void anm_eval_node(struct anm_node *node, anm_time_t tm);
   3.172 +/* calculate and set the matrix of this node and all its children recursively */
   3.173 +void anm_eval(struct anm_node *node, anm_time_t tm);
   3.174 +
   3.175 +
   3.176 +/* ---- bottom-up lazy matrix calculation interface ---- */
   3.177 +
   3.178  /* These calculate the matrix and inverse matrix of this node taking hierarchy
   3.179   * into account. The results are cached in thread-specific storage and returned
   3.180   * if there's no change in time or tracks from the last query...
   3.181 @@ -101,10 +212,6 @@
   3.182  void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);
   3.183  void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);
   3.184  
   3.185 -/* those return the start and end times of the whole tree */
   3.186 -anm_time_t anm_get_start_time(struct anm_node *node);
   3.187 -anm_time_t anm_get_end_time(struct anm_node *node);
   3.188 -
   3.189  #ifdef __cplusplus
   3.190  }
   3.191  #endif
     4.1 --- a/libs/anim/dynarr.h	Fri Jan 17 18:16:09 2014 +0200
     4.2 +++ b/libs/anim/dynarr.h	Fri Jan 17 18:27:47 2014 +0200
     4.3 @@ -1,8 +1,6 @@
     4.4  #ifndef DYNARR_H_
     4.5  #define DYNARR_H_
     4.6  
     4.7 -#include "config.h"
     4.8 -
     4.9  void *dynarr_alloc(int elem, int szelem);
    4.10  void dynarr_free(void *da);
    4.11  void *dynarr_resize(void *da, int elem);
     5.1 --- a/libs/anim/track.c	Fri Jan 17 18:16:09 2014 +0200
     5.2 +++ b/libs/anim/track.c	Fri Jan 17 18:27:47 2014 +0200
     5.3 @@ -69,7 +69,7 @@
     5.4  	free(track);
     5.5  }
     5.6  
     5.7 -void anm_copy_track(struct anm_track *dest, struct anm_track *src)
     5.8 +void anm_copy_track(struct anm_track *dest, const struct anm_track *src)
     5.9  {
    5.10  	free(dest->name);
    5.11  	if(dest->keys) {
     6.1 --- a/libs/anim/track.h	Fri Jan 17 18:16:09 2014 +0200
     6.2 +++ b/libs/anim/track.h	Fri Jan 17 18:27:47 2014 +0200
     6.3 @@ -59,7 +59,7 @@
     6.4  /* copies track src to dest
     6.5   * XXX: dest must have been initialized first
     6.6   */
     6.7 -void anm_copy_track(struct anm_track *dest, struct anm_track *src);
     6.8 +void anm_copy_track(struct anm_track *dest, const struct anm_track *src);
     6.9  
    6.10  int anm_set_track_name(struct anm_track *track, const char *name);
    6.11  const char *anm_get_track_name(struct anm_track *track);
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/goat3d_read.cc	Fri Jan 17 18:27:47 2014 +0200
     7.3 @@ -0,0 +1,13 @@
     7.4 +#include "goat3d_impl.h"
     7.5 +
     7.6 +using namespace g3dimpl;
     7.7 +
     7.8 +bool Scene::load(goat3d_io *io)
     7.9 +{
    7.10 +	return false;
    7.11 +}
    7.12 +
    7.13 +bool Scene::load_anim(goat3d_io *io)
    7.14 +{
    7.15 +	return false;
    7.16 +}
     8.1 --- a/src/goat3d_readxml.cc	Fri Jan 17 18:16:09 2014 +0200
     8.2 +++ b/src/goat3d_readxml.cc	Fri Jan 17 18:27:47 2014 +0200
     8.3 @@ -110,6 +110,12 @@
     8.4  	return true;
     8.5  }
     8.6  
     8.7 +bool Scene::load_anim_xml(goat3d_io *io)
     8.8 +{
     8.9 +	return false;
    8.10 +}
    8.11 +
    8.12 +
    8.13  static Material *read_material(Scene *scn, XMLElement *xml_mtl)
    8.14  {
    8.15  	Material *mtl = new Material;
     9.1 --- a/src/goat3d_write.cc	Fri Jan 17 18:16:09 2014 +0200
     9.2 +++ b/src/goat3d_write.cc	Fri Jan 17 18:27:47 2014 +0200
     9.3 @@ -225,3 +225,8 @@
     9.4  	return size;
     9.5  }
     9.6  #endif
     9.7 +
     9.8 +bool Scene::save_anim(const XFormNode *node, goat3d_io *io) const
     9.9 +{
    9.10 +	return false;
    9.11 +}
    10.1 --- a/src/scene.cc	Fri Jan 17 18:16:09 2014 +0200
    10.2 +++ b/src/scene.cc	Fri Jan 17 18:27:47 2014 +0200
    10.3 @@ -204,12 +204,7 @@
    10.4  	return (int)nodes.size();
    10.5  }
    10.6  
    10.7 -
    10.8 -bool Scene::load(goat3d_io *io)
    10.9 -{
   10.10 -	return false;
   10.11 -}
   10.12 -
   10.13 +// Scene::load is defined in goat3d_read.cc
   10.14  // Scene::loadxml is defined in goat3d_readxml.cc
   10.15  // Scene::save is defined in goat3d_write.cc
   10.16  // Scene::savexml is defined in goat3d_writexml.cc