Classes Interfaces Table of Contents

Handlers

Formally, handlers are plug-in classes that manage persistent instance data through callbacks identified in the activation function. Less formally, handlers are Layout plug-ins that respond to events. The user associates a handler with a specific object, surface or effect, and Layout calls the handler's functions at specific points during the animation and rendering of that scene element.

To see how this works, we'll walk through some example source code for a particular handler plug-in (blotch.p, a ShaderHandler), examining fragments of plug-in code more or less in the order Layout makes use of them.

Handler Activation

As with any plug-in file, the blotch.p source code includes a server description that lists what plug-ins the .p file contains and what their activation functions are. (See the common elements page for more about .p files, server descriptions and activation functions.) The Blotch example contains a shader, called "Demo_Blotch," and its interface.

   ServerRecord ServerDesc[] = {
      { "ShaderHandler",   "Demo_Blotch", BlotchActivate },
      { "ShaderInterface", "Demo_Blotch", Interface },
      { NULL }
   };

When Layout loads blotch.p, it looks at this server description and finds the activation function BlotchActivate, which it then calls.

   XCALL_( static int )
   BlotchActivate( long version, GlobalFunc *global,
      LWShaderHandler *local, void *serverData )
   {
      if ( version != LWSHADERHANDLER_VERSION )
         return ( AFUNC_BADVERSION );

      local->inst->create   = Create;
      local->inst->destroy  = Destroy;
      local->inst->copy     = Copy;
      local->inst->load     = Load;
      local->inst->save     = Save;
      local->inst->descln   = DescLn;

      local->item->useItems = UseItems;
      local->item->changeID = ChangeID;

      local->rend->init     = Init;
      local->rend->cleanup  = Cleanup;
      local->rend->newTime  = NewTime;

      local->evaluate       = Evaluate;
      local->flags          = Flags;

      return AFUNC_OK;
   }

Create, Destroy, Copy and so on are all functions in blotch.c. They are the plug-in's handler functions (or event callbacks), and the activation function tells Layout where they all are. The plug-in won't actually do any work until Layout starts calling the plug-in's handler functions. Layout doesn't care what names you give these functions in your source code, as long as you make the correct assignments to the members of the local structure.

Each handler class is passed a different structure as the local argument to its activation function (shaders receive an LWShaderHandler, for example), but the structures for all classes begin with a pointer to the basic handler functions for managing instance data.

Instance Data

Like any shader, Blotch can be attached to any number of surfaces in the scene. The way a handler distinguishes between these different instances of itself is through its instance data. The functions that manage instance data are contained in an LWInstanceFuncs structure, a pointer to which is the first element of every handler structure.

   typedef struct st_LWInstanceFuncs {
      void         *priv;
      LWInstance   (*create)  (void *priv, void *context, LWError *);
      void         (*destroy) (LWInstance);
      LWError      (*copy)    (LWInstance, LWInstance from);
      LWError      (*load)    (LWInstance, const LWLoadState *);
      LWError      (*save)    (LWInstance, const LWSaveState *);
      const char * (*descln)  (LWInstance);
   } LWInstanceFuncs;

Blotch's instance data looks like this:

   typedef struct st_BlotchInstance {
      float color[ 3 ];
      float center[ 3 ];
      float radius;
      float softness;
      float r2, piOverR;
   } BlotchInstance;

Blotch's Create function allocates memory for a BlotchInstance and initializes its contents, and the Destroy function frees the memory allocated by Create.

   XCALL_( static LWInstance )
   Create( void *priv, void *context, LWError *err )
   {
      BlotchInstance *bi;

      bi = calloc( 1, sizeof( BlotchInstance ));
      if ( !bi ) {
         *err = "Couldn't initialize Blotch!";
         return NULL;
      }
      bi->color[ 0 ] = 0.4;
      bi->radius     = 1.0;
      bi->softness   = 0.5;

      return ( LWInstance ) bi;
   }


   XCALL_( static void )
   Destroy( BlotchInstance *bi )
   {
      free( bi );
   }

The Copy function in Blotch just duplicates the contents of the instance data structure. Be careful when copying instance data that contains pointers. In most cases you won't want to copy over the pointers of the to instance, so you'll need to copy instance values individually, and for the pointer values, copy the contents of the memory they point to.

   XCALL_( static LWError )
   Copy( BlotchInstance *to, BlotchInstance *from )
   {
      *to = *from;
      return NULL;
   }
   XCALL_( static LWError )
   Load( BlotchInstance *bi, const LWLoadState *ls )
   {
      ls->readFP( ls->readData, bi->color, 3 );
      ls->readFP( ls->readData, bi->center, 3 );
      ls->readFP( ls->readData, &bi->radius, 1 );
      ls->readFP( ls->readData, &bi->softness, 1 );
      return NULL;
   }

   XCALL_( LWError )
   Save( BlotchInstance *bi, const LWSaveState *ss )
   {
      ss->writeFP( ss->writeData, bi->color, 3 );
      ss->writeFP( ss->writeData, bi->center, 3 );
      ss->writeFP( ss->writeData, &bi->radius, 1 );
      ss->writeFP( ss->writeData, &bi->softness, 1 );
      return NULL;
   }

Definitions

create( priv, context, error )
Called when the user selects your plug-in on the interface, and when Layout loads a scene or an object file that refers to your plug-in. You allocate and initialize a data structure (the instance data) that helps you keep track of this specific invocation of your plug-in, and return a pointer to this data. Your instance data should include whatever the rest of your plug-in needs to know or remember while performing its tasks. If you can't create an instance, set the error argument to an error message string and return NULL.

priv
An opaque pointer that you pass to certain functions you can call from your Create function.
context
What this contains will vary depending on the plug-in class. Many classes receive an item ID in this argument that tells the plug-in which item (object, bone, light, camera) the plug-in will be associated with.
error
You return error messages through this argument when your Create function fails.

destroy( instance )
Description.

copy( to, from )
Description.

load( instance, loadstate )
Description.

save( instance, savestate )
Description.

descln( instance )
Description.