libanim

view src/anim.c @ 22:9758004136f8

added anm_set_animation_name
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 27 Dec 2013 10:58:23 +0200
parents 5993f405a1cb
children 203c11299586
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 free(anim->name);
48 anim->name = newname;
49 }
51 /* ---- node implementation ----- */
53 int anm_init_node(struct anm_node *node)
54 {
55 memset(node, 0, sizeof *node);
57 node->cur_anim[1] = -1;
58 node->cur_mix = 0;
60 if(!(node->animations = dynarr_alloc(1, sizeof *node->animations))) {
61 return -1;
62 }
63 if(anm_init_animation(node->animations) == -1) {
64 dynarr_free(node->animations);
65 return -1;
66 }
68 /* initialize thread-local matrix cache */
69 pthread_key_create(&node->cache_key, 0);
70 pthread_mutex_init(&node->cache_list_lock, 0);
72 return 0;
73 }
75 void anm_destroy_node(struct anm_node *node)
76 {
77 int i;
78 free(node->name);
80 for(i=0; i<ANM_NUM_TRACKS; i++) {
81 anm_destroy_animation(node->animations + i);
82 }
83 dynarr_free(node->animations);
85 /* destroy thread-specific cache */
86 pthread_key_delete(node->cache_key);
88 while(node->cache_list) {
89 struct mat_cache *tmp = node->cache_list;
90 node->cache_list = tmp->next;
91 free(tmp);
92 }
93 }
95 void anm_destroy_node_tree(struct anm_node *tree)
96 {
97 struct anm_node *c, *tmp;
99 if(!tree) return;
101 c = tree->child;
102 while(c) {
103 tmp = c;
104 c = c->next;
106 anm_destroy_node_tree(tmp);
107 }
108 anm_destroy_node(tree);
109 }
111 struct anm_node *anm_create_node(void)
112 {
113 struct anm_node *n;
115 if((n = malloc(sizeof *n))) {
116 if(anm_init_node(n) == -1) {
117 free(n);
118 return 0;
119 }
120 }
121 return n;
122 }
124 void anm_free_node(struct anm_node *node)
125 {
126 anm_destroy_node(node);
127 free(node);
128 }
130 void anm_free_node_tree(struct anm_node *tree)
131 {
132 struct anm_node *c, *tmp;
134 if(!tree) return;
136 c = tree->child;
137 while(c) {
138 tmp = c;
139 c = c->next;
141 anm_free_node_tree(tmp);
142 }
144 anm_free_node(tree);
145 }
147 int anm_set_node_name(struct anm_node *node, const char *name)
148 {
149 char *str;
151 if(!(str = malloc(strlen(name) + 1))) {
152 return -1;
153 }
154 strcpy(str, name);
155 free(node->name);
156 node->name = str;
157 return 0;
158 }
160 const char *anm_get_node_name(struct anm_node *node)
161 {
162 return node->name ? node->name : "";
163 }
165 void anm_link_node(struct anm_node *p, struct anm_node *c)
166 {
167 c->next = p->child;
168 p->child = c;
170 c->parent = p;
171 invalidate_cache(c);
172 }
174 int anm_unlink_node(struct anm_node *p, struct anm_node *c)
175 {
176 struct anm_node *iter;
178 if(p->child == c) {
179 p->child = c->next;
180 c->next = 0;
181 invalidate_cache(c);
182 return 0;
183 }
185 iter = p->child;
186 while(iter->next) {
187 if(iter->next == c) {
188 iter->next = c->next;
189 c->next = 0;
190 invalidate_cache(c);
191 return 0;
192 }
193 }
194 return -1;
195 }
197 void anm_set_pivot(struct anm_node *node, vec3_t piv)
198 {
199 node->pivot = piv;
200 }
202 vec3_t anm_get_pivot(struct anm_node *node)
203 {
204 return node->pivot;
205 }
208 /* animation management */
210 int anm_use_node_animation(struct anm_node *node, int aidx)
211 {
212 if(aidx == node->cur_anim[0] && node->cur_anim[1] == -1) {
213 return 0; /* no change, no invalidation */
214 }
216 if(aidx < 0 || aidx >= anm_get_animation_count(node)) {
217 return -1;
218 }
220 node->cur_anim[0] = aidx;
221 node->cur_anim[1] = -1;
222 node->cur_mix = 0;
224 invalidate_cache(node);
225 return 0;
226 }
228 int anm_use_node_animations(struct anm_node *node, int aidx, int bidx, float t)
229 {
230 int num_anim;
232 if(node->cur_anim[0] == aidx && node->cur_anim[1] == bidx &&
233 fabs(t - node->cur_mix) < 1e-6) {
234 return 0; /* no change, no invalidation */
235 }
237 num_anim = anm_get_animation_count(node);
238 if(aidx < 0 || aidx >= num_anim) {
239 return anm_use_animation(node, bidx);
240 }
241 if(bidx < 0 || bidx >= num_anim) {
242 return anm_use_animation(node, aidx);
243 }
244 node->cur_anim[0] = aidx;
245 node->cur_anim[1] = bidx;
246 node->cur_mix = t;
248 invalidate_cache(node);
249 return 0;
250 }
252 int anm_use_animation(struct anm_node *node, int aidx)
253 {
254 struct anm_node *child;
256 if(anm_use_node_animation(node, aidx) == -1) {
257 return -1;
258 }
260 child = node->child;
261 while(child) {
262 if(anm_use_animation(child, aidx) == -1) {
263 return -1;
264 }
265 child = child->next;
266 }
267 return 0;
268 }
270 int anm_use_animations(struct anm_node *node, int aidx, int bidx, float t)
271 {
272 struct anm_node *child;
274 if(anm_use_node_animations(node, aidx, bidx, t) == -1) {
275 return -1;
276 }
278 child = node->child;
279 while(child) {
280 if(anm_use_animations(child, aidx, bidx, t) == -1) {
281 return -1;
282 }
283 child = child->next;
284 }
285 return 0;
287 }
289 int anm_get_active_animation_index(struct anm_node *node, int which)
290 {
291 if(which < 0 || which >= 2) return -1;
292 return node->cur_anim[which];
293 }
295 struct anm_animation *anm_get_active_animation(struct anm_node *node, int which)
296 {
297 int idx = anm_get_active_animation_index(node, which);
298 if(idx < 0 || idx >= anm_get_animation_count(node)) {
299 return 0;
300 }
301 return node->animations + idx;
302 }
304 float anm_get_active_animation_mix(struct anm_node *node)
305 {
306 return node->cur_mix;
307 }
309 int anm_get_animation_count(struct anm_node *node)
310 {
311 return dynarr_size(node->animations);
312 }
314 int anm_add_node_animation(struct anm_node *node)
315 {
316 struct anm_animation newanim;
317 anm_init_animation(&newanim);
319 node->animations = dynarr_push(node->animations, &newanim);
320 return 0;
321 }
323 int anm_remove_node_animation(struct anm_node *node, int idx)
324 {
325 fprintf(stderr, "anm_remove_animation: unimplemented!");
326 abort();
327 return 0;
328 }
330 int anm_add_animation(struct anm_node *node)
331 {
332 struct anm_node *child;
334 if(anm_add_node_animation(node) == -1) {
335 return -1;
336 }
338 child = node->child;
339 while(child) {
340 if(anm_add_animation(child)) {
341 return -1;
342 }
343 child = child->next;
344 }
345 return 0;
346 }
348 int anm_remove_animation(struct anm_node *node, int idx)
349 {
350 struct anm_node *child;
352 if(anm_remove_node_animation(node, idx) == -1) {
353 return -1;
354 }
356 child = node->child;
357 while(child) {
358 if(anm_remove_animation(child, idx) == -1) {
359 return -1;
360 }
361 child = child->next;
362 }
363 return 0;
364 }
366 struct anm_animation *anm_get_animation(struct anm_node *node, int idx)
367 {
368 if(idx < 0 || idx > anm_get_animation_count(node)) {
369 return 0;
370 }
371 return node->animations + idx;
372 }
374 struct anm_animation *anm_get_animation_by_name(struct anm_node *node, const char *name)
375 {
376 return anm_get_animation(node, anm_find_animation(node, name));
377 }
379 int anm_find_animation(struct anm_node *node, const char *name)
380 {
381 int i, count = anm_get_animation_count(node);
382 for(i=0; i<count; i++) {
383 if(strcmp(node->animations[i].name, name) == 0) {
384 return i;
385 }
386 }
387 return -1;
388 }
390 /* all the rest act on the current animation(s) */
392 void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
393 {
394 int i;
395 struct anm_animation *anim = anm_get_active_animation(node, 0);
396 if(!anim) return;
398 for(i=0; i<ANM_NUM_TRACKS; i++) {
399 anm_set_track_interpolator(anim->tracks + i, in);
400 }
401 invalidate_cache(node);
402 }
404 void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
405 {
406 int i;
407 struct anm_animation *anim = anm_get_active_animation(node, 0);
408 if(!anim) return;
410 for(i=0; i<ANM_NUM_TRACKS; i++) {
411 anm_set_track_extrapolator(anim->tracks + i, ex);
412 }
413 invalidate_cache(node);
414 }
416 void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
417 {
418 struct anm_animation *anim = anm_get_active_animation(node, 0);
419 if(!anim) return;
421 anm_set_value(anim->tracks + ANM_TRACK_POS_X, tm, pos.x);
422 anm_set_value(anim->tracks + ANM_TRACK_POS_Y, tm, pos.y);
423 anm_set_value(anim->tracks + ANM_TRACK_POS_Z, tm, pos.z);
424 invalidate_cache(node);
425 }
427 vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
428 {
429 vec3_t v;
430 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
431 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
433 if(!anim0) {
434 return v3_cons(0, 0, 0);
435 }
437 v.x = anm_get_value(anim0->tracks + ANM_TRACK_POS_X, tm);
438 v.y = anm_get_value(anim0->tracks + ANM_TRACK_POS_Y, tm);
439 v.z = anm_get_value(anim0->tracks + ANM_TRACK_POS_Z, tm);
441 if(anim1) {
442 vec3_t v1;
443 v1.x = anm_get_value(anim1->tracks + ANM_TRACK_POS_X, tm);
444 v1.y = anm_get_value(anim1->tracks + ANM_TRACK_POS_Y, tm);
445 v1.z = anm_get_value(anim1->tracks + ANM_TRACK_POS_Z, tm);
447 v.x = v.x + (v1.x - v.x) * node->cur_mix;
448 v.y = v.y + (v1.y - v.y) * node->cur_mix;
449 v.z = v.z + (v1.z - v.z) * node->cur_mix;
450 }
452 return v;
453 }
455 void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm)
456 {
457 struct anm_animation *anim = anm_get_active_animation(node, 0);
458 if(!anim) return;
460 anm_set_value(anim->tracks + ANM_TRACK_ROT_X, tm, rot.x);
461 anm_set_value(anim->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
462 anm_set_value(anim->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
463 anm_set_value(anim->tracks + ANM_TRACK_ROT_W, tm, rot.w);
464 invalidate_cache(node);
465 }
467 static quat_t get_node_rotation(struct anm_node *node, anm_time_t tm, struct anm_animation *anim)
468 {
469 #ifndef ROT_USE_SLERP
470 quat_t q;
471 q.x = anm_get_value(anim->tracks + ANM_TRACK_ROT_X, tm);
472 q.y = anm_get_value(anim->tracks + ANM_TRACK_ROT_Y, tm);
473 q.z = anm_get_value(anim->tracks + ANM_TRACK_ROT_Z, tm);
474 q.w = anm_get_value(anim->tracks + ANM_TRACK_ROT_W, tm);
475 return q;
476 #else
477 int idx0, idx1, last_idx;
478 anm_time_t tstart, tend;
479 float t, dt;
480 struct anm_track *track_x, *track_y, *track_z, *track_w;
481 quat_t q, q1, q2;
483 track_x = anim->tracks + ANM_TRACK_ROT_X;
484 track_y = anim->tracks + ANM_TRACK_ROT_Y;
485 track_z = anim->tracks + ANM_TRACK_ROT_Z;
486 track_w = anim->tracks + ANM_TRACK_ROT_W;
488 if(!track_x->count) {
489 q.x = track_x->def_val;
490 q.y = track_y->def_val;
491 q.z = track_z->def_val;
492 q.w = track_w->def_val;
493 return q;
494 }
496 last_idx = track_x->count - 1;
498 tstart = track_x->keys[0].time;
499 tend = track_x->keys[last_idx].time;
501 if(tstart == tend) {
502 q.x = track_x->keys[0].val;
503 q.y = track_y->keys[0].val;
504 q.z = track_z->keys[0].val;
505 q.w = track_w->keys[0].val;
506 return q;
507 }
509 tm = anm_remap_time(track_x, tm, tstart, tend);
511 idx0 = anm_get_key_interval(track_x, tm);
512 assert(idx0 >= 0 && idx0 < track_x->count);
513 idx1 = idx0 + 1;
515 if(idx0 == last_idx) {
516 q.x = track_x->keys[idx0].val;
517 q.y = track_y->keys[idx0].val;
518 q.z = track_z->keys[idx0].val;
519 q.w = track_w->keys[idx0].val;
520 return q;
521 }
523 dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time);
524 t = (float)(tm - track_x->keys[idx0].time) / dt;
526 q1.x = track_x->keys[idx0].val;
527 q1.y = track_y->keys[idx0].val;
528 q1.z = track_z->keys[idx0].val;
529 q1.w = track_w->keys[idx0].val;
531 q2.x = track_x->keys[idx1].val;
532 q2.y = track_y->keys[idx1].val;
533 q2.z = track_z->keys[idx1].val;
534 q2.w = track_w->keys[idx1].val;
536 /*q1 = quat_normalize(q1);
537 q2 = quat_normalize(q2);*/
539 return quat_slerp(q1, q2, t);
540 #endif
541 }
543 quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
544 {
545 quat_t q;
546 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
547 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
549 if(!anim0) {
550 return quat_identity();
551 }
553 q = get_node_rotation(node, tm, anim0);
555 if(anim1) {
556 quat_t q1 = get_node_rotation(node, tm, anim1);
558 q = quat_slerp(q, q1, node->cur_mix);
559 }
560 return q;
561 }
563 void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm)
564 {
565 struct anm_animation *anim = anm_get_active_animation(node, 0);
566 if(!anim) return;
568 anm_set_value(anim->tracks + ANM_TRACK_SCL_X, tm, scl.x);
569 anm_set_value(anim->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
570 anm_set_value(anim->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
571 invalidate_cache(node);
572 }
574 vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
575 {
576 vec3_t v;
577 struct anm_animation *anim0 = anm_get_active_animation(node, 0);
578 struct anm_animation *anim1 = anm_get_active_animation(node, 1);
580 if(!anim0) {
581 return v3_cons(1, 1, 1);
582 }
584 v.x = anm_get_value(anim0->tracks + ANM_TRACK_SCL_X, tm);
585 v.y = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Y, tm);
586 v.z = anm_get_value(anim0->tracks + ANM_TRACK_SCL_Z, tm);
588 if(anim1) {
589 vec3_t v1;
590 v1.x = anm_get_value(anim1->tracks + ANM_TRACK_SCL_X, tm);
591 v1.y = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Y, tm);
592 v1.z = anm_get_value(anim1->tracks + ANM_TRACK_SCL_Z, tm);
594 v.x = v.x + (v1.x - v.x) * node->cur_mix;
595 v.y = v.y + (v1.y - v.y) * node->cur_mix;
596 v.z = v.z + (v1.z - v.z) * node->cur_mix;
597 }
599 return v;
600 }
603 vec3_t anm_get_position(struct anm_node *node, anm_time_t tm)
604 {
605 mat4_t xform;
606 vec3_t pos = {0.0, 0.0, 0.0};
608 if(!node->parent) {
609 return anm_get_node_position(node, tm);
610 }
612 anm_get_matrix(node, xform, tm);
613 return v3_transform(pos, xform);
614 }
616 quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm)
617 {
618 quat_t rot, prot;
619 rot = anm_get_node_rotation(node, tm);
621 if(!node->parent) {
622 return rot;
623 }
625 prot = anm_get_rotation(node->parent, tm);
626 return quat_mul(prot, rot);
627 }
629 vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm)
630 {
631 vec3_t s, ps;
632 s = anm_get_node_scaling(node, tm);
634 if(!node->parent) {
635 return s;
636 }
638 ps = anm_get_scaling(node->parent, tm);
639 return v3_mul(s, ps);
640 }
642 void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
643 {
644 int i;
645 mat4_t rmat;
646 vec3_t pos, scale;
647 quat_t rot;
649 pos = anm_get_node_position(node, tm);
650 rot = anm_get_node_rotation(node, tm);
651 scale = anm_get_node_scaling(node, tm);
653 m4_set_translation(mat, node->pivot.x, node->pivot.y, node->pivot.z);
655 quat_to_mat4(rmat, rot);
656 for(i=0; i<3; i++) {
657 mat[i][0] = rmat[i][0];
658 mat[i][1] = rmat[i][1];
659 mat[i][2] = rmat[i][2];
660 }
661 /* this loop is equivalent to: m4_mult(mat, mat, rmat); */
663 mat[0][0] *= scale.x; mat[0][1] *= scale.y; mat[0][2] *= scale.z; mat[0][3] += pos.x;
664 mat[1][0] *= scale.x; mat[1][1] *= scale.y; mat[1][2] *= scale.z; mat[1][3] += pos.y;
665 mat[2][0] *= scale.x; mat[2][1] *= scale.y; mat[2][2] *= scale.z; mat[2][3] += pos.z;
667 m4_translate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
669 /* that's basically: pivot * rotation * translation * scaling * -pivot */
670 }
672 void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
673 {
674 mat4_t tmp;
675 anm_get_node_matrix(node, tmp, tm);
676 m4_inverse(mat, tmp);
677 }
679 void anm_eval_node(struct anm_node *node, anm_time_t tm)
680 {
681 anm_get_node_matrix(node, node->matrix, tm);
682 }
684 void anm_eval(struct anm_node *node, anm_time_t tm)
685 {
686 struct anm_node *c;
688 anm_eval_node(node, tm);
690 if(node->parent) {
691 /* due to post-order traversal, the parent matrix is already evaluated */
692 m4_mult(node->matrix, node->parent->matrix, node->matrix);
693 }
695 /* recersively evaluate all children */
696 c = node->child;
697 while(c) {
698 anm_eval(c, tm);
699 c = c->next;
700 }
701 }
703 void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
704 {
705 struct mat_cache *cache = pthread_getspecific(node->cache_key);
706 if(!cache) {
707 cache = malloc(sizeof *cache);
708 assert(cache);
710 pthread_mutex_lock(&node->cache_list_lock);
711 cache->next = node->cache_list;
712 node->cache_list = cache;
713 pthread_mutex_unlock(&node->cache_list_lock);
715 cache->time = ANM_TIME_INVAL;
716 cache->inv_time = ANM_TIME_INVAL;
717 pthread_setspecific(node->cache_key, cache);
718 }
720 if(cache->time != tm) {
721 anm_get_node_matrix(node, cache->matrix, tm);
723 if(node->parent) {
724 mat4_t parent_mat;
726 anm_get_matrix(node->parent, parent_mat, tm);
727 m4_mult(cache->matrix, parent_mat, cache->matrix);
728 }
729 cache->time = tm;
730 }
731 m4_copy(mat, cache->matrix);
732 }
734 void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
735 {
736 struct mat_cache *cache = pthread_getspecific(node->cache_key);
737 if(!cache) {
738 cache = malloc(sizeof *cache);
739 assert(cache);
741 pthread_mutex_lock(&node->cache_list_lock);
742 cache->next = node->cache_list;
743 node->cache_list = cache;
744 pthread_mutex_unlock(&node->cache_list_lock);
746 cache->inv_time = ANM_TIME_INVAL;
747 cache->inv_time = ANM_TIME_INVAL;
748 pthread_setspecific(node->cache_key, cache);
749 }
751 if(cache->inv_time != tm) {
752 anm_get_matrix(node, mat, tm);
753 m4_inverse(cache->inv_matrix, mat);
754 cache->inv_time = tm;
755 }
756 m4_copy(mat, cache->inv_matrix);
757 }
759 anm_time_t anm_get_start_time(struct anm_node *node)
760 {
761 int i, j;
762 struct anm_node *c;
763 anm_time_t res = LONG_MAX;
765 for(j=0; j<2; j++) {
766 struct anm_animation *anim = anm_get_active_animation(node, j);
767 if(!anim) break;
769 for(i=0; i<ANM_NUM_TRACKS; i++) {
770 if(anim->tracks[i].count) {
771 anm_time_t tm = anim->tracks[i].keys[0].time;
772 if(tm < res) {
773 res = tm;
774 }
775 }
776 }
777 }
779 c = node->child;
780 while(c) {
781 anm_time_t tm = anm_get_start_time(c);
782 if(tm < res) {
783 res = tm;
784 }
785 c = c->next;
786 }
787 return res;
788 }
790 anm_time_t anm_get_end_time(struct anm_node *node)
791 {
792 int i, j;
793 struct anm_node *c;
794 anm_time_t res = LONG_MIN;
796 for(j=0; j<2; j++) {
797 struct anm_animation *anim = anm_get_active_animation(node, j);
798 if(!anim) break;
800 for(i=0; i<ANM_NUM_TRACKS; i++) {
801 if(anim->tracks[i].count) {
802 anm_time_t tm = anim->tracks[i].keys[anim->tracks[i].count - 1].time;
803 if(tm > res) {
804 res = tm;
805 }
806 }
807 }
808 }
810 c = node->child;
811 while(c) {
812 anm_time_t tm = anm_get_end_time(c);
813 if(tm > res) {
814 res = tm;
815 }
816 c = c->next;
817 }
818 return res;
819 }
821 static void invalidate_cache(struct anm_node *node)
822 {
823 struct mat_cache *cache = pthread_getspecific(node->cache_key);
824 if(cache) {
825 cache->time = cache->inv_time = ANM_TIME_INVAL;
826 }
827 }