]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/lwo/envelope.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / picomodel / lwo / envelope.c
1 /*\r
2 ======================================================================\r
3 envelope.c\r
4 \r
5 Envelope functions for an LWO2 reader.\r
6 \r
7 Ernie Wright  16 Nov 00\r
8 ====================================================================== */\r
9 \r
10 #include "../picointernal.h"\r
11 #include "lwo2.h"\r
12 \r
13 /*\r
14 ======================================================================\r
15 lwFreeEnvelope()\r
16 \r
17 Free the memory used by an lwEnvelope.\r
18 ====================================================================== */\r
19 \r
20 void lwFreeEnvelope( lwEnvelope *env )\r
21 {\r
22    if ( env ) {\r
23       if ( env->name ) _pico_free( env->name );\r
24       lwListFree( env->key, _pico_free );\r
25       lwListFree( env->cfilter, lwFreePlugin );\r
26       _pico_free( env );\r
27    }\r
28 }\r
29 \r
30 \r
31 static int compare_keys( lwKey *k1, lwKey *k2 )\r
32 {\r
33    return k1->time > k2->time ? 1 : k1->time < k2->time ? -1 : 0;\r
34 }\r
35 \r
36 \r
37 /*\r
38 ======================================================================\r
39 lwGetEnvelope()\r
40 \r
41 Read an ENVL chunk from an LWO2 file.\r
42 ====================================================================== */\r
43 \r
44 lwEnvelope *lwGetEnvelope( picoMemStream_t *fp, int cksize )\r
45 {\r
46    lwEnvelope *env;\r
47    lwKey *key;\r
48    lwPlugin *plug;\r
49    unsigned int id;\r
50    unsigned short sz;\r
51    float f[ 4 ];\r
52    int i, nparams, pos, rlen;\r
53 \r
54 \r
55    /* allocate the Envelope structure */\r
56 \r
57    env = _pico_calloc( 1, sizeof( lwEnvelope ));\r
58    if ( !env ) goto Fail;\r
59 \r
60    /* remember where we started */\r
61 \r
62    set_flen( 0 );\r
63    pos = _pico_memstream_tell( fp );\r
64 \r
65    /* index */\r
66 \r
67    env->index = getVX( fp );\r
68 \r
69    /* first subchunk header */\r
70 \r
71    id = getU4( fp );\r
72    sz = getU2( fp );\r
73    if ( 0 > get_flen() ) goto Fail;\r
74 \r
75    /* process subchunks as they're encountered */\r
76 \r
77    while ( 1 ) {\r
78       sz += sz & 1;\r
79       set_flen( 0 );\r
80 \r
81       switch ( id ) {\r
82          case ID_TYPE:\r
83             env->type = getU2( fp );\r
84             break;\r
85 \r
86          case ID_NAME:\r
87             env->name = getS0( fp );\r
88             break;\r
89 \r
90          case ID_PRE:\r
91             env->behavior[ 0 ] = getU2( fp );\r
92             break;\r
93 \r
94          case ID_POST:\r
95             env->behavior[ 1 ] = getU2( fp );\r
96             break;\r
97 \r
98          case ID_KEY:\r
99             key = _pico_calloc( 1, sizeof( lwKey ));\r
100             if ( !key ) goto Fail;\r
101             key->time = getF4( fp );\r
102             key->value = getF4( fp );\r
103             lwListInsert( &env->key, key, compare_keys );\r
104             env->nkeys++;\r
105             break;\r
106 \r
107          case ID_SPAN:\r
108             if ( !key ) goto Fail;\r
109             key->shape = getU4( fp );\r
110 \r
111             nparams = ( sz - 4 ) / 4;\r
112             if ( nparams > 4 ) nparams = 4;\r
113             for ( i = 0; i < nparams; i++ )\r
114                f[ i ] = getF4( fp );\r
115 \r
116             switch ( key->shape ) {\r
117                case ID_TCB:\r
118                   key->tension = f[ 0 ];\r
119                   key->continuity = f[ 1 ];\r
120                   key->bias = f[ 2 ];\r
121                   break;\r
122 \r
123                case ID_BEZI:\r
124                case ID_HERM:\r
125                case ID_BEZ2:\r
126                   for ( i = 0; i < nparams; i++ )\r
127                      key->param[ i ] = f[ i ];\r
128                   break;\r
129             }\r
130             break;\r
131 \r
132          case ID_CHAN:\r
133             plug = _pico_calloc( 1, sizeof( lwPlugin ));\r
134             if ( !plug ) goto Fail;\r
135 \r
136             plug->name = getS0( fp );\r
137             plug->flags = getU2( fp );\r
138             plug->data = getbytes( fp, sz - get_flen() );\r
139 \r
140             lwListAdd( &env->cfilter, plug );\r
141             env->ncfilters++;\r
142             break;\r
143 \r
144          default:\r
145             break;\r
146       }\r
147 \r
148       /* error while reading current subchunk? */\r
149 \r
150       rlen = get_flen();\r
151       if ( rlen < 0 || rlen > sz ) goto Fail;\r
152 \r
153       /* skip unread parts of the current subchunk */\r
154 \r
155       if ( rlen < sz )\r
156          _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );\r
157 \r
158       /* end of the ENVL chunk? */\r
159 \r
160       rlen = _pico_memstream_tell( fp ) - pos;\r
161       if ( cksize < rlen ) goto Fail;\r
162       if ( cksize == rlen ) break;\r
163 \r
164       /* get the next subchunk header */\r
165 \r
166       set_flen( 0 );\r
167       id = getU4( fp );\r
168       sz = getU2( fp );\r
169       if ( 6 != get_flen() ) goto Fail;\r
170    }\r
171 \r
172    return env;\r
173 \r
174 Fail:\r
175    lwFreeEnvelope( env );\r
176    return NULL;\r
177 }\r
178 \r
179 \r
180 /*\r
181 ======================================================================\r
182 lwFindEnvelope()\r
183 \r
184 Returns an lwEnvelope pointer, given an envelope index.\r
185 ====================================================================== */\r
186 \r
187 lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index )\r
188 {\r
189    lwEnvelope *env;\r
190 \r
191    env = list;\r
192    while ( env ) {\r
193       if ( env->index == index ) break;\r
194       env = env->next;\r
195    }\r
196    return env;\r
197 }\r
198 \r
199 \r
200 /*\r
201 ======================================================================\r
202 range()\r
203 \r
204 Given the value v of a periodic function, returns the equivalent value\r
205 v2 in the principal interval [lo, hi].  If i isn't NULL, it receives\r
206 the number of wavelengths between v and v2.\r
207 \r
208    v2 = v - i * (hi - lo)\r
209 \r
210 For example, range( 3 pi, 0, 2 pi, i ) returns pi, with i = 1.\r
211 ====================================================================== */\r
212 \r
213 static float range( float v, float lo, float hi, int *i )\r
214 {\r
215    float v2, r = hi - lo;\r
216 \r
217    if ( r == 0.0 ) {\r
218       if ( i ) *i = 0;\r
219       return lo;\r
220    }\r
221 \r
222    v2 = lo + v - r * ( float ) floor(( double ) v / r );\r
223    if ( i ) *i = -( int )(( v2 - v ) / r + ( v2 > v ? 0.5 : -0.5 ));\r
224 \r
225    return v2;\r
226 }\r
227 \r
228 \r
229 /*\r
230 ======================================================================\r
231 hermite()\r
232 \r
233 Calculate the Hermite coefficients.\r
234 ====================================================================== */\r
235 \r
236 static void hermite( float t, float *h1, float *h2, float *h3, float *h4 )\r
237 {\r
238    float t2, t3;\r
239 \r
240    t2 = t * t;\r
241    t3 = t * t2;\r
242 \r
243    *h2 = 3.0f * t2 - t3 - t3;\r
244    *h1 = 1.0f - *h2;\r
245    *h4 = t3 - t2;\r
246    *h3 = *h4 - t2 + t;\r
247 }\r
248 \r
249 \r
250 /*\r
251 ======================================================================\r
252 bezier()\r
253 \r
254 Interpolate the value of a 1D Bezier curve.\r
255 ====================================================================== */\r
256 \r
257 static float bezier( float x0, float x1, float x2, float x3, float t )\r
258 {\r
259    float a, b, c, t2, t3;\r
260 \r
261    t2 = t * t;\r
262    t3 = t2 * t;\r
263 \r
264    c = 3.0f * ( x1 - x0 );\r
265    b = 3.0f * ( x2 - x1 ) - c;\r
266    a = x3 - x0 - c - b;\r
267 \r
268    return a * t3 + b * t2 + c * t + x0;\r
269 }\r
270 \r
271 \r
272 /*\r
273 ======================================================================\r
274 bez2_time()\r
275 \r
276 Find the t for which bezier() returns the input time.  The handle\r
277 endpoints of a BEZ2 curve represent the control points, and these have\r
278 (time, value) coordinates, so time is used as both a coordinate and a\r
279 parameter for this curve type.\r
280 ====================================================================== */\r
281 \r
282 static float bez2_time( float x0, float x1, float x2, float x3, float time,\r
283    float *t0, float *t1 )\r
284 {\r
285    float v, t;\r
286 \r
287    t = *t0 + ( *t1 - *t0 ) * 0.5f;\r
288    v = bezier( x0, x1, x2, x3, t );\r
289    if ( fabs( time - v ) > .0001f ) {\r
290       if ( v > time )\r
291          *t1 = t;\r
292       else\r
293          *t0 = t;\r
294       return bez2_time( x0, x1, x2, x3, time, t0, t1 );\r
295    }\r
296    else\r
297       return t;\r
298 }\r
299 \r
300 \r
301 /*\r
302 ======================================================================\r
303 bez2()\r
304 \r
305 Interpolate the value of a BEZ2 curve.\r
306 ====================================================================== */\r
307 \r
308 static float bez2( lwKey *key0, lwKey *key1, float time )\r
309 {\r
310    float x, y, t, t0 = 0.0f, t1 = 1.0f;\r
311 \r
312    if ( key0->shape == ID_BEZ2 )\r
313       x = key0->time + key0->param[ 2 ];\r
314    else\r
315       x = key0->time + ( key1->time - key0->time ) / 3.0f;\r
316 \r
317    t = bez2_time( key0->time, x, key1->time + key1->param[ 0 ], key1->time,\r
318       time, &t0, &t1 );\r
319 \r
320    if ( key0->shape == ID_BEZ2 )\r
321       y = key0->value + key0->param[ 3 ];\r
322    else\r
323       y = key0->value + key0->param[ 1 ] / 3.0f;\r
324 \r
325    return bezier( key0->value, y, key1->param[ 1 ] + key1->value, key1->value, t );\r
326 }\r
327 \r
328 \r
329 /*\r
330 ======================================================================\r
331 outgoing()\r
332 \r
333 Return the outgoing tangent to the curve at key0.  The value returned\r
334 for the BEZ2 case is used when extrapolating a linear pre behavior and\r
335 when interpolating a non-BEZ2 span.\r
336 ====================================================================== */\r
337 \r
338 static float outgoing( lwKey *key0, lwKey *key1 )\r
339 {\r
340    float a, b, d, t, out;\r
341 \r
342    switch ( key0->shape )\r
343    {\r
344       case ID_TCB:\r
345          a = ( 1.0f - key0->tension )\r
346            * ( 1.0f + key0->continuity )\r
347            * ( 1.0f + key0->bias );\r
348          b = ( 1.0f - key0->tension )\r
349            * ( 1.0f - key0->continuity )\r
350            * ( 1.0f - key0->bias );\r
351          d = key1->value - key0->value;\r
352 \r
353          if ( key0->prev ) {\r
354             t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time );\r
355             out = t * ( a * ( key0->value - key0->prev->value ) + b * d );\r
356          }\r
357          else\r
358             out = b * d;\r
359          break;\r
360 \r
361       case ID_LINE:\r
362          d = key1->value - key0->value;\r
363          if ( key0->prev ) {\r
364             t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time );\r
365             out = t * ( key0->value - key0->prev->value + d );\r
366          }\r
367          else\r
368             out = d;\r
369          break;\r
370 \r
371       case ID_BEZI:\r
372       case ID_HERM:\r
373          out = key0->param[ 1 ];\r
374          if ( key0->prev )\r
375             out *= ( key1->time - key0->time ) / ( key1->time - key0->prev->time );\r
376          break;\r
377 \r
378       case ID_BEZ2:\r
379          out = key0->param[ 3 ] * ( key1->time - key0->time );\r
380          if ( fabs( key0->param[ 2 ] ) > 1e-5f )\r
381             out /= key0->param[ 2 ];\r
382          else\r
383             out *= 1e5f;\r
384          break;\r
385 \r
386       case ID_STEP:\r
387       default:\r
388          out = 0.0f;\r
389          break;\r
390    }\r
391 \r
392    return out;\r
393 }\r
394 \r
395 \r
396 /*\r
397 ======================================================================\r
398 incoming()\r
399 \r
400 Return the incoming tangent to the curve at key1.  The value returned\r
401 for the BEZ2 case is used when extrapolating a linear post behavior.\r
402 ====================================================================== */\r
403 \r
404 static float incoming( lwKey *key0, lwKey *key1 )\r
405 {\r
406    float a, b, d, t, in;\r
407 \r
408    switch ( key1->shape )\r
409    {\r
410       case ID_LINE:\r
411          d = key1->value - key0->value;\r
412          if ( key1->next ) {\r
413             t = ( key1->time - key0->time ) / ( key1->next->time - key0->time );\r
414             in = t * ( key1->next->value - key1->value + d );\r
415          }\r
416          else\r
417             in = d;\r
418          break;\r
419 \r
420       case ID_TCB:\r
421          a = ( 1.0f - key1->tension )\r
422            * ( 1.0f - key1->continuity )\r
423            * ( 1.0f + key1->bias );\r
424          b = ( 1.0f - key1->tension )\r
425            * ( 1.0f + key1->continuity )\r
426            * ( 1.0f - key1->bias );\r
427          d = key1->value - key0->value;\r
428 \r
429          if ( key1->next ) {\r
430             t = ( key1->time - key0->time ) / ( key1->next->time - key0->time );\r
431             in = t * ( b * ( key1->next->value - key1->value ) + a * d );\r
432          }\r
433          else\r
434             in = a * d;\r
435          break;\r
436 \r
437       case ID_BEZI:\r
438       case ID_HERM:\r
439          in = key1->param[ 0 ];\r
440          if ( key1->next )\r
441             in *= ( key1->time - key0->time ) / ( key1->next->time - key0->time );\r
442          break;\r
443          return in;\r
444 \r
445       case ID_BEZ2:\r
446          in = key1->param[ 1 ] * ( key1->time - key0->time );\r
447          if ( fabs( key1->param[ 0 ] ) > 1e-5f )\r
448             in /= key1->param[ 0 ];\r
449          else\r
450             in *= 1e5f;\r
451          break;\r
452 \r
453       case ID_STEP:\r
454       default:\r
455          in = 0.0f;\r
456          break;\r
457    }\r
458 \r
459    return in;\r
460 }\r
461 \r
462 \r
463 /*\r
464 ======================================================================\r
465 evalEnvelope()\r
466 \r
467 Given a list of keys and a time, returns the interpolated value of the\r
468 envelope at that time.\r
469 ====================================================================== */\r
470 \r
471 float evalEnvelope( lwEnvelope *env, float time )\r
472 {\r
473    lwKey *key0, *key1, *skey, *ekey;\r
474    float t, h1, h2, h3, h4, in, out, offset = 0.0f;\r
475    int noff;\r
476 \r
477 \r
478    /* if there's no key, the value is 0 */\r
479 \r
480    if ( env->nkeys == 0 ) return 0.0f;\r
481 \r
482    /* if there's only one key, the value is constant */\r
483 \r
484    if ( env->nkeys == 1 )\r
485       return env->key->value;\r
486 \r
487    /* find the first and last keys */\r
488 \r
489    skey = ekey = env->key;\r
490    while ( ekey->next ) ekey = ekey->next;\r
491 \r
492    /* use pre-behavior if time is before first key time */\r
493 \r
494    if ( time < skey->time ) {\r
495       switch ( env->behavior[ 0 ] )\r
496       {\r
497          case BEH_RESET:\r
498             return 0.0f;\r
499 \r
500          case BEH_CONSTANT:\r
501             return skey->value;\r
502 \r
503          case BEH_REPEAT:\r
504             time = range( time, skey->time, ekey->time, NULL );\r
505             break;\r
506 \r
507          case BEH_OSCILLATE:\r
508             time = range( time, skey->time, ekey->time, &noff );\r
509             if ( noff % 2 )\r
510                time = ekey->time - skey->time - time;\r
511             break;\r
512 \r
513          case BEH_OFFSET:\r
514             time = range( time, skey->time, ekey->time, &noff );\r
515             offset = noff * ( ekey->value - skey->value );\r
516             break;\r
517 \r
518          case BEH_LINEAR:\r
519             out = outgoing( skey, skey->next )\r
520                 / ( skey->next->time - skey->time );\r
521             return out * ( time - skey->time ) + skey->value;\r
522       }\r
523    }\r
524 \r
525    /* use post-behavior if time is after last key time */\r
526 \r
527    else if ( time > ekey->time ) {\r
528       switch ( env->behavior[ 1 ] )\r
529       {\r
530          case BEH_RESET:\r
531             return 0.0f;\r
532 \r
533          case BEH_CONSTANT:\r
534             return ekey->value;\r
535 \r
536          case BEH_REPEAT:\r
537             time = range( time, skey->time, ekey->time, NULL );\r
538             break;\r
539 \r
540          case BEH_OSCILLATE:\r
541             time = range( time, skey->time, ekey->time, &noff );\r
542             if ( noff % 2 )\r
543                time = ekey->time - skey->time - time;\r
544             break;\r
545 \r
546          case BEH_OFFSET:\r
547             time = range( time, skey->time, ekey->time, &noff );\r
548             offset = noff * ( ekey->value - skey->value );\r
549             break;\r
550 \r
551          case BEH_LINEAR:\r
552             in = incoming( ekey->prev, ekey )\r
553                / ( ekey->time - ekey->prev->time );\r
554             return in * ( time - ekey->time ) + ekey->value;\r
555       }\r
556    }\r
557 \r
558    /* get the endpoints of the interval being evaluated */\r
559 \r
560    key0 = env->key;\r
561    while ( time > key0->next->time )\r
562       key0 = key0->next;\r
563    key1 = key0->next;\r
564 \r
565    /* check for singularities first */\r
566 \r
567    if ( time == key0->time )\r
568       return key0->value + offset;\r
569    else if ( time == key1->time )\r
570       return key1->value + offset;\r
571 \r
572    /* get interval length, time in [0, 1] */\r
573 \r
574    t = ( time - key0->time ) / ( key1->time - key0->time );\r
575 \r
576    /* interpolate */\r
577 \r
578    switch ( key1->shape )\r
579    {\r
580       case ID_TCB:\r
581       case ID_BEZI:\r
582       case ID_HERM:\r
583          out = outgoing( key0, key1 );\r
584          in = incoming( key0, key1 );\r
585          hermite( t, &h1, &h2, &h3, &h4 );\r
586          return h1 * key0->value + h2 * key1->value + h3 * out + h4 * in + offset;\r
587 \r
588       case ID_BEZ2:\r
589          return bez2( key0, key1, time ) + offset;\r
590 \r
591       case ID_LINE:\r
592          return key0->value + t * ( key1->value - key0->value ) + offset;\r
593 \r
594       case ID_STEP:\r
595          return key0->value + offset;\r
596 \r
597       default:\r
598          return offset;\r
599    }\r
600 }\r