vrshoot
diff libs/anim/track.c @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/anim/track.c Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,331 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <assert.h> 1.8 +#include "track.h" 1.9 +#include "dynarr.h" 1.10 + 1.11 +static int keycmp(const void *a, const void *b); 1.12 +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm); 1.13 + 1.14 +static float interp_step(float v0, float v1, float v2, float v3, float t); 1.15 +static float interp_linear(float v0, float v1, float v2, float v3, float t); 1.16 +static float interp_cubic(float v0, float v1, float v2, float v3, float t); 1.17 + 1.18 +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end); 1.19 +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end); 1.20 +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end); 1.21 +static anm_time_t remap_pingpong(anm_time_t tm, anm_time_t start, anm_time_t end); 1.22 + 1.23 +/* XXX keep this in sync with enum anm_interpolator at track.h */ 1.24 +static float (*interp[])(float, float, float, float, float) = { 1.25 + interp_step, 1.26 + interp_linear, 1.27 + interp_cubic, 1.28 + 0 1.29 +}; 1.30 + 1.31 +/* XXX keep this in sync with enum anm_extrapolator at track.h */ 1.32 +static anm_time_t (*remap_time[])(anm_time_t, anm_time_t, anm_time_t) = { 1.33 + remap_extend, 1.34 + remap_clamp, 1.35 + remap_repeat, 1.36 + remap_pingpong, 1.37 + 0 1.38 +}; 1.39 + 1.40 +int anm_init_track(struct anm_track *track) 1.41 +{ 1.42 + memset(track, 0, sizeof *track); 1.43 + 1.44 + if(!(track->keys = dynarr_alloc(0, sizeof *track->keys))) { 1.45 + return -1; 1.46 + } 1.47 + track->interp = ANM_INTERP_LINEAR; 1.48 + track->extrap = ANM_EXTRAP_CLAMP; 1.49 + return 0; 1.50 +} 1.51 + 1.52 +void anm_destroy_track(struct anm_track *track) 1.53 +{ 1.54 + dynarr_free(track->keys); 1.55 +} 1.56 + 1.57 +struct anm_track *anm_create_track(void) 1.58 +{ 1.59 + struct anm_track *track; 1.60 + 1.61 + if((track = malloc(sizeof *track))) { 1.62 + if(anm_init_track(track) == -1) { 1.63 + free(track); 1.64 + return 0; 1.65 + } 1.66 + } 1.67 + return track; 1.68 +} 1.69 + 1.70 +void anm_free_track(struct anm_track *track) 1.71 +{ 1.72 + anm_destroy_track(track); 1.73 + free(track); 1.74 +} 1.75 + 1.76 +void anm_copy_track(struct anm_track *dest, const struct anm_track *src) 1.77 +{ 1.78 + free(dest->name); 1.79 + if(dest->keys) { 1.80 + dynarr_free(dest->keys); 1.81 + } 1.82 + 1.83 + if(src->name) { 1.84 + dest->name = malloc(strlen(src->name) + 1); 1.85 + strcpy(dest->name, src->name); 1.86 + } 1.87 + 1.88 + dest->count = src->count; 1.89 + dest->keys = dynarr_alloc(src->count, sizeof *dest->keys); 1.90 + memcpy(dest->keys, src->keys, src->count * sizeof *dest->keys); 1.91 + 1.92 + dest->def_val = src->def_val; 1.93 + dest->interp = src->interp; 1.94 + dest->extrap = src->extrap; 1.95 +} 1.96 + 1.97 +void anm_clear_track(struct anm_track *track) 1.98 +{ 1.99 + if(track->keys) { 1.100 + dynarr_free(track->keys); 1.101 + } 1.102 + track->keys = 0; 1.103 + track->count = 0; 1.104 + 1.105 + if(!(track->keys = dynarr_alloc(0, sizeof *track->keys))) { 1.106 + fprintf(stderr, "anm_clear_track failed to allocate zero-length keyframe track\n"); 1.107 + } 1.108 + assert(track->keys); 1.109 +} 1.110 + 1.111 +int anm_set_track_name(struct anm_track *track, const char *name) 1.112 +{ 1.113 + char *tmp; 1.114 + 1.115 + if(!(tmp = malloc(strlen(name) + 1))) { 1.116 + return -1; 1.117 + } 1.118 + free(track->name); 1.119 + track->name = tmp; 1.120 + return 0; 1.121 +} 1.122 + 1.123 +const char *anm_get_track_name(struct anm_track *track) 1.124 +{ 1.125 + return track->name; 1.126 +} 1.127 + 1.128 +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in) 1.129 +{ 1.130 + track->interp = in; 1.131 +} 1.132 + 1.133 +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex) 1.134 +{ 1.135 + track->extrap = ex; 1.136 +} 1.137 + 1.138 +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end) 1.139 +{ 1.140 + return remap_time[track->extrap](tm, start, end); 1.141 +} 1.142 + 1.143 +void anm_set_track_default(struct anm_track *track, float def) 1.144 +{ 1.145 + track->def_val = def; 1.146 +} 1.147 + 1.148 +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key) 1.149 +{ 1.150 + int idx = anm_get_key_interval(track, key->time); 1.151 + 1.152 + /* if we got a valid keyframe index, compare them... */ 1.153 + if(idx >= 0 && idx < track->count && keycmp(key, track->keys + idx) == 0) { 1.154 + /* ... it's the same key, just update the value */ 1.155 + track->keys[idx].val = key->val; 1.156 + } else { 1.157 + /* ... it's a new key, add it and re-sort them */ 1.158 + void *tmp; 1.159 + if(!(tmp = dynarr_push(track->keys, key))) { 1.160 + return -1; 1.161 + } 1.162 + track->keys = tmp; 1.163 + /* TODO lazy qsort */ 1.164 + qsort(track->keys, ++track->count, sizeof *track->keys, keycmp); 1.165 + } 1.166 + return 0; 1.167 +} 1.168 + 1.169 +static int keycmp(const void *a, const void *b) 1.170 +{ 1.171 + return ((struct anm_keyframe*)a)->time - ((struct anm_keyframe*)b)->time; 1.172 +} 1.173 + 1.174 +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx) 1.175 +{ 1.176 + if(idx < 0 || idx >= track->count) { 1.177 + return 0; 1.178 + } 1.179 + return track->keys + idx; 1.180 +} 1.181 + 1.182 +int anm_get_key_interval(struct anm_track *track, anm_time_t tm) 1.183 +{ 1.184 + int last; 1.185 + 1.186 + if(!track->count || tm < track->keys[0].time) { 1.187 + return -1; 1.188 + } 1.189 + 1.190 + last = track->count - 1; 1.191 + if(tm > track->keys[last].time) { 1.192 + return last; 1.193 + } 1.194 + 1.195 + return find_prev_key(track->keys, 0, last, tm); 1.196 +} 1.197 + 1.198 +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm) 1.199 +{ 1.200 + int mid; 1.201 + 1.202 + if(end - start <= 1) { 1.203 + return start; 1.204 + } 1.205 + 1.206 + mid = (start + end) / 2; 1.207 + if(tm < arr[mid].time) { 1.208 + return find_prev_key(arr, start, mid, tm); 1.209 + } 1.210 + if(tm > arr[mid].time) { 1.211 + return find_prev_key(arr, mid, end, tm); 1.212 + } 1.213 + return mid; 1.214 +} 1.215 + 1.216 +int anm_set_value(struct anm_track *track, anm_time_t tm, float val) 1.217 +{ 1.218 + struct anm_keyframe key; 1.219 + key.time = tm; 1.220 + key.val = val; 1.221 + 1.222 + return anm_set_keyframe(track, &key); 1.223 +} 1.224 + 1.225 +float anm_get_value(struct anm_track *track, anm_time_t tm) 1.226 +{ 1.227 + int idx0, idx1, last_idx; 1.228 + anm_time_t tstart, tend; 1.229 + float t, dt; 1.230 + float v0, v1, v2, v3; 1.231 + 1.232 + if(!track->count) { 1.233 + return track->def_val; 1.234 + } 1.235 + 1.236 + last_idx = track->count - 1; 1.237 + 1.238 + tstart = track->keys[0].time; 1.239 + tend = track->keys[last_idx].time; 1.240 + 1.241 + if(tstart == tend) { 1.242 + return track->keys[0].val; 1.243 + } 1.244 + 1.245 + tm = remap_time[track->extrap](tm, tstart, tend); 1.246 + 1.247 + idx0 = anm_get_key_interval(track, tm); 1.248 + assert(idx0 >= 0 && idx0 < track->count); 1.249 + idx1 = idx0 + 1; 1.250 + 1.251 + if(idx0 == last_idx) { 1.252 + return track->keys[idx0].val; 1.253 + } 1.254 + 1.255 + dt = (float)(track->keys[idx1].time - track->keys[idx0].time); 1.256 + t = (float)(tm - track->keys[idx0].time) / dt; 1.257 + 1.258 + v1 = track->keys[idx0].val; 1.259 + v2 = track->keys[idx1].val; 1.260 + 1.261 + /* get the neigboring values to allow for cubic interpolation */ 1.262 + v0 = idx0 > 0 ? track->keys[idx0 - 1].val : v1; 1.263 + v3 = idx1 < last_idx ? track->keys[idx1 + 1].val : v2; 1.264 + 1.265 + return interp[track->interp](v0, v1, v2, v3, t); 1.266 +} 1.267 + 1.268 + 1.269 +static float interp_step(float v0, float v1, float v2, float v3, float t) 1.270 +{ 1.271 + return v1; 1.272 +} 1.273 + 1.274 +static float interp_linear(float v0, float v1, float v2, float v3, float t) 1.275 +{ 1.276 + return v1 + (v2 - v1) * t; 1.277 +} 1.278 + 1.279 +static float interp_cubic(float a, float b, float c, float d, float t) 1.280 +{ 1.281 + float x, y, z, w; 1.282 + float tsq = t * t; 1.283 + 1.284 + x = -a + 3.0 * b - 3.0 * c + d; 1.285 + y = 2.0 * a - 5.0 * b + 4.0 * c - d; 1.286 + z = c - a; 1.287 + w = 2.0 * b; 1.288 + 1.289 + return 0.5 * (x * tsq * t + y * tsq + z * t + w); 1.290 +} 1.291 + 1.292 +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end) 1.293 +{ 1.294 + return remap_repeat(tm, start, end); 1.295 +} 1.296 + 1.297 +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end) 1.298 +{ 1.299 + if(start == end) { 1.300 + return start; 1.301 + } 1.302 + return tm < start ? start : (tm >= end ? end - 1 : tm); 1.303 +} 1.304 + 1.305 +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end) 1.306 +{ 1.307 + anm_time_t x, interv = end - start; 1.308 + 1.309 + if(interv == 0) { 1.310 + return start; 1.311 + } 1.312 + 1.313 + x = (tm - start) % interv; 1.314 + if(x < 0) { 1.315 + x += interv; 1.316 + } 1.317 + return x + start; 1.318 + 1.319 + /*if(tm < start) { 1.320 + while(tm < start) { 1.321 + tm += interv; 1.322 + } 1.323 + return tm; 1.324 + } 1.325 + return (tm - start) % interv + start;*/ 1.326 +} 1.327 + 1.328 +static anm_time_t remap_pingpong(anm_time_t tm, anm_time_t start, anm_time_t end) 1.329 +{ 1.330 + anm_time_t interv = end - start; 1.331 + anm_time_t x = remap_repeat(tm, start, end + interv); 1.332 + 1.333 + return x > end ? end + interv - x : x; 1.334 +}