### Animation Envelopes Availability LightWave® 6.0 Component Layout, Modeler Header lwenvel.h A key is a structure that holds the value of an animation parameter at a specific time. An envelope is an array of keys, along with methods for interpolation (tweening) and extrapolation (what happens to the parameter value before the first key and after the last one). The Animation Envelopes global returns functions that allow you to create and manage envelopes and their keys, including a function to display an interface to the user for editing envelopes. Other global mechanisms are built on top of envelopes. A channel contains the continuous value of a parameter as a function of time, and this is based on both the underlying envelope and on external effects, including plug-ins (channel and item motion classes, for example) that can alter channel values. And the Variant Parameters global defines a data type used by XPanel envelope controls. See also the Motions section of the Layout commands page, as well as the commands supported by the Graph and Surface Editors. Global Call
       LWEnvelopeFuncs *envfunc;
       envfunc = global( LWENVELOPEFUNCS_GLOBAL, GFUSE_TRANSIENT );
The global function returns a pointer to an LWEnvelopeFuncs.
       typedef struct st_LWEnvelopeFuncs {
          LWEnvelopeID    (*create)      (LWChanGroupID, const char *, int);
          void            (*destroy)     (LWEnvelopeID);
          LWChanGroupID   (*createGroup) (LWChanGroupID parent, const char *);
          void            (*destroyGroup)(LWChanGroupID);
          LWError         (*copy)        (LWEnvelopeID to, LWEnvelopeID from);
          LWError         (*load)        (LWEnvelopeID, LWLoadState *);
          LWError         (*save)        (LWEnvelopeID, LWSaveState *);
          double          (*evaluate)    (LWEnvelopeID, LWTime);
          int             (*edit)        (LWChanGroupID, LWEnvelopeID,
                                            int flags, void *data);
          int             (*envAge)      (LWEnvelopeID);
          LWEnvKeyframeID (*createKey)   (LWEnvelopeID, LWTime, double value);
          void            (*destroyKey)  (LWEnvelopeID, LWEnvKeyframeID);
          LWEnvKeyframeID (*findKey)     (LWEnvelopeID, LWTime);
          LWEnvKeyframeID (*nextKey)     (LWEnvelopeID, LWEnvKeyframeID);
          LWEnvKeyframeID (*prevKey)     (LWEnvelopeID, LWEnvKeyframeID);
          int             (*keySet)      (LWEnvelopeID, LWEnvKeyframeID,
                                            LWKeyTag, void *value);
          int             (*keyGet)      (LWEnvelopeID, LWEnvKeyframeID,
                                            LWKeyTag, void *value);
          int             (*setEnvEvent) (LWEnvelopeID, LWEnvEventFunc,
                                            void *data);
          int             (*egSet)       (LWEnvelopeID, LWChanGroupID,
                                            int tag, void *value);
          int             (*egGet)       (LWEnvelopeID, LWChanGroupID,
                                            int tag, void *value);
       } LWEnvelopeFuncs;
