libanim

view src/anim.c @ 23:203c11299586

set/get currently active animation name and minor enhancements in the example
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 27 Dec 2013 11:29:42 +0200
parents 9758004136f8
children 09e267e7ed4a
line source
1 #include <stdlib.h>
2 #include <limits.h>
3 #include <assert.h>
4 #include "anim.h"
5 #include "dynarr.h"
7 #define ROT_USE_SLERP
9 static void invalidate_cache(struct anm_node *node);
11 int anm_init_animation(struct anm_animation *anim)
12 {
13 int i, j;
14 static const float defaults[] = {
15 0.0f, 0.0f, 0.0f, /* default position */
16 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */
17 1.0f, 1.0f, 1.0f /* default scale factor */
18 };
20 anim->name = 0;
22 for(i=0; i<ANM_NUM_TRACKS; i++) {
23 if(anm_init_track(anim->tracks + i) == -1) {
24 for(j=0; j<i; j++) {
25 anm_destroy_track(anim->tracks + i);
26 }
27 }
28 anm_set_track_default(anim->tracks + i, defaults[i]);
29 }
30 return 0;
31 }
33 void anm_destroy_animation(struct anm_animation *anim)
34 {
35 int i;
36 for(i=0; i<ANM_NUM_TRACKS; i++) {
37 anm_destroy_track(anim->tracks + i);
38 }
39 free(anim->name);
40 }
42 void anm_set_animation_name(struct anm_animation *anim, const char *name)
43 {
44 char *newname = malloc(strlen(name) + 1);
45 if(!newname) return;
47 strcpy(newname, name);
49 free(anim->name);
50 anim->name = newname;
51 }
53 /* ---- node implementation ----- */
55 int anm_init_node(struct anm_node *node)
56 {
57 memset(node, 0, sizeof *node);
59 node->cur_anim[1] = -1;
60 node->cur_mix = 0;
62 if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) {
63 return -1;
64 }
65 if(anm_init_animation(node->animations) == -1) {
66 dynarr_free(node->animations);
67 return -1;
68 }
70 /* initialize thread-local matrix cache */
71 pthread_key_create(&node->cache_key, 0);
72 pthread_mutex_init(&node->cache_list_lock, 0);
74 return 0;
75 }
77 void anm_destroy_node(struct anm_node *node)
78 {
79 int i;
80 free(node->name);
82 for(i=0; i<ANM_NUM_TRACKS; i++) {
83 anm_destroy_animation(node->animations + i);
84 }
85 dynarr_free(node->animations);
87 /* destroy thread-specific cache */
88 pthread_key_delete(node->cache_key);
90 while(node->cache_list) {
91 struct mat_cache *tmp = node->cache_list;
92 node->cache_list = tmp->next;
93 free(tmp);
94 }
95 }
97 void anm_destroy_node_tree(struct anm_node *tree)
98 {
99 struct anm_node *c, *tmp;
101 if(!tree) return;
103 c = tree->child;
104 while(c) {
105 tmp = c;
106 c = c->next;
108 anm_destroy_node_tree(tmp);
109 }
110 anm_destroy_node(tree);
111 }
113 struct anm_node *anm_create_node(void)
114 {
115 struct anm_node *n;
117 if((n = malloc(sizeof *n))) {
118 if(anm_init_node(n) == -1) {
119 free(n);
120 return 0;
121 }
122 }
123 return n;
124 }
126 void anm_free_node(struct anm_node *node)
127 {
128 anm_destroy_node(node);
129 free(node);
130 }
132 void anm_free_node_tree(struct anm_node *tree)
133 {
134 struct anm_node *c, *tmp;
136 if(!tree) return;
138 c = tree->child;
139 while(c) {
140 tmp = c;
141 c = c->next;
143 anm_free_node_tree(tmp);
144 }
146 anm_free_node(tree);
147 }
149 int anm_set_node_name(struct anm_node *node, const char *name)
150 {
151 char *str;
153 if(!(str = malloc(strlen(name) + 1))) {
154 return -1;
155 }
156 strcpy(str, name);
157 free(node->name);
158 node->name = str;
159 return 0;
160 }
162 const char *anm_get_node_name(struct anm_node *node)
163 {
164 return node->name ? node->name : "";
165 }
167 void anm_link_node(struct anm_node *p, struct anm_node *c)
168 {
169 c->next = p->child;
170 p->child = c;
172 c->parent = p;
173 invalidate_cache(c);
174 }
176 int anm_unlink_node(struct anm_node *p, struct anm_node *c)
177 {
178 struct anm_node *iter;
180 if(p->child == c) {
181 p->child = c->next;
182 c->next = 0;
183 invalidate_cache(c);
184 return 0;
185 }
187 iter = p->child;
188 while(iter->next) {
189 if(iter->next == c) {
190 iter->next = c->next;
191 c->next = 0;
192 invalidate_cache(c);
193 return 0;
194 }
195 }
196 return -1;
197 }
199 void anm_set_pivot(struct anm_node *node, vec3_t piv)
200 {
201 node->pivot = piv;
202 }
204 vec3_t anm_get_pivot(struct anm_node *node)
205 {
206 return node->pivot;
207 }
210 /* animation management */
212 int anm_use_node_animation(struct anm_node *node, int aidx)
213 {
214 if(aidx == node->cur_anim[0] && node->cur_anim[1] == -1) {
215 return 0; /* no change, no invalidation */
216 }
218 if(aidx < 0 || aidx >= anm_get_animation_count(node)) {
219 return -1;
220 }
222 node->cur_anim[0] = aidx;
223 node->cur_anim[1] = -1;
224 node->cur_mix = 0;
226 invalidate_cache(node);
227 return 0;
228 }
230 int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t)
231 {
232 int num_anim;
234 if(node->cur_anim[0] == aidx && node->cur_anim[1] == bidx &&
235 fabs(t - node->cur_mix) < 1e-6) {
236 return 0; /* no change, no invalidation */
237 }
239 num_anim = anm_get_animation_count(node);
240 if(aidx < 0 || aidx >= num_anim) {
241 return anm_use_animation(node, bidx);
242 }
243 if(bidx < 0 || bidx >= num_anim) {
244 return anm_use_animation(node, aidx);
245 }
246 node->cur_anim[0] = aidx;
247 node->cur_anim[1] = bidx;
248 node->cur_mix = t;
250 invalidate_cache(node);
251 return 0;
252 }
254 int anm_use_animation(struct anm_node *node, int aidx)
255 {
256 struct anm_node *child;
258 if(anm_use_node_animation(node, aidx) == -1) {
259 return -1;
260 }
262 child = node->child;
263 while(child) {
264 if(anm_use_animation(child, aidx) == -1) {
265 return -1;
266 }
267 child = child->next;
268 }
269 return 0;
270 }
272 int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t)
273 {
274 struct anm_node *child;
276 if(anm_use_node_animations(node, aidx, bidx, t) == -1) {
277 return -1;
278 }
280 child = node->child;
281 while(child) {
282 if(anm_use_animations(child, aidx, bidx, t) == -1) {
283 return -1;
284 }
285 child = child->next;
286 }
287 return 0;
289 }
291 int anm_get_active_animation_index(struct anm_node *node, int which)
292 {
293 if(which < 0 || which >= 2) return -1;
294 return node->cur_anim[which];
295 }
297 struct anm_animation *anm_get_active_animation(struct anm_node *node, int which)
298 {
299 int idx = anm_get_active_animation_index(node, which);
300 if(idx < 0 || idx >= anm_get_animation_count(node)) {
301 return 0;
302 }
303 return node->animations + idx;
304 }
306 float anm_get_active_animation_mix(struct anm_node *node)
307 {
308 return node->cur_mix;
309 }
311 int anm_get_animation_count(struct anm_node *node)
312 {
313 return dynarr_size(node->animations);
314 }
316 int anm_add_node_animation(struct anm_node *node)
317 {
318 struct anm_animation newanim;
319 anm_init_animation(&newanim);
321 node->animations = dynarr_push(node->animations, &newanim);
322 return 0;
323 }
325 int anm_remove_node_animation(struct anm_node *node, int idx)
326 {
327 fprintf(stderr, "anm_remove_animation: unimplemented!");
328 abort();
329 return 0;
330 }
332 int anm_add_animation(struct anm_node *node)
333 {
334 struct anm_node *child;
336 if(anm_add_node_animation(node) == -1) {
337 return -1;
338 }
340 child = node->child;
341 while(child) {
342 if(anm_add_animation(child)) {
343 return -1;
344 }
345 child = child->next;
346 }
347 return 0;
348 }
350 int anm_remove_animation(struct anm_node *node, int idx)
351 {
352 struct anm_node *child;
354 if(anm_remove_node_animation(node, idx) == -1) {
355 return -1;
356 }
358 child = node->child;
359 while(child) {
360 if(anm_remove_animation(child, idx) == -1) {
361 return -1;
362 }
363 child = child->next;
364 }
365 return 0;
366 }
368 struct anm_animation *anm_get_animation(struct anm_node *node, int idx)
369 {
370 if(idx < 0 || idx > anm_get_animation_count(node)) {
371 return 0;
372 }
373 return node->animations + idx;
374 }
376 struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name)
377 {
378 return anm_get_animation(node, anm_find_animation(node, name));
379 }
381 int anm_find_animation(struct anm_node *node, const char *name)
382 {
383 int i, count = anm_get_animation_count(node);
384 for(i=0; i<count; i++) {
385 if(strcmp(node->animations[i].name, name) == 0) {
386 return i;
387 }
388 }
389 return -1;
390 }
392 /* all the rest act on the current animation(s) */
394 void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
395 {
396 int i;
397 struct anm_animation *anim = anm_get_active_animation(node, 0);
398 if(!anim) return;
400 for(i=0; i<ANM_NUM_TRACKS; i++) {
401 anm_set_track_interpolator(anim->tracks + i, in);
402 }
403 invalidate_cache(node);
404 }
406 void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
407 {
408 int i;
409 struct anm_animation *anim = anm_get_active_animation(node, 0);
410 if(!anim) return;
412 for(i=0; i<ANM_NUM_TRACKS; i++) {
413 anm_set_track_extrapolator(anim->tracks + i, ex);
414 }
415 invalidate_cache(node);
416 }
418 void anm_set_node_active_animation_name(struct anm_node *node, const char *name)
419 {
420 struct anm_animation *anim = anm_get_active_animation(node, 0);
421 if(!anim) return;
423 anm_set_animation_name(anim, name);
424 }
426 void anm_set_active_animation_name(struct anm_node *node, const char *name)
427 {
428 struct anm_node *child;
430 anm_set_node_active_animation_name(node, name);
432 child = node->child;
433 while(child) {
434 anm_set_active_animation_name(child, name);
435 child = child->next;
436 }
437 }
439 const char *anm_get_active_animation_name(struct anm_node *node)
440 {
441 struct anm_animation *anim = anm_get_active_animation(node, 0);
442 if(anim) {
443 return anim->name;
444 }
445 return 0;
446 }
448 void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
449 {
450 struct anm_animation *anim = anm_get_active_animation(node, 0);
451 if(!anim) return;
453 anm_set_value(anim->tracks + ANM_TRACK_POS_X, tm, pos.x);
454 anm_set_value(anim->tracks + ANM_TRACK_POS_Y, tm, pos.y);
455 anm_set_value(anim->tracks + ANM_TRACK_POS_Z, tm, pos.z);
456 invalidate_cache(node);
457 }
459 vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
460 {
461 vec3_t v;
462 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
463 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
465 if(!anim0) {
466 return v3_cons(0, 0, 0);
467 }
469 v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm);
470 v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm);
471 v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm);
473 if(anim1) {
474 vec3_t v1;
475 v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm);
476 v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm);
477 v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm);
479 v.x = v.x + (v1.x - v.x) * node->cur_mix;
480 v.y = v.y + (v1.y - v.y) * node->cur_mix;
481 v.z = v.z + (v1.z - v.z) * node->cur_mix;
482 }
484 return v;
485 }
487 void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm)
488 {
489 struct anm_animation *anim = anm_get_active_animation(node, 0);
490 if(!anim) return;
492 anm_set_value(anim->tracks + ANM_TRACK_ROT_X, tm, rot.x);
493 anm_set_value(anim->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
494 anm_set_value(anim->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
495 anm_set_value(anim->tracks + ANM_TRACK_ROT_W, tm, rot.w);
496 invalidate_cache(node);
497 }
499 static quat_t get_node_rotation(struct anm_node *node, anm_time_t tm, struct anm_animation *anim)
500 {
501 #ifndef ROT_USE_SLERP
502 quat_t q;
503 q.x = anm_get_value(anim->tracks + ANM_TRACK_ROT_X, tm);
504 q.y = anm_get_value(anim->tracks + ANM_TRACK_ROT_Y, tm);
505 q.z = anm_get_value(anim->tracks + ANM_TRACK_ROT_Z, tm);
506 q.w = anm_get_value(anim->tracks + ANM_TRACK_ROT_W, tm);
507 return q;
508 #else
509 int idx0, idx1, last_idx;
510 anm_time_t tstart, tend;
511 float t, dt;
512 struct anm_track *track_x, *track_y, *track_z, *track_w;
513 quat_t q, q1, q2;
515 track_x = anim->tracks + ANM_TRACK_ROT_X;
516 track_y = anim->tracks + ANM_TRACK_ROT_Y;
517 track_z = anim->tracks + ANM_TRACK_ROT_Z;
518 track_w = anim->tracks + ANM_TRACK_ROT_W;
520 if(!track_x->count) {
521 q.x = track_x->def_val;
522 q.y = track_y->def_val;
523 q.z = track_z->def_val;
524 q.w = track_w->def_val;
525 return q;
526 }
528 last_idx = track_x->count - 1;
530 tstart = track_x->keys[0].time;
531 tend = track_x->keys[last_idx].time;
533 if(tstart == tend) {
534 q.x = track_x->keys[0].val;
535 q.y = track_y->keys[0].val;
536 q.z = track_z->keys[0].val;
537 q.w = track_w->keys[0].val;
538 return q;
539 }
541 tm = anm_remap_time(track_x, tm, tstart, tend);
543 idx0 = anm_get_key_interval(track_x, tm);
544 assert(idx0 >= 0 && idx0 < track_x->count);
545 idx1 = idx0 + 1;
547 if(idx0 == last_idx) {
548 q.x = track_x->keys[idx0].val;
549 q.y = track_y->keys[idx0].val;
550 q.z = track_z->keys[idx0].val;
551 q.w = track_w->keys[idx0].val;
552 return q;
553 }
555 dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time);
556 t = (float)(tm - track_x->keys[idx0].time) / dt;
558 q1.x = track_x->keys[idx0].val;
559 q1.y = track_y->keys[idx0].val;
560 q1.z = track_z->keys[idx0].val;
561 q1.w = track_w->keys[idx0].val;
563 q2.x = track_x->keys[idx1].val;
564 q2.y = track_y->keys[idx1].val;
565 q2.z = track_z->keys[idx1].val;
566 q2.w = track_w->keys[idx1].val;
568 /*q1 = quat_normalize(q1);
569 q2 = quat_normalize(q2);*/
571 return quat_slerp(q1, q2, t);
572 #endif
573 }
575 quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
576 {
577 quat_t q;
578 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
579 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
581 if(!anim0) {
582 return quat_identity();
583 }
585 q = get_node_rotation(node, tm, anim0);
587 if(anim1) {
588 quat_t q1 = get_node_rotation(node, tm, anim1);
590 q = quat_slerp(q, q1, node->cur_mix);
591 }
592 return q;
593 }
595 void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm)
596 {
597 struct anm_animation *anim = anm_get_active_animation(node, 0);
598 if(!anim) return;
600 anm_set_value(anim->tracks + ANM_TRACK_SCL_X, tm, scl.x);
601 anm_set_value(anim->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
602 anm_set_value(anim->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
603 invalidate_cache(node);
604 }
606 vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
607 {
608 vec3_t v;
609 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
610 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
612 if(!anim0) {
613 return v3_cons(1, 1, 1);
614 }
616 v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm);
617 v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm);
618 v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm);
620 if(anim1) {
621 vec3_t v1;
622 v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm);
623 v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm);
624 v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm);
626 v.x = v.x + (v1.x - v.x) * node->cur_mix;
627 v.y = v.y + (v1.y - v.y) * node->cur_mix;
628 v.z = v.z + (v1.z - v.z) * node->cur_mix;
629 }
631 return v;
632 }
635 vec3_t anm_get_position(struct anm_node *node, anm_time_t tm)
636 {
637 mat4_t xform;
638 vec3_t pos = {0.0, 0.0, 0.0};
640 if(!node->parent) {
641 return anm_get_node_position(node, tm);
642 }
644 anm_get_matrix(node, xform, tm);
645 return v3_transform(pos, xform);
646 }
648 quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm)
649 {
650 quat_t rot, prot;
651 rot = anm_get_node_rotation(node, tm);
653 if(!node->parent) {
654 return rot;
655 }
657 prot = anm_get_rotation(node->parent, tm);
658 return quat_mul(prot, rot);
659 }
661 vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm)
662 {
663 vec3_t s, ps;
664 s = anm_get_node_scaling(node, tm);
666 if(!node->parent) {
667 return s;
668 }
670 ps = anm_get_scaling(node->parent, tm);
671 return v3_mul(s, ps);
672 }
674 void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
675 {
676 int i;
677 mat4_t rmat;
678 vec3_t pos, scale;
679 quat_t rot;
681 pos = anm_get_node_position(node, tm);
682 rot = anm_get_node_rotation(node, tm);
683 scale = anm_get_node_scaling(node, tm);
685 m4_set_translation(mat, node->pivot.x, node->pivot.y, node->pivot.z);
687 quat_to_mat4(rmat, rot);
688 for(i=0; i<3; i++) {
689 mat[i][0] = rmat[i][0];
690 mat[i][1] = rmat[i][1];
691 mat[i][2] = rmat[i][2];
692 }
693 /* this loop is equivalent to: m4_mult(mat, mat, rmat); */
695 mat[0][0] *= scale.x; mat[0][1] *= scale.y; mat[0][2] *= scale.z; mat[0][3] += pos.x;
696 mat[1][0] *= scale.x; mat[1][1] *= scale.y; mat[1][2] *= scale.z; mat[1][3] += pos.y;
697 mat[2][0] *= scale.x; mat[2][1] *= scale.y; mat[2][2] *= scale.z; mat[2][3] += pos.z;
699 m4_translate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
701 /* that's basically: pivot * rotation * translation * scaling * -pivot */
702 }
704 void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
705 {
706 mat4_t tmp;
707 anm_get_node_matrix(node, tmp, tm);
708 m4_inverse(mat, tmp);
709 }
711 void anm_eval_node(struct anm_node *node, anm_time_t tm)
712 {
713 anm_get_node_matrix(node, node->matrix, tm);
714 }
716 void anm_eval(struct anm_node *node, anm_time_t tm)
717 {
718 struct anm_node *c;
720 anm_eval_node(node, tm);
722 if(node->parent) {
723 /* due to post-order traversal, the parent matrix is already evaluated */
724 m4_mult(node->matrix, node->parent->matrix, node->matrix);
725 }
727 /* recersively evaluate all children */
728 c = node->child;
729 while(c) {
730 anm_eval(c, tm);
731 c = c->next;
732 }
733 }
735 void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
736 {
737 struct mat_cache *cache = pthread_getspecific(node->cache_key);
738 if(!cache) {
739 cache = malloc(sizeof *cache);
740 assert(cache);
742 pthread_mutex_lock(&node->cache_list_lock);
743 cache->next = node->cache_list;
744 node->cache_list = cache;
745 pthread_mutex_unlock(&node->cache_list_lock);
747 cache->time = ANM_TIME_INVAL;
748 cache->inv_time = ANM_TIME_INVAL;
749 pthread_setspecific(node->cache_key, cache);
750 }
752 if(cache->time != tm) {
753 anm_get_node_matrix(node, cache->matrix, tm);
755 if(node->parent) {
756 mat4_t parent_mat;
758 anm_get_matrix(node->parent, parent_mat, tm);
759 m4_mult(cache->matrix, parent_mat, cache->matrix);
760 }
761 cache->time = tm;
762 }
763 m4_copy(mat, cache->matrix);
764 }
766 void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
767 {
768 struct mat_cache *cache = pthread_getspecific(node->cache_key);
769 if(!cache) {
770 cache = malloc(sizeof *cache);
771 assert(cache);
773 pthread_mutex_lock(&node->cache_list_lock);
774 cache->next = node->cache_list;
775 node->cache_list = cache;
776 pthread_mutex_unlock(&node->cache_list_lock);
778 cache->inv_time = ANM_TIME_INVAL;
779 cache->inv_time = ANM_TIME_INVAL;
780 pthread_setspecific(node->cache_key, cache);
781 }
783 if(cache->inv_time != tm) {
784 anm_get_matrix(node, mat, tm);
785 m4_inverse(cache->inv_matrix, mat);
786 cache->inv_time = tm;
787 }
788 m4_copy(mat, cache->inv_matrix);
789 }
791 anm_time_t anm_get_start_time(struct anm_node *node)
792 {
793 int i, j;
794 struct anm_node *c;
795 anm_time_t res = LONG_MAX;
797 for(j=0; j<2; j++) {
798 struct anm_animation *anim = anm_get_active_animation(node, j);
799 if(!anim) break;
801 for(i=0; i<ANM_NUM_TRACKS; i++) {
802 if(anim->tracks[i].count) {
803 anm_time_t tm = anim->tracks[i].keys[0].time;
804 if(tm < res) {
805 res = tm;
806 }
807 }
808 }
809 }
811 c = node->child;
812 while(c) {
813 anm_time_t tm = anm_get_start_time(c);
814 if(tm < res) {
815 res = tm;
816 }
817 c = c->next;
818 }
819 return res;
820 }
822 anm_time_t anm_get_end_time(struct anm_node *node)
823 {
824 int i, j;
825 struct anm_node *c;
826 anm_time_t res = LONG_MIN;
828 for(j=0; j<2; j++) {
829 struct anm_animation *anim = anm_get_active_animation(node, j);
830 if(!anim) break;
832 for(i=0; i<ANM_NUM_TRACKS; i++) {
833 if(anim->tracks[i].count) {
834 anm_time_t tm = anim->tracks[i].keys[anim->tracks[i].count - 1].time;
835 if(tm > res) {
836 res = tm;
837 }
838 }
839 }
840 }
842 c = node->child;
843 while(c) {
844 anm_time_t tm = anm_get_end_time(c);
845 if(tm > res) {
846 res = tm;
847 }
848 c = c->next;
849 }
850 return res;
851 }
853 static void invalidate_cache(struct anm_node *node)
854 {
855 struct mat_cache *cache = pthread_getspecific(node->cache_key);
856 if(cache) {
857 cache->time = cache->inv_time = ANM_TIME_INVAL;
858 }
859 }