![]() |
![]() |
![]() |
HandlersFormally, 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
|