`env = create( group, name, type )` Create a new envelope. The type defines how the envelope's values are interpreted and displayed to the user in the graph editor. It can be one of the following. `LWET_FLOAT LWET_DISTANCE LWET_PERCENT LWET_ANGLE` `destroy( env )` Destroy an envelope created using `create`. `group = createGroup( parent, name )` Create a new envelope group. An envelope group is just a way to organize related envelopes. `destroyGroup( group )` Destroy an envelope group created using `createGroup`. `error = copy( to, from )` Copy an envelope. This is meant to be called from within a handler's `copy` callback. `error = load( env, loadstate )` Load an envelope. This is meant to be called from within a handler's `load` callback. `error = save( env, savestate )` Save an envelope. This is meant to be called from within a handler's `save` callback. `value = evaluate( env, time )` Returns the interpolated value of the envelope. `result = edit( group, env, flags, data )` Open the graph editor window and allow the user to edit the envelope. The flags and data arguments are currently unused. `age = envAge( env )` Returns an integer containing the number of times the envelope has been changed. `key = createKey( env, time, value )` Create a new key in an envelope. `destroyKey( env, key )` Delete a key in an envelope. `key = findKey( env, time )` Returns the key for a given time. `key = nextKey( env, key )` Returns the next key in the envelope. `key = prevKey( env, key )` Returns the previous key in the envelope. `result = keySet( env, key, tag, value )` Set a value associated with a key. This can be the value of the key itself, the shape of the key, or one of the interpolation parameters. The result is true (non-zero) if the function succeeds and false (0) if it fails. The tag describing the value can be one of the following. `LWKEY_VALUE` The value of the key. `LWKEY_SHAPE` The curve type, an integer corresponding to the options in the graph editor: 0 - TCB (Kochanek-Bartels) 1 - Hermite 2 - 1D Bezier (obsolete, equivalent to Hermit) 3 - Linear 4 - Stepped 5 - 2D Bezier `LWKEY_TENSION LWKEY_CONTINUITY LWKEY_BIAS` The Kochanek-Bartels blending parameters. `LWKEY_PARAM_0 LWKEY_PARAM_1 LWKEY_PARAM_2 LWKEY_PARAM_3` The curve parameters. These are the Hermite coefficients for Hermite curves, and the incoming and outgoing tangents for 2D Bezier curves. `result = keyGet( env, key, tag, value )` Get a value associated with a key. The result is true (non-zero) if the function succeeds and false (0) if it fails. The tags are the same as those for `keySet`, along with `LWKEY_TIME`, the time of the key. `result = setEnvEvent( env, event_func, data )` Set a callback for an envelope. Whenever the envelope is modified, your `event_func` function will be called with `data` as its first argument. Currently the result is false (0) if `event_func` is NULL and true (non-zero) otherwise. When you no longer need it, you must unhook your event callback by calling `setEnvEvent` again with a NULL `event_func` argument. (But if your callback has already been called for an `LWEEVNT_DESTROY` event, don't try to unhook it, since at that point the envelope no longer exists.) The `data` argument should be the same as it was in the original call. This argument is used to uniquely identify the owner of a callback, which is necessary because more than one event callback can be set for a given envelope. For the same reason, `data` should not be NULL. `result = egSet( env, group, tag, value )` Set a value associated with the envelope. The result is true (non-zero) if the function succeeds and false (0) if it fails. `LWENVTAG_VISIBLE` Invisible envelopes won't appear in the graph editor. You can use these to store internal variables. The value for this tag is an integer containing true (1) or false (0). `LWENVTAG_PREBEHAVE LWENVTAG_POSTBEHAVE` Pre- and post-behavior setting, an integer corresponding to the options in the graph editor: 0 - Reset 1 - Constant 2 - Repeat 3 - Oscillate 4 - Offset Repeat 5 - Linear `result = egGet( env, group, tag, value )` Get a value associated with an envelope. In addition to the value types defined for `egSet`, you can use `LWENVTAG_KEYCOUNT` to get the number of keys defined for the envelope. The result is true (non-zero) if the function succeeds and false (0) if it fails. Event Callback The `setEnvEvent` function lets you set a callback that LightWave® will call whenever an envelope is modified. The callback looks like this.
       typedef int (*LWEnvEventFunc) (void *data, LWEnvelopeID env,
          LWEnvEvent event, void *eventData);
`data` is what you passed as the third argument to the `setEnvEvent` function. The `eventData` depends on the event, which can be one of the following.
       LWEEVNT_DESTROY
       LWEEVNT_KEY_INSERT
       LWEEVNT_KEY_DELETE
       LWEEVNT_KEY_VALUE
       LWEEVNT_KEY_TIME
For the `KEY` events, the `eventData` is the LWKeyframeID. For the `DESTROY` event, the `eventData` is currently undefined and the LWEnvelopeID is invalid. When your callback is called for a `DESTROY` event, the envelope has already been destroyed, and you should ensure that you invalidate any of your own references to the envelope. Example The envelope sample shows how envelopes are interpolated. It also uses the the envelope global functions to create and examine the envelope to be interpolated. The following code fragment finds a key for the red level of the first light at 5 seconds. If the light doesn't have a color envelope, we add it using the `AddEnvelope` command, and if there's no key at 5 seconds, we create it. The key value (the red level) is set to 0.75. In order to do this, we need to find the item ID for the first light, the channel group for that light, the red channel in the channel group, the underlying envelope for the red channel, and the key in that envelope at 5 seconds, if it exists. In addition to the envelope global, we use the channel info, item info and message globals.
       #include <lwserver.h>
       #include <lwenvel.h>
       #include <lwhost.h>

       LWEnvelopeFuncs *envf;
       LWChannelInfo *chinfo;
       LWItemInfo *iteminfo;
       LWMessageFuncs *msgf;
       LWItemID id;
       LWChanGroupID group;
       LWEnvelopeID envred;
       LWEnvKeyframeID key;
       char buf[ 128 ];
       double val;

       chinfo = global( LWCHANNELINFO_GLOBAL, GFUSE_TRANSIENT );
       envf = global( LWENVELOPEFUNCS_GLOBAL, GFUSE_TRANSIENT );
       iteminfo = global( LWITEMINFO_GLOBAL, GFUSE_TRANSIENT );
       msg = global( LWMESSAGEFUNCS_GLOBAL, GFUSE_TRANSIENT );

       if ( !chinfo || !envf || !iteminfo || !msgf )
          return AFUNC_BADGLOBAL;

       id = iteminfo->first( LWI_LIGHT, NULL );
       group = iteminfo->chanGroup( id );

       envred = findEnv( group, "Color.R" );
       if ( !envred ) {
          sprintf( buf, "SelectItem %x", id );
          local->evaluate( local->data, buf );
          local->evaluate( local->data, "AddEnvelope Color.R" );
          envred = findEnv( group, "Color.R" );
       }
       if ( !envred ) {
          msgf->info( "Couldn't create an envelope for",
             iteminfo->name( id ));
          return AFUNC_OK;
       }

       val = 0.75;
       key = envf->findKey( envred, 5.0 );
       if ( !key )
          key = envf->createKey( envred, 5.0, val );
       if ( key )
          envf->keySet( envred, key, LWKEY_VALUE, &val );
       else {
          sprintf( buf, "%s.Color.R", iteminfo->name( id ));
          msg->info( "Couldn't create a key in", buf );
       }
Our `findEnv` function simply loops through the channels in a channel group searching for a given channel name. If a match is found, it returns the envelope ID for the channel.
       LWEnvelopeID findEnv( LWChanGroupID group, char *name )
       {
          LWChannelID chan;

          chan = chinfo->nextChannel( group, NULL );
          while ( chan ) {
             if ( !strcmp( chinfo->channelName( chan ), name ))
                return chinfo->channelEnvelope( chan );
             chan = chinfo->nextChannel( group, chan );
          }
          return NULL;
       }