]> de.git.xonotic.org Git - voretournament/voretournament.git/blobdiff - misc/mediasource/netradiant-src/libs/picomodel/lwo/envelope.c
Move the netradiant and fteqcc sources
[voretournament/voretournament.git] / misc / mediasource / netradiant-src / libs / picomodel / lwo / envelope.c
diff --git a/misc/mediasource/netradiant-src/libs/picomodel/lwo/envelope.c b/misc/mediasource/netradiant-src/libs/picomodel/lwo/envelope.c
new file mode 100644 (file)
index 0000000..3720a8e
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+======================================================================
+envelope.c
+
+Envelope functions for an LWO2 reader.
+
+Ernie Wright  16 Nov 00
+====================================================================== */
+
+#include "../picointernal.h"
+#include "lwo2.h"
+
+/*
+======================================================================
+lwFreeEnvelope()
+
+Free the memory used by an lwEnvelope.
+====================================================================== */
+
+void lwFreeEnvelope( lwEnvelope *env )
+{
+   if ( env ) {
+      if ( env->name ) _pico_free( env->name );
+      lwListFree( env->key, _pico_free );
+      lwListFree( env->cfilter, (void *) lwFreePlugin );
+      _pico_free( env );
+   }
+}
+
+
+static int compare_keys( lwKey *k1, lwKey *k2 )
+{
+   return k1->time > k2->time ? 1 : k1->time < k2->time ? -1 : 0;
+}
+
+
+/*
+======================================================================
+lwGetEnvelope()
+
+Read an ENVL chunk from an LWO2 file.
+====================================================================== */
+
+lwEnvelope *lwGetEnvelope( picoMemStream_t *fp, int cksize )
+{
+   lwEnvelope *env;
+   lwKey *key = NULL;
+   lwPlugin *plug;
+   unsigned int id;
+   unsigned short sz;
+   float f[ 4 ];
+   int i, nparams, pos, rlen;
+
+
+   /* allocate the Envelope structure */
+
+   env = _pico_calloc( 1, sizeof( lwEnvelope ));
+   if ( !env ) goto Fail;
+
+   /* remember where we started */
+
+   set_flen( 0 );
+   pos = _pico_memstream_tell( fp );
+
+   /* index */
+
+   env->index = getVX( fp );
+
+   /* first subchunk header */
+
+   id = getU4( fp );
+   sz = getU2( fp );
+   if ( 0 > get_flen() ) goto Fail;
+
+   /* process subchunks as they're encountered */
+
+   while ( 1 ) {
+      sz += sz & 1;
+      set_flen( 0 );
+
+      switch ( id ) {
+         case ID_TYPE:
+            env->type = getU2( fp );
+            break;
+
+         case ID_NAME:
+            env->name = getS0( fp );
+            break;
+
+         case ID_PRE:
+            env->behavior[ 0 ] = getU2( fp );
+            break;
+
+         case ID_POST:
+            env->behavior[ 1 ] = getU2( fp );
+            break;
+
+         case ID_KEY:
+            key = _pico_calloc( 1, sizeof( lwKey ));
+            if ( !key ) goto Fail;
+            key->time = getF4( fp );
+            key->value = getF4( fp );
+            lwListInsert( (void **) &env->key, key, (void *) compare_keys );
+            env->nkeys++;
+            break;
+
+         case ID_SPAN:
+            if ( !key ) goto Fail;
+            key->shape = getU4( fp );
+
+            nparams = ( sz - 4 ) / 4;
+            if ( nparams > 4 ) nparams = 4;
+            for ( i = 0; i < nparams; i++ )
+               f[ i ] = getF4( fp );
+
+            switch ( key->shape ) {
+               case ID_TCB:
+                  key->tension = f[ 0 ];
+                  key->continuity = f[ 1 ];
+                  key->bias = f[ 2 ];
+                  break;
+
+               case ID_BEZI:
+               case ID_HERM:
+               case ID_BEZ2:
+                  for ( i = 0; i < nparams; i++ )
+                     key->param[ i ] = f[ i ];
+                  break;
+            }
+            break;
+
+         case ID_CHAN:
+            plug = _pico_calloc( 1, sizeof( lwPlugin ));
+            if ( !plug ) goto Fail;
+
+            plug->name = getS0( fp );
+            plug->flags = getU2( fp );
+            plug->data = getbytes( fp, sz - get_flen() );
+
+            lwListAdd( (void *) &env->cfilter, plug );
+            env->ncfilters++;
+            break;
+
+         default:
+            break;
+      }
+
+      /* error while reading current subchunk? */
+
+      rlen = get_flen();
+      if ( rlen < 0 || rlen > sz ) goto Fail;
+
+      /* skip unread parts of the current subchunk */
+
+      if ( rlen < sz )
+         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
+
+      /* end of the ENVL chunk? */
+
+      rlen = _pico_memstream_tell( fp ) - pos;
+      if ( cksize < rlen ) goto Fail;
+      if ( cksize == rlen ) break;
+
+      /* get the next subchunk header */
+
+      set_flen( 0 );
+      id = getU4( fp );
+      sz = getU2( fp );
+      if ( 6 != get_flen() ) goto Fail;
+   }
+
+   return env;
+
+Fail:
+   lwFreeEnvelope( env );
+   return NULL;
+}
+
+
+/*
+======================================================================
+lwFindEnvelope()
+
+Returns an lwEnvelope pointer, given an envelope index.
+====================================================================== */
+
+lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index )
+{
+   lwEnvelope *env;
+
+   env = list;
+   while ( env ) {
+      if ( env->index == index ) break;
+      env = env->next;
+   }
+   return env;
+}
+
+
+/*
+======================================================================
+range()
+
+Given the value v of a periodic function, returns the equivalent value
+v2 in the principal interval [lo, hi].  If i isn't NULL, it receives
+the number of wavelengths between v and v2.
+
+   v2 = v - i * (hi - lo)
+
+For example, range( 3 pi, 0, 2 pi, i ) returns pi, with i = 1.
+====================================================================== */
+
+static float range( float v, float lo, float hi, int *i )
+{
+   float v2, r = hi - lo;
+
+   if ( r == 0.0 ) {
+      if ( i ) *i = 0;
+      return lo;
+   }
+
+   v2 = lo + v - r * ( float ) floor(( double ) v / r );
+   if ( i ) *i = -( int )(( v2 - v ) / r + ( v2 > v ? 0.5 : -0.5 ));
+
+   return v2;
+}
+
+
+/*
+======================================================================
+hermite()
+
+Calculate the Hermite coefficients.
+====================================================================== */
+
+static void hermite( float t, float *h1, float *h2, float *h3, float *h4 )
+{
+   float t2, t3;
+
+   t2 = t * t;
+   t3 = t * t2;
+
+   *h2 = 3.0f * t2 - t3 - t3;
+   *h1 = 1.0f - *h2;
+   *h4 = t3 - t2;
+   *h3 = *h4 - t2 + t;
+}
+
+
+/*
+======================================================================
+bezier()
+
+Interpolate the value of a 1D Bezier curve.
+====================================================================== */
+
+static float bezier( float x0, float x1, float x2, float x3, float t )
+{
+   float a, b, c, t2, t3;
+
+   t2 = t * t;
+   t3 = t2 * t;
+
+   c = 3.0f * ( x1 - x0 );
+   b = 3.0f * ( x2 - x1 ) - c;
+   a = x3 - x0 - c - b;
+
+   return a * t3 + b * t2 + c * t + x0;
+}
+
+
+/*
+======================================================================
+bez2_time()
+
+Find the t for which bezier() returns the input time.  The handle
+endpoints of a BEZ2 curve represent the control points, and these have
+(time, value) coordinates, so time is used as both a coordinate and a
+parameter for this curve type.
+====================================================================== */
+
+static float bez2_time( float x0, float x1, float x2, float x3, float time,
+   float *t0, float *t1 )
+{
+   float v, t;
+
+   t = *t0 + ( *t1 - *t0 ) * 0.5f;
+   v = bezier( x0, x1, x2, x3, t );
+   if ( fabs( time - v ) > .0001f ) {
+      if ( v > time )
+         *t1 = t;
+      else
+         *t0 = t;
+      return bez2_time( x0, x1, x2, x3, time, t0, t1 );
+   }
+   else
+      return t;
+}
+
+
+/*
+======================================================================
+bez2()
+
+Interpolate the value of a BEZ2 curve.
+====================================================================== */
+
+static float bez2( lwKey *key0, lwKey *key1, float time )
+{
+   float x, y, t, t0 = 0.0f, t1 = 1.0f;
+
+   if ( key0->shape == ID_BEZ2 )
+      x = key0->time + key0->param[ 2 ];
+   else
+      x = key0->time + ( key1->time - key0->time ) / 3.0f;
+
+   t = bez2_time( key0->time, x, key1->time + key1->param[ 0 ], key1->time,
+      time, &t0, &t1 );
+
+   if ( key0->shape == ID_BEZ2 )
+      y = key0->value + key0->param[ 3 ];
+   else
+      y = key0->value + key0->param[ 1 ] / 3.0f;
+
+   return bezier( key0->value, y, key1->param[ 1 ] + key1->value, key1->value, t );
+}
+
+
+/*
+======================================================================
+outgoing()
+
+Return the outgoing tangent to the curve at key0.  The value returned
+for the BEZ2 case is used when extrapolating a linear pre behavior and
+when interpolating a non-BEZ2 span.
+====================================================================== */
+
+static float outgoing( lwKey *key0, lwKey *key1 )
+{
+   float a, b, d, t, out;
+
+   switch ( key0->shape )
+   {
+      case ID_TCB:
+         a = ( 1.0f - key0->tension )
+           * ( 1.0f + key0->continuity )
+           * ( 1.0f + key0->bias );
+         b = ( 1.0f - key0->tension )
+           * ( 1.0f - key0->continuity )
+           * ( 1.0f - key0->bias );
+         d = key1->value - key0->value;
+
+         if ( key0->prev ) {
+            t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time );
+            out = t * ( a * ( key0->value - key0->prev->value ) + b * d );
+         }
+         else
+            out = b * d;
+         break;
+
+      case ID_LINE:
+         d = key1->value - key0->value;
+         if ( key0->prev ) {
+            t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time );
+            out = t * ( key0->value - key0->prev->value + d );
+         }
+         else
+            out = d;
+         break;
+
+      case ID_BEZI:
+      case ID_HERM:
+         out = key0->param[ 1 ];
+         if ( key0->prev )
+            out *= ( key1->time - key0->time ) / ( key1->time - key0->prev->time );
+         break;
+
+      case ID_BEZ2:
+         out = key0->param[ 3 ] * ( key1->time - key0->time );
+         if ( fabs( key0->param[ 2 ] ) > 1e-5f )
+            out /= key0->param[ 2 ];
+         else
+            out *= 1e5f;
+         break;
+
+      case ID_STEP:
+      default:
+         out = 0.0f;
+         break;
+   }
+
+   return out;
+}
+
+
+/*
+======================================================================
+incoming()
+
+Return the incoming tangent to the curve at key1.  The value returned
+for the BEZ2 case is used when extrapolating a linear post behavior.
+====================================================================== */
+
+static float incoming( lwKey *key0, lwKey *key1 )
+{
+   float a, b, d, t, in;
+
+   switch ( key1->shape )
+   {
+      case ID_LINE:
+         d = key1->value - key0->value;
+         if ( key1->next ) {
+            t = ( key1->time - key0->time ) / ( key1->next->time - key0->time );
+            in = t * ( key1->next->value - key1->value + d );
+         }
+         else
+            in = d;
+         break;
+
+      case ID_TCB:
+         a = ( 1.0f - key1->tension )
+           * ( 1.0f - key1->continuity )
+           * ( 1.0f + key1->bias );
+         b = ( 1.0f - key1->tension )
+           * ( 1.0f + key1->continuity )
+           * ( 1.0f - key1->bias );
+         d = key1->value - key0->value;
+
+         if ( key1->next ) {
+            t = ( key1->time - key0->time ) / ( key1->next->time - key0->time );
+            in = t * ( b * ( key1->next->value - key1->value ) + a * d );
+         }
+         else
+            in = a * d;
+         break;
+
+      case ID_BEZI:
+      case ID_HERM:
+         in = key1->param[ 0 ];
+         if ( key1->next )
+            in *= ( key1->time - key0->time ) / ( key1->next->time - key0->time );
+         break;
+         return in;
+
+      case ID_BEZ2:
+         in = key1->param[ 1 ] * ( key1->time - key0->time );
+         if ( fabs( key1->param[ 0 ] ) > 1e-5f )
+            in /= key1->param[ 0 ];
+         else
+            in *= 1e5f;
+         break;
+
+      case ID_STEP:
+      default:
+         in = 0.0f;
+         break;
+   }
+
+   return in;
+}
+
+
+/*
+======================================================================
+evalEnvelope()
+
+Given a list of keys and a time, returns the interpolated value of the
+envelope at that time.
+====================================================================== */
+
+float evalEnvelope( lwEnvelope *env, float time )
+{
+   lwKey *key0, *key1, *skey, *ekey;
+   float t, h1, h2, h3, h4, in, out, offset = 0.0f;
+   int noff;
+
+
+   /* if there's no key, the value is 0 */
+
+   if ( env->nkeys == 0 ) return 0.0f;
+
+   /* if there's only one key, the value is constant */
+
+   if ( env->nkeys == 1 )
+      return env->key->value;
+
+   /* find the first and last keys */
+
+   skey = ekey = env->key;
+   while ( ekey->next ) ekey = ekey->next;
+
+   /* use pre-behavior if time is before first key time */
+
+   if ( time < skey->time ) {
+      switch ( env->behavior[ 0 ] )
+      {
+         case BEH_RESET:
+            return 0.0f;
+
+         case BEH_CONSTANT:
+            return skey->value;
+
+         case BEH_REPEAT:
+            time = range( time, skey->time, ekey->time, NULL );
+            break;
+
+         case BEH_OSCILLATE:
+            time = range( time, skey->time, ekey->time, &noff );
+            if ( noff % 2 )
+               time = ekey->time - skey->time - time;
+            break;
+
+         case BEH_OFFSET:
+            time = range( time, skey->time, ekey->time, &noff );
+            offset = noff * ( ekey->value - skey->value );
+            break;
+
+         case BEH_LINEAR:
+            out = outgoing( skey, skey->next )
+                / ( skey->next->time - skey->time );
+            return out * ( time - skey->time ) + skey->value;
+      }
+   }
+
+   /* use post-behavior if time is after last key time */
+
+   else if ( time > ekey->time ) {
+      switch ( env->behavior[ 1 ] )
+      {
+         case BEH_RESET:
+            return 0.0f;
+
+         case BEH_CONSTANT:
+            return ekey->value;
+
+         case BEH_REPEAT:
+            time = range( time, skey->time, ekey->time, NULL );
+            break;
+
+         case BEH_OSCILLATE:
+            time = range( time, skey->time, ekey->time, &noff );
+            if ( noff % 2 )
+               time = ekey->time - skey->time - time;
+            break;
+
+         case BEH_OFFSET:
+            time = range( time, skey->time, ekey->time, &noff );
+            offset = noff * ( ekey->value - skey->value );
+            break;
+
+         case BEH_LINEAR:
+            in = incoming( ekey->prev, ekey )
+               / ( ekey->time - ekey->prev->time );
+            return in * ( time - ekey->time ) + ekey->value;
+      }
+   }
+
+   /* get the endpoints of the interval being evaluated */
+
+   key0 = env->key;
+   while ( time > key0->next->time )
+      key0 = key0->next;
+   key1 = key0->next;
+
+   /* check for singularities first */
+
+   if ( time == key0->time )
+      return key0->value + offset;
+   else if ( time == key1->time )
+      return key1->value + offset;
+
+   /* get interval length, time in [0, 1] */
+
+   t = ( time - key0->time ) / ( key1->time - key0->time );
+
+   /* interpolate */
+
+   switch ( key1->shape )
+   {
+      case ID_TCB:
+      case ID_BEZI:
+      case ID_HERM:
+         out = outgoing( key0, key1 );
+         in = incoming( key0, key1 );
+         hermite( t, &h1, &h2, &h3, &h4 );
+         return h1 * key0->value + h2 * key1->value + h3 * out + h4 * in + offset;
+
+      case ID_BEZ2:
+         return bez2( key0, key1, time ) + offset;
+
+      case ID_LINE:
+         return key0->value + t * ( key1->value - key0->value ) + offset;
+
+      case ID_STEP:
+         return key0->value + offset;
+
+      default:
+         return offset;
+   }
+}