rev |
line source |
nuclear@67
|
1 #include <limits.h>
|
nuclear@67
|
2 #include <assert.h>
|
nuclear@67
|
3 #include "anim.h"
|
nuclear@67
|
4 #include "dynarr.h"
|
nuclear@67
|
5
|
nuclear@67
|
6 int anm_init_node(struct anm_node *node)
|
nuclear@67
|
7 {
|
nuclear@67
|
8 int i, j;
|
nuclear@67
|
9 static const float defaults[] = {
|
nuclear@67
|
10 0.0f, 0.0f, 0.0f, /* default position */
|
nuclear@67
|
11 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */
|
nuclear@67
|
12 1.0f, 1.0f, 1.0f /* default scale factor */
|
nuclear@67
|
13 };
|
nuclear@67
|
14
|
nuclear@67
|
15 memset(node, 0, sizeof *node);
|
nuclear@67
|
16
|
nuclear@67
|
17 for(i=0; i<ANM_NUM_TRACKS; i++) {
|
nuclear@67
|
18 if(anm_init_track(node->tracks + i) == -1) {
|
nuclear@67
|
19 for(j=0; j<i; j++) {
|
nuclear@67
|
20 anm_destroy_track(node->tracks + i);
|
nuclear@67
|
21 }
|
nuclear@67
|
22 }
|
nuclear@67
|
23 anm_set_track_default(node->tracks + i, defaults[i]);
|
nuclear@67
|
24 }
|
nuclear@67
|
25 return 0;
|
nuclear@67
|
26 }
|
nuclear@67
|
27
|
nuclear@67
|
28 void anm_destroy_node(struct anm_node *node)
|
nuclear@67
|
29 {
|
nuclear@67
|
30 int i;
|
nuclear@67
|
31 free(node->name);
|
nuclear@67
|
32
|
nuclear@67
|
33 for(i=0; i<ANM_NUM_TRACKS; i++) {
|
nuclear@67
|
34 anm_destroy_track(node->tracks + i);
|
nuclear@67
|
35 }
|
nuclear@67
|
36 }
|
nuclear@67
|
37
|
nuclear@67
|
38 void anm_destroy_node_tree(struct anm_node *tree)
|
nuclear@67
|
39 {
|
nuclear@67
|
40 struct anm_node *c, *tmp;
|
nuclear@67
|
41
|
nuclear@67
|
42 if(!tree) return;
|
nuclear@67
|
43
|
nuclear@67
|
44 c = tree->child;
|
nuclear@67
|
45 while(c) {
|
nuclear@67
|
46 tmp = c;
|
nuclear@67
|
47 c = c->next;
|
nuclear@67
|
48
|
nuclear@67
|
49 anm_destroy_node_tree(tmp);
|
nuclear@67
|
50 }
|
nuclear@67
|
51 anm_destroy_node(tree);
|
nuclear@67
|
52 }
|
nuclear@67
|
53
|
nuclear@67
|
54 struct anm_node *anm_create_node(void)
|
nuclear@67
|
55 {
|
nuclear@67
|
56 struct anm_node *n;
|
nuclear@67
|
57
|
nuclear@67
|
58 if((n = malloc(sizeof *n))) {
|
nuclear@67
|
59 if(anm_init_node(n) == -1) {
|
nuclear@67
|
60 free(n);
|
nuclear@67
|
61 return 0;
|
nuclear@67
|
62 }
|
nuclear@67
|
63 }
|
nuclear@67
|
64 return n;
|
nuclear@67
|
65 }
|
nuclear@67
|
66
|
nuclear@67
|
67 void anm_free_node(struct anm_node *node)
|
nuclear@67
|
68 {
|
nuclear@67
|
69 anm_destroy_node(node);
|
nuclear@67
|
70 free(node);
|
nuclear@67
|
71 }
|
nuclear@67
|
72
|
nuclear@67
|
73 void anm_free_node_tree(struct anm_node *tree)
|
nuclear@67
|
74 {
|
nuclear@67
|
75 struct anm_node *c, *tmp;
|
nuclear@67
|
76
|
nuclear@67
|
77 if(!tree) return;
|
nuclear@67
|
78
|
nuclear@67
|
79 c = tree->child;
|
nuclear@67
|
80 while(c) {
|
nuclear@67
|
81 tmp = c;
|
nuclear@67
|
82 c = c->next;
|
nuclear@67
|
83
|
nuclear@67
|
84 anm_free_node_tree(tmp);
|
nuclear@67
|
85 }
|
nuclear@67
|
86
|
nuclear@67
|
87 anm_free_node(tree);
|
nuclear@67
|
88 }
|
nuclear@67
|
89
|
nuclear@67
|
90 int anm_set_node_name(struct anm_node *node, const char *name)
|
nuclear@67
|
91 {
|
nuclear@67
|
92 char *str;
|
nuclear@67
|
93
|
nuclear@67
|
94 if(!(str = malloc(strlen(name) + 1))) {
|
nuclear@67
|
95 return -1;
|
nuclear@67
|
96 }
|
nuclear@67
|
97 strcpy(str, name);
|
nuclear@67
|
98 free(node->name);
|
nuclear@67
|
99 node->name = str;
|
nuclear@67
|
100 return 0;
|
nuclear@67
|
101 }
|
nuclear@67
|
102
|
nuclear@67
|
103 const char *anm_get_node_name(struct anm_node *node)
|
nuclear@67
|
104 {
|
nuclear@67
|
105 return node->name ? node->name : "";
|
nuclear@67
|
106 }
|
nuclear@67
|
107
|
nuclear@67
|
108 void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
|
nuclear@67
|
109 {
|
nuclear@67
|
110 int i;
|
nuclear@67
|
111
|
nuclear@67
|
112 for(i=0; i<ANM_NUM_TRACKS; i++) {
|
nuclear@67
|
113 anm_set_track_interpolator(node->tracks + i, in);
|
nuclear@67
|
114 }
|
nuclear@67
|
115 }
|
nuclear@67
|
116
|
nuclear@67
|
117 void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
|
nuclear@67
|
118 {
|
nuclear@67
|
119 int i;
|
nuclear@67
|
120
|
nuclear@67
|
121 for(i=0; i<ANM_NUM_TRACKS; i++) {
|
nuclear@67
|
122 anm_set_track_extrapolator(node->tracks + i, ex);
|
nuclear@67
|
123 }
|
nuclear@67
|
124 }
|
nuclear@67
|
125
|
nuclear@67
|
126 void anm_link_node(struct anm_node *p, struct anm_node *c)
|
nuclear@67
|
127 {
|
nuclear@67
|
128 c->next = p->child;
|
nuclear@67
|
129 p->child = c;
|
nuclear@67
|
130
|
nuclear@67
|
131 c->parent = p;
|
nuclear@67
|
132 }
|
nuclear@67
|
133
|
nuclear@67
|
134 int anm_unlink_node(struct anm_node *p, struct anm_node *c)
|
nuclear@67
|
135 {
|
nuclear@67
|
136 struct anm_node *iter;
|
nuclear@67
|
137
|
nuclear@67
|
138 if(p->child == c) {
|
nuclear@67
|
139 p->child = c->next;
|
nuclear@67
|
140 c->next = 0;
|
nuclear@67
|
141 return 0;
|
nuclear@67
|
142 }
|
nuclear@67
|
143
|
nuclear@67
|
144 iter = p->child;
|
nuclear@67
|
145 while(iter->next) {
|
nuclear@67
|
146 if(iter->next == c) {
|
nuclear@67
|
147 iter->next = c->next;
|
nuclear@67
|
148 c->next = 0;
|
nuclear@67
|
149 return 0;
|
nuclear@67
|
150 }
|
nuclear@67
|
151 }
|
nuclear@67
|
152 return -1;
|
nuclear@67
|
153 }
|
nuclear@67
|
154
|
nuclear@67
|
155 void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
|
nuclear@67
|
156 {
|
nuclear@67
|
157 anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x);
|
nuclear@67
|
158 anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y);
|
nuclear@67
|
159 anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z);
|
nuclear@67
|
160 }
|
nuclear@67
|
161
|
nuclear@67
|
162 vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
|
nuclear@67
|
163 {
|
nuclear@67
|
164 vec3_t v;
|
nuclear@67
|
165 v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm);
|
nuclear@67
|
166 v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm);
|
nuclear@67
|
167 v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm);
|
nuclear@67
|
168 return v;
|
nuclear@67
|
169 }
|
nuclear@67
|
170
|
nuclear@67
|
171 void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm)
|
nuclear@67
|
172 {
|
nuclear@67
|
173 anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x);
|
nuclear@67
|
174 anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
|
nuclear@67
|
175 anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
|
nuclear@67
|
176 anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w);
|
nuclear@67
|
177 }
|
nuclear@67
|
178
|
nuclear@67
|
179 quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
|
nuclear@67
|
180 {
|
nuclear@67
|
181 int idx0, idx1, last_idx;
|
nuclear@67
|
182 anm_time_t tstart, tend;
|
nuclear@67
|
183 float t, dt;
|
nuclear@67
|
184 struct anm_track *track_x, *track_y, *track_z, *track_w;
|
nuclear@67
|
185 quat_t q, q1, q2;
|
nuclear@67
|
186
|
nuclear@67
|
187 track_x = node->tracks + ANM_TRACK_ROT_X;
|
nuclear@67
|
188 track_y = node->tracks + ANM_TRACK_ROT_Y;
|
nuclear@67
|
189 track_z = node->tracks + ANM_TRACK_ROT_Z;
|
nuclear@67
|
190 track_w = node->tracks + ANM_TRACK_ROT_W;
|
nuclear@67
|
191
|
nuclear@67
|
192 if(!track_x->count) {
|
nuclear@67
|
193 q.x = track_x->def_val;
|
nuclear@67
|
194 q.y = track_y->def_val;
|
nuclear@67
|
195 q.z = track_z->def_val;
|
nuclear@67
|
196 q.w = track_w->def_val;
|
nuclear@67
|
197 return q;
|
nuclear@67
|
198 }
|
nuclear@67
|
199
|
nuclear@67
|
200 last_idx = track_x->count - 1;
|
nuclear@67
|
201
|
nuclear@67
|
202 tstart = track_x->keys[0].time;
|
nuclear@67
|
203 tend = track_x->keys[last_idx].time;
|
nuclear@67
|
204 tm = anm_remap_time(track_x, tm, tstart, tend);
|
nuclear@67
|
205
|
nuclear@67
|
206 idx0 = anm_get_key_interval(track_x, tm);
|
nuclear@67
|
207 assert(idx0 >= 0 && idx0 < track_x->count);
|
nuclear@67
|
208 idx1 = idx0 + 1;
|
nuclear@67
|
209
|
nuclear@67
|
210 dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time);
|
nuclear@67
|
211 t = (float)(tm - track_x->keys[idx0].time) / dt;
|
nuclear@67
|
212
|
nuclear@67
|
213 q1.x = track_x->keys[idx0].val;
|
nuclear@67
|
214 q1.y = track_y->keys[idx0].val;
|
nuclear@67
|
215 q1.z = track_z->keys[idx0].val;
|
nuclear@67
|
216 q1.w = track_w->keys[idx0].val;
|
nuclear@67
|
217
|
nuclear@67
|
218 q2.x = track_x->keys[idx1].val;
|
nuclear@67
|
219 q2.y = track_y->keys[idx1].val;
|
nuclear@67
|
220 q2.z = track_z->keys[idx1].val;
|
nuclear@67
|
221 q2.w = track_w->keys[idx1].val;
|
nuclear@67
|
222
|
nuclear@67
|
223 return quat_slerp(q1, q2, t);
|
nuclear@67
|
224 }
|
nuclear@67
|
225
|
nuclear@67
|
226 void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm)
|
nuclear@67
|
227 {
|
nuclear@67
|
228 anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x);
|
nuclear@67
|
229 anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
|
nuclear@67
|
230 anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
|
nuclear@67
|
231 }
|
nuclear@67
|
232
|
nuclear@67
|
233 vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
|
nuclear@67
|
234 {
|
nuclear@67
|
235 vec3_t v;
|
nuclear@67
|
236 v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm);
|
nuclear@67
|
237 v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm);
|
nuclear@67
|
238 v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm);
|
nuclear@67
|
239 return v;
|
nuclear@67
|
240 }
|
nuclear@67
|
241
|
nuclear@67
|
242
|
nuclear@67
|
243 vec3_t anm_get_position(struct anm_node *node, anm_time_t tm)
|
nuclear@67
|
244 {
|
nuclear@67
|
245 mat4_t xform;
|
nuclear@67
|
246 vec3_t pos = {0.0, 0.0, 0.0};
|
nuclear@67
|
247
|
nuclear@67
|
248 if(!node->parent) {
|
nuclear@67
|
249 return anm_get_node_position(node, tm);
|
nuclear@67
|
250 }
|
nuclear@67
|
251
|
nuclear@67
|
252 anm_get_matrix(node, xform, tm);
|
nuclear@67
|
253 return v3_transform(pos, xform);
|
nuclear@67
|
254 }
|
nuclear@67
|
255
|
nuclear@67
|
256 quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm)
|
nuclear@67
|
257 {
|
nuclear@67
|
258 quat_t rot, prot;
|
nuclear@67
|
259 rot = anm_get_node_rotation(node, tm);
|
nuclear@67
|
260
|
nuclear@67
|
261 if(!node->parent) {
|
nuclear@67
|
262 return rot;
|
nuclear@67
|
263 }
|
nuclear@67
|
264
|
nuclear@67
|
265 prot = anm_get_rotation(node->parent, tm);
|
nuclear@67
|
266 return quat_mul(prot, rot);
|
nuclear@67
|
267 }
|
nuclear@67
|
268
|
nuclear@67
|
269 vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm)
|
nuclear@67
|
270 {
|
nuclear@67
|
271 vec3_t s, ps;
|
nuclear@67
|
272 s = anm_get_node_scaling(node, tm);
|
nuclear@67
|
273
|
nuclear@67
|
274 if(!node->parent) {
|
nuclear@67
|
275 return s;
|
nuclear@67
|
276 }
|
nuclear@67
|
277
|
nuclear@67
|
278 ps = anm_get_scaling(node->parent, tm);
|
nuclear@67
|
279 return v3_mul(s, ps);
|
nuclear@67
|
280 }
|
nuclear@67
|
281
|
nuclear@67
|
282 void anm_set_pivot(struct anm_node *node, vec3_t piv)
|
nuclear@67
|
283 {
|
nuclear@67
|
284 node->pivot = piv;
|
nuclear@67
|
285 }
|
nuclear@67
|
286
|
nuclear@67
|
287 vec3_t anm_get_pivot(struct anm_node *node)
|
nuclear@67
|
288 {
|
nuclear@67
|
289 return node->pivot;
|
nuclear@67
|
290 }
|
nuclear@67
|
291
|
nuclear@67
|
292 void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
|
nuclear@67
|
293 {
|
nuclear@67
|
294 #ifdef ANIM_THREAD_SAFE
|
nuclear@67
|
295 /* XXX we're holding the mutex for way too anm_time_t... but it looks like the
|
nuclear@67
|
296 * alternative would be to lock/unlock twice which might be worse.
|
nuclear@67
|
297 */
|
nuclear@67
|
298 pthread_mutex_lock(node->cache_mutex);
|
nuclear@67
|
299 #endif
|
nuclear@67
|
300
|
nuclear@67
|
301 if(node->cache_time != tm) {
|
nuclear@67
|
302 mat4_t tmat, rmat, smat, pivmat, neg_pivmat;
|
nuclear@67
|
303 vec3_t pos, scale;
|
nuclear@67
|
304 quat_t rot;
|
nuclear@67
|
305
|
nuclear@67
|
306 m4_identity(tmat);
|
nuclear@67
|
307 /*no need to m4_identity(rmat); quat_to_mat4 sets this properly */
|
nuclear@67
|
308 m4_identity(smat);
|
nuclear@67
|
309 m4_identity(pivmat);
|
nuclear@67
|
310 m4_identity(neg_pivmat);
|
nuclear@67
|
311
|
nuclear@67
|
312 pos = anm_get_node_position(node, tm);
|
nuclear@67
|
313 rot = anm_get_node_rotation(node, tm);
|
nuclear@67
|
314 scale = anm_get_node_scaling(node, tm);
|
nuclear@67
|
315
|
nuclear@67
|
316 m4_translate(pivmat, node->pivot.x, node->pivot.y, node->pivot.z);
|
nuclear@67
|
317 m4_translate(neg_pivmat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
|
nuclear@67
|
318
|
nuclear@67
|
319 m4_translate(tmat, pos.x, pos.y, pos.z);
|
nuclear@67
|
320 quat_to_mat4(rmat, rot);
|
nuclear@67
|
321 m4_translate(smat, scale.x, scale.y, scale.z);
|
nuclear@67
|
322
|
nuclear@67
|
323 /* ok this would look nicer in C++ */
|
nuclear@67
|
324 m4_mult(node->cache_matrix, pivmat, tmat);
|
nuclear@67
|
325 m4_mult(node->cache_matrix, node->cache_matrix, rmat);
|
nuclear@67
|
326 m4_mult(node->cache_matrix, node->cache_matrix, smat);
|
nuclear@67
|
327 m4_mult(node->cache_matrix, node->cache_matrix, neg_pivmat);
|
nuclear@67
|
328
|
nuclear@67
|
329 if(node->parent) {
|
nuclear@67
|
330 mat4_t parent_mat;
|
nuclear@67
|
331
|
nuclear@67
|
332 anm_get_matrix(node->parent, mat, tm);
|
nuclear@67
|
333 m4_mult(node->cache_matrix, parent_mat, node->cache_matrix);
|
nuclear@67
|
334 }
|
nuclear@67
|
335 node->cache_time = tm;
|
nuclear@67
|
336 }
|
nuclear@67
|
337 m4_copy(mat, node->cache_matrix);
|
nuclear@67
|
338
|
nuclear@67
|
339 #ifdef ANIM_THREAD_SAFE
|
nuclear@67
|
340 pthread_mutex_unlock(node->cache_mutex);
|
nuclear@67
|
341 #endif
|
nuclear@67
|
342 }
|
nuclear@67
|
343
|
nuclear@67
|
344 anm_time_t anm_get_start_time(struct anm_node *node)
|
nuclear@67
|
345 {
|
nuclear@67
|
346 int i;
|
nuclear@67
|
347 struct anm_node *c;
|
nuclear@67
|
348 anm_time_t res = LONG_MAX;
|
nuclear@67
|
349
|
nuclear@67
|
350 for(i=0; i<ANM_NUM_TRACKS; i++) {
|
nuclear@67
|
351 if(node->tracks[i].count) {
|
nuclear@67
|
352 anm_time_t tm = node->tracks[i].keys[0].time;
|
nuclear@67
|
353 if(tm < res) {
|
nuclear@67
|
354 res = tm;
|
nuclear@67
|
355 }
|
nuclear@67
|
356 }
|
nuclear@67
|
357 }
|
nuclear@67
|
358
|
nuclear@67
|
359 c = node->child;
|
nuclear@67
|
360 while(c) {
|
nuclear@67
|
361 anm_time_t tm = anm_get_start_time(c);
|
nuclear@67
|
362 if(tm < res) {
|
nuclear@67
|
363 res = tm;
|
nuclear@67
|
364 }
|
nuclear@67
|
365 c = c->next;
|
nuclear@67
|
366 }
|
nuclear@67
|
367 return res;
|
nuclear@67
|
368 }
|
nuclear@67
|
369
|
nuclear@67
|
370 anm_time_t anm_get_end_time(struct anm_node *node)
|
nuclear@67
|
371 {
|
nuclear@67
|
372 int i;
|
nuclear@67
|
373 struct anm_node *c;
|
nuclear@67
|
374 anm_time_t res = LONG_MIN;
|
nuclear@67
|
375
|
nuclear@67
|
376 for(i=0; i<ANM_NUM_TRACKS; i++) {
|
nuclear@67
|
377 if(node->tracks[i].count) {
|
nuclear@67
|
378 anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time;
|
nuclear@67
|
379 if(tm > res) {
|
nuclear@67
|
380 res = tm;
|
nuclear@67
|
381 }
|
nuclear@67
|
382 }
|
nuclear@67
|
383 }
|
nuclear@67
|
384
|
nuclear@67
|
385 c = node->child;
|
nuclear@67
|
386 while(c) {
|
nuclear@67
|
387 anm_time_t tm = anm_get_end_time(c);
|
nuclear@67
|
388 if(tm > res) {
|
nuclear@67
|
389 res = tm;
|
nuclear@67
|
390 }
|
nuclear@67
|
391 c = c->next;
|
nuclear@67
|
392 }
|
nuclear@67
|
393 return res;
|
nuclear@67
|
394 }
|