This document describes the plug-in interfaces defined for the LightWave 3D animation and modeling programs. The two programs which make up the LightWave 3D suite each have different plug-in interfaces to allow access to their internal state and functions, as well as some common interfaces which are shared between the two. These common interfaces are the subject of the first portion of this document. After the common interfaces, the plug-in interfaces specific to modeling and animation are described.
The reader should be familiar with the basic concepts of the LightWave plug-in design, such as server classes, local data, and global data. These are described in the document entitled "LightWave Plug-in Architecture," and should be considered a prerequsite to this document. In addition, some of the common server classes are defined in other documents which will be referenced.
The LightWave animation program will be called 'Layout' and the modeling program will be called 'Modeler' throughout this document.
This document is a design specification for the LightWave version 5.0 release. Some classes and globals have been added since 4.0 and some server capabilities have been extended. 5.0-specific features are marked in the document, and new server capabilites will be indicated by a change in the version number. Servers designed to work with 4.0 will still function when used with 5.0.
The version number for all classes in release 4.0 was 1. In 5.0, many of the handler classes (and their associated interface classes) have been bumped up to version 2. When Layout activates one of these handlers, it first tries passing version 2. If the server only supports the version 1 interface, then its activation function should return AFUNC_BADVERSION, and Layout will activate it with the old version 1 interface.
All the globals present in 4.0 are still available along with the new ones added for 5.0. Globals present only in 5.0 have unique names and therefore attempts to request them under 4.0 will return a null pointer.
The pre-release is no longer specifically supported since users will at this point at least all have 4.0.
These are the common server classes defined for both programs in the LightWave 3D suite. There are interfaces for these server classes present in Layout and Modeler and any plug-in of one of these classes may be shared by both programs.
The "FileRequester" and "ImageLoader" utility server classes are used by both LightWave and Modeler. The interfaces for these server classes are defined in the "File Requester Plug-ins" and "LightWave Images" documents.
When Layout or Modeler encounters a foreign object file which it cannot parse, it will call an "ObjectLoader" class server to import it. All the loaders defined for the host will be activated in sequence, and the first one to recognize the file will load it. The order in which loaders are called is not defined, although it may be alphabetical by server name.
At activate, an ObjectImport structure is passed to a plug-in object loader as its local data, and the loader should attempt to parse the input file given by the filename field. If it cannot open or recognize the file the loader should set the `result' field to the appropriate code and return.
If it recognizes the file type it should send the mesh and surface data to the host by calling the callbacks. The `data' field is an opaque pointer to some internal state for the host and should be the first argument to every callback. The `monitor' field will contain a pointer to a monitor which can be used to track the progress of loading. The monitor should not be used unless the object format is recognized.
typedef struct st_ObjectImport { int result; const char *filename; Monitor *monitor; char *failedBuf; int failedLen; void *data; void (*begin) (void *, void *); void (*done) (void *); void (*numPoints) (void *, int total); void (*points) (void *, int numPts, const float *xyz); int (*surfIndex) (void *, const char *name, int *firstTime); void (*polygon) (void *, int numPts, int surf, int flags, const unsigned short *); void (*surfData) (void *, const char *name, int size, void *data); } ObjectImport; . . .
ObjectLoader class servers have a version number of 1.
Sending mesh data to the host involves calling the functions provided in the ObjectImport structure in a semi-sequential order. The basics are to start the data transfer, send the points, define surface names, send the polygons, assign surface parameters to names, and complete the transfer.
. . . #define OBJPOLF_FACE 0 #define OBJPOLF_CURVE (1<<0) #define OBJPOLF_DETAIL (1<<1) #define OBJPOLF_STARTCC (1<<2) #define OBJPOLF_ENDCC (1<<3) . . .
If a failure occurs partway through loading a file, the loader can set the result field and return without having to do any other cleanup.
The loader must set the `result' field to one of these following values before it returns. OK indicates successful parsing of the object file. BADFILE indicates that the loader could not open the file. NOREC indicates that the loader could not recognize the format, and ABORTED indicates the that the user manually aborted the load. Any other failure is indicated by the generic FAILED value. In this case, the loader may also place a human-readable error message into the buffer pointed to by `failedBuf,' provided that `failedLen' is non-zero.
. . . #define OBJSTAT_OK 0 #define OBJSTAT_NOREC 1 #define OBJSTAT_BADFILE 2 #define OBJSTAT_ABORTED 3 #define OBJSTAT_FAILED 99
This section contains descriptions of the data pointers which can be accessed by passing specific global ID strings to the global functions of both Modeler and Layout. Common servers can access these globals regardless of which program they are running under.
The global ID "Host Display Info" returns a HostDisplayInfo structure initialized for the host application's main window. This structure is described in the "LightWave Plug-in Architecture" document and defined in the `hdisp.h' header file.
The global ID "File Type Pattern" returns a file type function used to get filename filters for different file types. This is used by the file requester class primarily and is described in the "File Requester Plug-ins" document.
The global ID "File Request" returns a `FileReqFunc' pointer. Servers can use this function to request filenames from users with the same file requester used by the host. The `hail' string is the title of the request and the `name' & `path' buffers should be filled in with the starting base name and path for the request. These buffers will be modified and the `fullName' buffer filled with the final complete name for the user-selected file. `bufLen' is the length of all the passed buffers. The function returns 0 if the user elected to cancel, 1 if they hit Ok, and negative values for any errors.
typedef int FileReqFunc (const char *hail, char *name, char *path, char *fullName, int buflen); . . .
The global ID "Info Messages" returns a pointer to a MessageFunc structure which provides simple functions for displaying messages to the user. The functions will display different types of messages, each with one or two lines of text. The second string argument can be null for one-line messages.
. . . typedef struct st_MessageFuncs { void (*info) (const char *, const char *); void (*error) (const char *, const char *); void (*warning) (const char *, const char *); } MessageFuncs; . . .
The global ID "System ID" returns a 32-bit unsigned value (cast as a "void *" to be returned from the global function) which is the ID for the system. The 4 high-order bits of this longword indicate the type of the host which is either interactive Layout, interactive Modeler or non-interactive ScreamerNet render module. The low-order bits of the longword are the serial number of this particular copy of LightWave.
Each unique serial number represents a LightWave license, so a plug-in installed on a particular machine can use this value to lock that installation to the LightWave license. If the serial number is zero, then there is no license, or LightWave cannot provide a serial number for that system. ScreamerNet modules will often return a zero serial number indicating that there is no license locking on the render module. Interactive Amiga versions will also return a zero serial number since there is currently no method to assign one.
. . . #define LWSYS_TYPEBITS 0xF0000000 #define LWSYS_SERIALBITS 0x0FFFFFFF #define LWSYS_LAYOUT 0x00000000 #define LWSYS_MODELER 0x10000000 #define LWSYS_SCREAMERNET 0x20000000
This global is only available in version 5.0.
The servers and globals for Modeler share a set of type and value definitions. These are basic to an understanding of the Modeler servers and globals.
Dynamic Values are values of variable type. Unlike normal C types which have a fixed interpretation, dynamic values have a type which can vary according to what is needed. The possible types for a dymamic value are given by the following definitions.
typedef int DynaType; #define DY_NULL 0 #define DY_STRING 1 #define DY_INTEGER 2 #define DY_FLOAT 3 #define DY_DISTANCE 4 #define DY_VINT 5 #define DY_VFLOAT 6 #define DY_VDIST 7 #define DY_BOOLEAN 8 #define DY_CHOICE 9 #define DY_SURFACE 10 #define DY_FONT 11 #define DY_TEXT 12 #define DY_LAYERS 13 #define DY_CUSTOM 14 #define DY__LAST DY_CUSTOM . . .
A dynamic value datatype is a structure whose first field is a DynaType code for the type of the value, followed by varient fields which hold the value in a form appropriate to the given type. The different varient forms of value encoding are listed here.
DY_STRING and DY_SURFACE type values contain a pointer to a string buffer and a buffer size. If the buffer size is zero, the buffer is read-only.
typedef struct st_DyValString { DynaType type; char *buf; int bufLen; } DyValString; . . .
Integer values are used for types DY_INTEGER, DY_BOOLEAN (zero or non-zero), DY_CHOICE (0 - n-1), DY_FONT (font number 0 to n-1) and DY_LAYERS (bit mask for layer set). The default value is only used in requesters as the reset value.
. . . typedef struct st_DyValInt { DynaType type; int value; int defVal; } DyValInt; . . .
Floating point values are used for types DY_FLOAT and DY_DISTANCE (distance measure in meters). The default value is again used when resetting a requester.
. . . typedef struct st_DyValFloat { DynaType type; double value; double defVal; } DyValFloat; . . .
The DY_VINT type is an integer vector with three components. The single default value resets all three components of the vector when used in a requester.
. . . typedef struct st_DyValIVector { DynaType type; int val[3]; int defVal; } DyValIVector; . . .
Floating point three-component vectors are used for the types DY_VFLOAT and DY_VDIST, with the latter being distances encoded in meters.
. . . typedef struct st_DyValFVector { DynaType type; double val[3]; double defVal; } DyValFVector; . . .
The custom dynamic type, DY_CUSTOM, is used to encode values which do not fit one of the standard types. The meaning of the fields following the type for a custom value are defined by agreement between the sender and receiver and are usually a set of 4-byte numbers and pointers, although they can be anything. Usually anywhere a custom value is required, an alternate string form can also be accepted.
. . . typedef struct st_DyValCustom { DynaType type; int val[4]; } DyValCustom; . . .
A DynaValue type is the union of all possible value type varients plus the type code itself which is the only field set for DY_NONE and DY_TEXT types.
. . . typedef union un_DynaValue { DynaType type; DyValString str; DyValInt intv; DyValFloat flt; DyValIVector ivec; DyValFVector fvec; DyValCustom cust; } DynaValue; . . .
Error codes returned from the dynamic data type functions.
. . . #define DYERR_NONE 0 #define DYERR_MEMORY (-1) #define DYERR_BADTYPE (-2) #define DYERR_BADSEQ (-3) #define DYERR_BADCTRLID (-4) #define DYERR_TOOMANYCTRL (-5) #define DYERR_INTERNAL (-6) . . .
At any given moment Modeler holds some set of layers, each containing a potentially large collection of point and polygon elements. The user selects which subset of elements are to be affected by an operation by picking layers as active and inactive, and selecting elements in those layers with the element selection tools. Plug-in operations can decide what elements to operate on as a function of the user's selections.
EltOpLayer codes are used to select which layers will be affected by an operation.
. . . typedef int EltOpLayer; #define OPLYR_PRIMARY 0 #define OPLYR_FG 1 #define OPLYR_BG 2 #define OPLYR_SELECT 3 #define OPLYR_ALL 4 #define OPLYR_EMPTY 5 #define OPLYR_NONEMPTY 6 . . .
EltOpSelect is a selection mode to pick elements from the selected layers for operations.
. . . typedef int EltOpSelect; #define OPSEL_GLOBAL 0 #define OPSEL_USER 1 #define OPSEL_DIRECT 2 . . .
There are two types of servers defined for Modeler which can perform modeling operations. Mesh Edit servers can perform a single mesh data editing operation by affecting the data elements at a fairly low level. Command Sequence servers can execute a sequence of editing operations, including most of those accessable to the user as well as low-level mesh edits.
The "MeshDataEdit" class provides the capability of editing existing layer data though low-level point and polygon operations. The available MeshDataEdit servers in a Modeler session are presented to the user in the "Custom" popup in the "Tools" menu. Editing is done through functions which operate on elements represented by opaque pointers. The editing state itself is also maintained as an opaque pointer and is the first argument to most of the calls.
. . . typedef struct st_Vertex *PntID; typedef struct st_Polygon *PolID; typedef struct st_EditState *EditStateRef; . . .
A mesh edit operation is a single undoable modification to layer data. The server starts the operation and is given an EditStateRef pointer to refer to the ongoing state of the edit operation. The server may then add new elements and modify or delete existing elements. As the server requests changes, those are logged by the host but will not be applied until the operation is complete. At any time the server may abort the operation and the pending changes will be discarded, or it can accept the changes and they will be applied as the last step before the server exits.
Upon activation, a mesh edit server gets a `MeshEditBegin' function pointer as its local data. To initiate the mesh editing operation, the server calls this function and gets back a `MeshEditOp' which contains the data for the edit as well as pointers to all the editing functions. This can be called only once for each activation.
typedef MeshEditOp * MeshEditBegin (int pntBuf, int polBuf, EltOpSelect); . . .
The first two arguments to the function are the client data sizes (in bytes) for points and polygons, respectively. If non-zero, the host will allocate a block of memory for each and every point and polygon for the exclusive use of this edit operation. These client data buffers can be used to associate any information with specific points and polygons for the course of the edit operation, and will be freed when the operation completes. The third argument is the selection option and determines what elements are initially selected.
The version number on activation will be 1 for 4.0 and 5.0.
Servers can get information about existing elements by reading them out into special information structures. The PointInfo and PolygonInfo structures are used to hold information about points and polygons, respectively. Every element has an ID, a userData pointer, a layer number and flags.
. . . #define PPDF_SELECT (1<<0) #define PPDF_DELETE (1<<1) . . .
Except for the memory pointed to by the userData pointer, the contents of info structures or the data they reference are read-only and cannot be modified. Any attempts to do so will either be futile or catastrophic.
In addition to the common parts, a PointInfo struct also includes the point position as a triple of floating point numbers for the X, Y and Z coordinates.
. . . typedef struct st_PointInfo { PntID pnt; void *userData; int layer; int flags; double position[3]; } PointInfo; . . .
In addition to the common parts of the info structure, a PolygonInfo struct encodes the polygon shape as the number of points and an array of their IDs. The surface assigned to the polygon is given by a name string.
. . . typedef struct st_PolygonInfo { PolID pol; void *userData; int layer; int flags; int numPnts; const PntID *points; const char *surface; } PolygonInfo; . . .
Polygons also have some additional flag bits. CCEND and CCSTART are set if the polygon has continuity points at either end. CURVE is set if this is a curve (it is a face if this is clear). DETAIL is set if the polygon is a detail.
. . . #define PPDF_CCEND (1<<2) #define PPDF_CCSTART (1<<3) #define PPDF_CURVE (1<<4) #define PPDF_DETAIL (1<<5) . . .
When the `MeshEditBegin' function starts an edit operation, it returns a MeshEditOp structure which the client uses to execute the edit. This structure contains a few data fields and a large set of function fields.
. . . typedef struct st_MeshEditOp { EditStateRef state; int layerNum; void (*done) (EditStateRef, EDError, int selm); <Mesh Edit Count functions> <Mesh Edit Enumeration functions> <Mesh Edit Query functions> <Mesh Edit Create functions> <Mesh Edit Modify functions> } MeshEditOp; . . .
. . . #define EDSELM_CLEARCURRENT (1<<0) #define EDSELM_SELECTNEW (1<<1) #define EDSELM_FORCEVRTS (1<<2) #define EDSELM_FORCEPOLS (1<<3) . . .
As changes are made they are buffered through the undo mechanism, so they are not reflected in the data until the operation is complete. For example, if a MeshDataEdit client reads the coordinates of a point and changes them (correctly using the `pntMove' function) and reads the coordinates again, they will be the same as the first time. The coordinates will not change until the edits are sucessfully applied using the `done' function.
Errors are integer codes returned from functions and passed to the `done' function. The exceptions are functions which create new elements in which case an error is signaled by a null return value. The BADLAYER error will be returned for an attempt to operate on data not in the primary edit layer. BADSURF will be returned for an illegal surface name. BADARGS is the catch-all for other invalid arguments.
. . . typedef int EDError; #define EDERR_NONE 0 #define EDERR_NOMEMORY 1 #define EDERR_BADLAYER 2 #define EDERR_BADSURF 3 #define EDERR_USERABORT 4 #define EDERR_BADARGS 5 . . .
Clients can get a count of the number of points or polygons in specific layers. The `mode' argument to the count functions specify all the elements, only the selected elements or only the elements deleted in this edit session.
int (*pointCount) (EditStateRef, EltOpLayer, int mode); int (*polyCount) (EditStateRef, EltOpLayer, int mode);
. . . #define EDCOUNT_ALL 0 #define EDCOUNT_SELECT 1 #define EDCOUNT_DELETE 2 . . .
Given a point or polygon ID, the client can get info for that element. The returned info pointer is only valid until the next call to an info function (including enumeration). The normal vector for a polygon may also be found given its ID. The `polyNormal' function returns zero if the polygon has fewer than 3 vertices, or the normal is degenerate for some reason. If it returns 1, then the normal has been written to the caller's vector.
PointInfo * (*pointInfo) (EditStateRef, PntID); PolygonInfo * (*polyInfo) (EditStateRef, PolID); int (*polyNormal) (EditStateRef, PolID, double[3]);
There is only one of each of the PointInfo and PolygonInfo structs for every usage. The same pointer is returned from each query call and passed to the enumeration functions, so the client must copy any information needed before calling the query function again.
The client can traverse all the elements in a layer or combination of layers by passing a callback to be called for each element. These enumeration functions (given by the prototypes below) take as arguments a client data pointer which can be arbitrary, and the info structure for the current element. If the client returns an error code (or any non-zero value for that matter) from this function, the scan will be aborted and that code will be returned.
. . . typedef EDError PointScanFunc (void *, const PointInfo *); typedef EDError PolyScanFunc (void *, const PolygonInfo *); . . .
The following functions initiate a scan of points or polygons in layer data. The client provides an enumeration callback and client data pointer as well as specifying which layers to include in the scan. The function will be called for each point and polygon in order. If the selection mode used to begin this edit was DIRECT, the order of the selected elements is the same as the order that the user selected them. In other select modes, the order is the creation order for points and undefined for polygons. The return value is EDERR_NONE (0) if the scan completed, and the non-zero error code returned by the enumeration callback if the scan was aborted.
EDError (*pointScan) (EditStateRef, PointScanFunc *, void *, EltOpLayer); EDError (*polyScan) (EditStateRef, PolyScanFunc *, void *, EltOpLayer);
A new data element is added by calling the appropriate function, which creates the new element but does not add it to the layer until the edit is completed. Polygons are created from lists of PntIDs which can be the IDs of pre-existing points or of points created in this session, as long as the existing ones are in the primary layer.
. . . typedef struct st_PBoundCv { PolID curve; int start, end; } PBoundCv;
PntID (*addPoint) (EditStateRef, double *xyz); PolID (*addPoly) (EditStateRef, const char *surf, int numPnt, const PntID *); PolID (*addCurve) (EditStateRef, const char *surf, int numPnt, const PntID *, int flags); EDError (*addQuad) (EditStateRef, PntID, PntID, PntID, PntID); EDError (*addTri) (EditStateRef, PntID, PntID, PntID); EDError (*addPatch) (EditStateRef, int nr, int nc, int lr, int lc, PBoundCv *r0, PBoundCv *r1, PBoundCv *c0, PBoundCv *c1);
These functions are used to alter existing data. If called with elements created in this edit session they will return BADLAYER.
EDError (*remPoint) (EditStateRef, PntID); EDError (*remPoly) (EditStateRef, PolID); EDError (*pntMove) (EditStateRef, PntID, const double *); EDError (*polSurf) (EditStateRef, PolID, const char *); EDError (*polPnts) (EditStateRef, PolID, int, const PntID *); EDError (*polFlag) (EditStateRef, PolID, int mask, int value);
The "CommandSequence" class servers can execute a sequence of Modeler commands and/or mesh edits. CommandSequence servers are presented to the user in the "Custom" popup in the "Objects" menu, and the user has the ability to configure the server to take different string arguments. The argument string selected by the user is passed to the server at activation.
Modeling commands are identified by unique case-insensitive names and by unique integer codes. Codes may be looked up given the command string.
. . . typedef int CommandCode; . . .
Commands are executed by passing the command code and a list of arguments in the form of DynaValues. The values can have any type which is can be converted to the required type of each positional argument. A command sequence server can execute any sequence of commands and may combine them with mesh edit operations as well.
A CommandSequence server gets a ModCommand structure passed to its activation function. The activation function performs the sequence of commands and mesh edits and returns when complete.
. . . typedef struct st_ModCommand { void *data; const char *argument; CommandCode (*lookup) (void *, const char *cmdName); int (*execute) (void *, CommandCode cmd, int argc, const DynaValue *argv, EltOpSelect, DynaValue *result); MeshEditBegin *editBegin; } ModCommand;
The version number on activation will be 1 for 4.0 and 5.0.
Here follows a complete list of the commands supported by the command mode interface and their arguments. Optional arguments are listed in square brackets. A more complete description of each command may be found in the Modeler ARexx documentation.
New for 5.0:
Can only be done in OPSEL_USER or OPSEL_DIRECT modes.
Positive numbers indicate division based on knots, negative numbers indicate length. Can only be done in OPSEL_USER or OPSEL_DIRECT modes.
When Modeler is running under Windows, CommandSequence class servers in the program can be triggered by other Windows programs. The Modeler main window looks for messages with a code created by the function RegisterWindowMessage() with the string "LWM CmdSeq Trigger". This message code is unique throughout the Windows session and the arguments of this message describe the server to activate. The first argument (wp) should be null, and the second argument (lp) should be two global atoms containing the CommandSequence server name and argument string, combined with the MAKELONG() macro.
The following Windows function triggers a server in Modeler given the handle to Modeler's main window. Atoms are created to pass the server name and argument (if any) and the message is posted to Modeler's window. If the PostMessage fails, this function frees the atoms, otherwise Modeler will free them when it processes the message. The message code could be looked up only one time if multiple messages are to be sent, and SendMessage could be used for synchronous triggering.
static void TriggerModeler ( HWND wnd, const char *server, const char *argument) { UINT msg; ATOM name, arg; msg = RegisterWindowMessage ("LWM CmdSeq Trigger"); name = GlobalAddAtom (server); if (argument && argument[0]) arg = GlobalAddAtom (argument); else arg = 0; if (!PostMessage (wnd, msg, NULL, MAKELONG (name, arg))) { GlobalDeleteAtom (name); if (arg) GlobalDeleteAtom (arg); } }
This section contains descriptions of the global data pointers which can be accessed from Modeler's global function.
The global ID "LWM: Dynamic Conversion" returns a DynaConvertFunc which can be used to translate a dynamic type element to another type. An error may be returned if the conversion cannot be performed, and hints may be provided when converting strings to integer bitfield or choice values.
typedef int DynaConvertFunc (const DynaValue *, DynaValue *, const DynaStringHint *); . . .
String hints are choice hints and/or bitfield hints. The choice hint is list of strings and values used when converting between DY_STRING and DY_CHOICE types. The pairs indicate a mapping between choice values and strings. The list is terminated with a null item string. The bitfield hint is a list of character codes and bit values used when converting between DY_STRING and DY_INTEGER types. If the character (upper or lower case) is present in the string, the bit value will be ORed into the result, and visa-versa. The list is terminated with a zero bitval.
. . . typedef struct st_DyChoiceHint { const char *item; int value; } DyChoiceHint; typedef struct st_DyBitfieldHint { char code; int bitval; } DyBitfieldHint; . . .
Either field in the string hint structure may be null.
. . . typedef struct st_DynaStringHint { DyChoiceHint *chc; DyBitfieldHint *bits; } DynaStringHint; . . .
The global ID "LWM: Dynamic Request" returns a set of functions for creating and displaying a simple requester. The requesters that can be created with this interface are like simple forms. There is a title and a series of lines each with a label and a control for a single value. The controls are described by DynaValues, with the DynaType determining the type of control and the value determining its setting. The user can change the value of the controls while the requester is displayed.
. . . typedef struct st_DynaReqFuncs { DynaRequestID (*create) (const char *); int (*addCtrl) (DynaRequestID, const char *, DyReqControlDesc *); DynaType (*ctrlType) (DynaRequestID, int); int (*valueSet) (DynaRequestID, int, DynaValue *); int (*valueGet) (DynaRequestID, int, DynaValue *); int (*post) (DynaRequestID); void (*destroy) (DynaRequestID); } DynaReqFuncs; . . .
The basic idea is to create a requester, set up its controls, set their values, post the requester, read out the modified values and destroy the requester. The set/post/get cycle may be done any number of times once the requester is created.
. . . typedef struct st_DynaRequest *DynaRequestID; . . .
Controls in a dynamic requester are determined primarily by a DynaType for the type of data being edited, however for some types additional settings may be required for correct display of the value.
Controls of type DY_STRING have a width, in characters, of the input field. This is an average width on systems with variable pitch fonts.
. . . typedef struct st_DyReqStringDesc { DynaType type; int width; } DyReqStringDesc; . . .
Controls of type DY_CHOICE present a set of labeled buttons for the user to select between. The descriptor contains a pointer to an array of strings (terminated with a null pointer) for the labels of the choice items. If the vertical flag is true, the choices will be set in a vertical layout, otherwise they will be horizontal.
. . . typedef struct st_DyReqChoiceDesc { DynaType type; const char **items; int vertical; } DyReqChoiceDesc; . . .
The DY_TEXT control type is a constant control for displaying lines of text. The text lines are contained in an array of strings (terminated with a null pointer).
. . . typedef struct st_DyReqTextDesc { DynaType type; const char **text; } DyReqTextDesc; . . .
The control descriptor is the union of all these varient records plus the DynaType alone. If there is no special descriptive data for a type, then only the type code is needed to create a control of that type.
. . . typedef union un_DyReqControlDesc { DynaType type; DyReqStringDesc string; DyReqChoiceDesc choice; DyReqTextDesc text; } DyReqControlDesc;
The global ID "LWM: Dynamic Monitor" returns a structure holding functions which can be used to create a monitor for providing feedback on the progress of an operation and allow user to abort it. Monitors are described in the "LightWave Plug-in Architecture" document and are declared in the `moni.h' header file.
. . . typedef struct st_DynaMonitorFuncs { Monitor * (*create) (const char *, const char *); void (*destroy) (Monitor *); } DynaMonitorFuncs; . . .
The global ID "LWM: Custom Commands" returns a set of functions for manipulating the custom commands and function key mappings. These may be changed by a server, but there should be some provision for setting them back to the user's defaults when complete.
. . . typedef struct st_CustomCommandFuncs { int (*listAdd) (const char *name, const char *server, const char *arg); void (*listRem) (const char *name); int (*funGet) (int n, char *server, char *arg, int bufLen); void (*funSet) (int n, const char *server, const char *arg); } CustomCommandFuncs; . . .
For ARexx scripts on the Amiga Modeler, the server name is "$REXX" and the argument is the script name.
The global ID "LWM: State Query" returns a set of functions for querying Modeler's global state. It can be queried at any time although it may only be altered at specific times.
. . . typedef struct st_StateQueryFuncs { int (*numLayers) (void); unsigned int (*layerMask) (EltOpLayer); const char * (*surface) (void); unsigned int (*bbox) (EltOpLayer, double *minmax); } StateQueryFuncs; . . .
The global ID "LWM: Surface List" returns a set of functions which can be used to read and modify Modeler's surface list. Clients may add, rename and modify the contents of surfaces at any time, but there is no capability to remove them. Note that adding surfaces or renaming them will alter the relative order of surfaces in the list.
. . . typedef struct st_SurfaceListFuncs { const char * (*next) (const char *name); void (*create) (const char *name); void (*rename) (const char *name, const char *newName); void * (*getData) (const char *name, int *size); void (*setData) (const char *name, int size, void *data); } SurfaceListFuncs; . . .
The global ID "LWM: Font List" returns a set of functions for reading and modifying Modeler's font list. The list may be modified at any time, but keep in mind that altering the list may affect stored font choices in your requesters, if any.
. . . typedef struct st_FontListFuncs { int (*count) (void); int (*index) (const char *name); const char * (*name) (int index); int (*load) (const char *filename); void (*clear) (int index); } FontListFuncs;
The servers and globals for Layout share a set of type and value definitions which are basic to understanding concepts behind the Layout servers and globals.
There are several conventions used to interpret different types of values within LightWave and throughout this external interface.
Floating point values with a nominal range of 0.0 to 1.0 will sometimes be converted to single-byte values for storing in image buffers. Colors and alphas are converted this way for final image output and other values are used this way internally. The floating point value is clipped to be strictly within the 0.0 to 1.0 range and is then scaled and converted to a BufferValue type so that 0.0 is 0 and 1.0 is 255.
typedef unsigned char BufferValue; . . .
A LightWave item is anthing which can be keyframed in the layout interface. All objects, lights, bones and cameras in LightWave are items and have a unique LWItemID value.
. . . typedef void * LWItemID; #define LWITEM_NULL ((LWItemID) 0) . . .
Types of items are given by LWItemType codes.
. . . typedef int LWItemType; #define LWI_OBJECT 0 #define LWI_LIGHT 1 #define LWI_CAMERA 2 #define LWI_BONE 3 . . .
All items have a set of vector parameters which servers can read (and sometimes write) using property codes.
. . . typedef int LWItemParam; #define LWIP_POSITION 1 #define LWIP_RIGHT 2 #define LWIP_UP 3 #define LWIP_FORWARD 4 #define LWIP_ROTATION 5 #define LWIP_SCALING 6 #define LWIP_PIVOT 7 #define LWIP_W_POSITION 8 #define LWIP_W_RIGHT 9 #define LWIP_W_UP 10 #define LWIP_W_FORWARD 11 . . .
Time values in LightWave are given in two ways. A frame number is the index of a single image (typically the current image) in the output sequence of still images that make up the animation. A time value is the precise instant of an event in seconds. Assuming a scene rendered at 30 frames per second and without motion blur (or with a blur length of zero), frame N is a snapshot of the animation at a time in seconds of N/30. If there is motion blur, then some events in frame N will be from times slightly before N/30 seconds, and if the motion blur length is greater than 100%, then some events may even overlap with the times of events in previous frames.
. . . typedef int LWFrame; typedef double LWTime; . . .
Server functions return errors to LightWave by returning a string pointer. A null string pointer indicates no error, and a non-null pointer points to an error string. The string will be displayed for the user and, except where otherwise indicated, the user will have the option to ignore the error and continue with the operation.
. . . typedef const char * LWError; . . .
Most LightWave plug-ins are "handlers" which manage "instances." An instance is a specific collection of user settings for a texture, image filter, etc., which persist across sessions by being stored in scene and object files. A `LWInstance' is any longword value which identifies a specific instance for a specific server, usually a pointer to allocated memory.
. . . typedef void * LWInstance; . . .
Instances have to load and save their data to and from ASCII scene files and binary object files, and sometimes both, so the data read/write mechanism provides servers with functions to read and write data in both these formats. The `read' function reads bytes from the source and returns the number of bytes read. The `write' function writes bytes to the output and tracks any errors internally. The format of the file is given by `ioMode' code, either OBJECT or SCENE.
. . . #define LWIO_OBJECT 0 #define LWIO_SCENE 1 . . .
If the mode is OBJECT, the format is binary and no scene-specific information should be stored. The read and write functions deal in raw bytes which can have any value from 0 to 255. They read or write the number of bytes requested using the passed buffer.
If the mode is SCENE, the format is ASCII and bytes stored must be in the extended ASCII range of 32 to 255. Values outside this range are ignored or undefined. The read and write functions in this case deal with lines. The write function writes a line at a time and looks for a null terminator in the input rather than the length. The read function can read partial lines if a length less then or equal to the total line length is requested. If the length is greater than the remaining line length, the length is returned and the buffer is null-terminated. The read function returns -1 for the actual end of input, since a read length of zero is valid for a blank line.
. . . typedef struct st_LWLoadState { int ioMode; void *readData; int (*read) (void *readData, char *buf, int len); } LWLoadState; typedef struct st_LWSaveState { int ioMode; void *writeData; void (*write) (void *writeData, char *buf, int len); } LWSaveState; . . .
Plug-in clients which write instance data must do their own versioning so they can read old forms of their own data, and their own bit twiddling to read and write binary data on machines with different byte order and floating point formats. Clients must also make sure they do not read past the end of their own data. This last restriction may be lifted in future versions.
A server manages its instances by providing LightWave with functions to create, destroy, load and save them. The server activation function gets a handler structure which it initializes with the standard instance handler functions listed here, plus whatever else is required by the specific class of plug-in.
The create function should create a default instance which can then be modified by the interface function. The load and copy functions will overwrite existing instances with new values read from a file or a source instance.
The descln function is new for the version 5.0 release. The differences in handlers are flaged by version numbers.
There is often another server associated with a handler and that is the "Interface" server. The activation function for the interface server is called with a LWInstance as its local data. The server will then allow the user to edit the instance description and return. The interface server is just another function that operates on instances of a specific type, but it is separate from the other instance handler functions for two reasons. The first is that the user interface code is frequently the largest and least often used part of a handler, so it makes sense to allow it to be loaded separately only when needed. The second is to permit plug-in authors to easily make "render-only" versions of their plug-in servers for rendering accelerators or packaging bundles.
For example, if the plug-in type was "XXX", there would be two classes of server, "XXXHandler" and "XXXInterface". Then for a specific server of the XXX type, called "MyXXX", there would be a MyXXX defined for the XXXHandler class which would provide all the normal handler functions, and there would also be a MyXXX server of the XXXInterface class which would perform the user interface.
The many server classes for Layout provide a wide range of capabilities and extentions to basic LightWave rendering and animation. Since there are so many servers there may be multiple ways to accomplish the same effect, some better than others.
The "ImageSaver" class, described in the "LightWave Images" document is used by Layout to save output images in different formats.
The "ImageFilterHandler" (and "ImageFilterInterface") class is used to apply image post processing (filtering) effects to the final rendered image. Each filter is applied after all the antialiasing and motion blur passes are complete, and the server modifying the red, green, blue and alpha values of the final image.
In addition to looking at the RGBA of the image, the server can compute its effects based on a potentially large set of full-image buffers, given by the LWBUF codes below. Each of these is a full-screen array of 0-255 BufferValues indicating the presence or absence of that particular attribte for each pixel in the final image.
#define LWBUF_SPECIAL 0 #define LWBUF_LUMINOUS 1 #define LWBUF_DIFFUSE 2 #define LWBUF_SPECULAR 3 #define LWBUF_MIRROR 4 #define LWBUF_TRANS 5 #define LWBUF_RAW_RED 6 #define LWBUF_RAW_GREEN 7 #define LWBUF_RAW_BLUE 8 #define LWBUF_SHADING 9 #define LWBUF_SHADOW 10 #define LWBUF_GEOMETRY 11 #define LWBUF_DEPTH 12 #define LWBUF_RED 32 #define LWBUF_GREEN 33 #define LWBUF_BLUE 34 #define LWBUF_ALPHA 35 . . .
At each frame that the filter is active, the server will get the image to process. It reads the contents of the image buffers and writes new RGB and Alpha data to the output buffer and exits when it has processed the entire frame. This processing is done using a `FilterAccess' structure which contains data fields and functions.
. . . typedef struct st_FilterAccess { int width, height; LWFrame frame; LWTime start, end; BufferValue * (*bufLine) (int type, int y); float * (*fltLine) (int type, int y); void (*setRGB) (int x, int y, BufferValue[3]); void (*setAlpha) (int x, int y, BufferValue); Monitor *monitor; } FilterAccess; . . .
The activation function for an image filter gets passed a blank handler structure as its local data which the server must fill in. In addition to the normal instance functions, it must also provide a `process' function and `flags' function.
. . . typedef struct st_ImageFilterHandler { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); void (*process) (LWInstance, const FilterAccess *); unsigned int (*flags) (LWInstance); const char * (*descln) (LWInstance); } ImageFilterHandler; . . .
This handler is version 2. Version 1 is the same but without the `descln' function.
The "PixelFilterHandler" (and "PixelFilterInterface") class is used to apply image post processing effects to individual pixels in the rendered image. This differs from image filtering in that pixel filtering happens before pixels are antialiased and motion blured. Thus the effects added by pixel filtering have the advantage of being antialiased and motion blured using the same mechanism that LightWave uses on objects. The disadvantage of pixel filters as opposed to image filters is that they can only affect a single sample at a time, so warps and convolutions are impossible.
Pixel filters have access to all the same special buffers as image post process filters, but only a pixel at a time.
For each pixel in the rendered image, the pixel filter will get called with a PixelAccess struct. Because the sampling of the output image is adaptive, pixel positions may be evaluated in any order, multiple times, or not at all.
The access structure also provides pointers to the special `illuminate' and `rayTrace' functions. These are described in the section on procedural textures (shaders).
. . . typedef struct st_PixelAccess { double sx, sy; void (*bufVal) (int type, int num, BufferValue *); void (*fltVal) (int type, int num, float *); void (*setRGBA) (BufferValue[4]); <Special rendering functions> } PixelAccess; . . .
The activation function for the server gets passed a blank handler struct which must be filled in to specify each of the following functions.
. . . typedef struct st_PixelFilterHandler { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); LWError (*init) (LWInstance); void (*cleanup) (LWInstance); LWError (*newTime) (LWInstance, LWFrame, LWTime); void (*evaluate) (LWInstance, const PixelAccess *); unsigned int (*flags) (LWInstance); const char * (*descln) (LWInstance); } PixelFilterHandler; #define LWPFF_RAYTRACE (1<<31) . . .
This handler is version 2 for consistency. There is no version 1 for this handler.
The "ShaderHandler" (and "ShaderInterface") class is for modifying the attributes of a pixel as it is being rendered. These are sometimes called "procedural textures," but in the LightWave implementation they are quite a bit more powerful than that. Since it is called on a per-pixel basis, this interface is designed for speed.
As LightWave goes through the process of converting abstract 3D surfaces into imagery, it breaks surfaces down into tiny patches which each get a uniform color. Computing the color of these tiny spots is done by starting from a set of basic surface parameters which are approximately constant over the patch: base color, surface normal, luminosity, diffuse reflection, specular reflection, reflectivity, transparency, refractive index and roughness (or glossiness). From these values LightWave's illumination calculation computes the color and intensity of reflected light and transmitted light and determines the color of the spot as seen from the given viewpoint. Plug-in shaders can either alter the base parameters and let LightWave do the rendering calculation, or they can perform the illumination themselves and compute the preceived color directly.
The spot evaluation function is called for every visible spot on a surface with a `ShaderAccess' structure describing the spot to be shaded. The access structure contains some values which are read-only and some which are meant to be modified. The read-only values describe the geometry of the pixel being shaded. The read-write values describe the current parameters of this pixel and should be modified in place to affect the final look of the spot. Since shaders may be layered, these properties may be altered many more times before final rendering. The access structure also contains special functions usable only while rendering.
. . . typedef struct st_ShaderAccess { <Read-only shader parameters> <Modifiable shader parameters> <Special rendering functions> } ShaderAccess; . . .
The spot parameters are read-only and describe the local geometry of the spot being shaded.
int sx, sy; double oPos[3], wPos[3]; double gNorm[3]; double spotSize; double raySource[3]; double rayLength; double cosine; double oXfrm[9], wXfrm[9]; LWItemID objID; int polNum;
These parameters are used by the renderer to compute the perceived color at the spot and may be modified by the shader. The shader must return the correct flags for any value it will modify or the change will not take effect (see below).
double wNorm[3]; double color[3]; double luminous; double diffuse; double specular; double mirror; double transparency; double eta; double roughness;
To set the perceived color directly a shader can set all the parameters to zero except for luminous which is 1.0 and color which is the output color of the spot.
These special functions are provided to shaders only in the context of rendering. They cannot be called any other time since they depend very strongly on the state of the host renderer. They can be accessed from some other handler classes as well.
int (*illuminate) (LWItemID light, const double position[3], double direction[3], double color[3]); double (*rayTrace) (const double position[3], const double direction[3], double color[3]);
A shader instance may store its data in an object (in the case of a surface texture) or in a scene (in the case of a clip map) so the save/load functions should be prepared to deal with both cases.
. . . #define LWSHF_NORMAL (1<<0) #define LWSHF_COLOR (1<<1) #define LWSHF_LUMINOUS (1<<2) #define LWSHF_DIFFUSE (1<<3) #define LWSHF_SPECULAR (1<<4) #define LWSHF_MIRROR (1<<5) #define LWSHF_TRANSP (1<<6) #define LWSHF_ETA (1<<7) #define LWSHF_ROUGH (1<<8) #define LWSHF_RAYTRACE (1<<10) . . .
. . . typedef struct st_ShaderHandler { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); LWError (*init) (LWInstance); void (*cleanup) (LWInstance); LWError (*newTime) (LWInstance, LWFrame, LWTime); void (*evaluate) (LWInstance, ShaderAccess *); unsigned int (*flags) (LWInstance); const char * (*descln) (LWInstance); } ShaderHandler; . . .
This handler is version 2. Version 1 is the same but without the `descln' function.
The "DisplacementHandler" (and "DisplacementInterface") class is called upon before rendering to modify the geometry of an object. This is done not only during rendering but also during interactive previewing in the Layout window. This means that a server should always be prepared to process a displacement instance at any time.
At its core a displacement handler takes point coordinates and moves them for each timestep. The access structure for a displacement map gets the position of the point to displace in two ways.
. . . typedef struct st_DisplacementAccess { double oPos[3]; double source[3]; } DisplacementAccess; . . .
The handler functions for a displacement map are the same as a shader except for the lack of `init' and `cleanup' functions. The `newTime' function also has a parameter for the ID of the object being affected by the displacement. The LWDMF_WORLD bit should be set in the `flags' return value if the displacement will take place in world coordinates.
. . . typedef struct st_DisplacementHandler { LWInstance (*create) (LWError *, LWItemID); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to, LWItemID); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); LWError (*newTime) (LWInstance, LWItemID, LWFrame, LWTime); void (*evaluate) (LWInstance, DisplacementAccess *); unsigned int (*flags) (LWInstance); const char * (*descln) (LWInstance); } DisplacementHandler; #define LWDMF_WORLD (1<<0) . . .
This handler is version 2. For backward compatibility, the version 1 handler is listed below. It is the same but without the `descln' function and the `create' and `copy' functions do not have a LWItemID.
. . . typedef struct st_DisplacementHandler_V1 { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); LWError (*newTime) (LWInstance, LWItemID, LWFrame, LWTime); void (*evaluate) (LWInstance, DisplacementAccess *); unsigned int (*flags) (LWInstance); } DisplacementHandler_V1; . . .
The "ItemMotionHandler" (and "ItemMotionInterface") class is used to apply animation behavior to any item in a scene which can be keyframed. After the keyframe position of the item is computed, the item motion server can alter the keyframed motion or replace it with a completely different one. Motions will be evaluated both during rendering and while interactively laying out a scene.
At each time instant and for each affected item, the motion evaluation function will be called with an access structure holding the ID of the item and the time instant for which the motion should be computed. The server can query keyframe parameters for the item and set its own values for the current time.
. . . typedef struct st_ItemMotionAccess { LWItemID item; LWFrame frame; LWTime time; void (*getParam) (LWItemParam, LWTime, double vector[3]); void (*setParam) (LWItemParam, const double vector[3]); } ItemMotionAccess; . . .
Procedural motions are not currently allowed to interact. If a motion evaluation function attempts to read out the position of another object which is affected by a procedural motion, only the values of the keyframed motion will be returned.
The handler for item motions adds only the `evaluate' function to the standard set of handler functions. This computes the motion for an item at a given timestep, and may be called at any time.
. . . typedef struct st_ItemMotionHandler { LWInstance (*create) (LWError *, LWItemID); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to, LWItemID); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); void (*evaluate) (LWInstance, const ItemMotionAccess *); const char * (*descln) (LWInstance); } ItemMotionHandler; . . .
This handler is version 2. Version 1 is included below and is largely compatible with version 2.
. . . typedef struct st_ItemMotionHandler_V1 { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); void (*evaluate) (LWInstance, const ItemMotionAccess *); } ItemMotionHandler_V1; . . .
The "ObjReplacementHandler" (and "ObjReplacementInterface") class allows another type of animation which can replace the entire object geometry at every single step. Replacement is done by object name, so the server evaluation function can provide a new object name to load for each subframe timestep, or it can only load a new object periodically, allowing the same geometry to persist for a length of time.
Filenames are used instead of direct mesh replacement for generality. An object replacement server could use a series of prebuilt objects, like character heads for example, to do expressions or lip-syncing by providing the name of the correct head at each step. Some animation could be done very efficiently using a combination of object replacement and object import servers. The replacement server could write a brief description file for the parameters of a timestep (positions and sizes of metaballs, for example) which the object import server could then convert into a complete mesh while loading. A simple form of this server could be used to replace objects with nulls when they are not visible in the scene.
The access structure passed to the evaluation function contains information about the currently loaded object and the next timestep. The server compares the current settings and the next step and provides a new filename if a different object should be loaded for the next timestep to be evaluated. If the currently loaded geometry can be used for the new frame and time, then the new filename can be set to null.
. . . #define OBJREP_NONE 0 #define OBJREP_PREVIEW 1 #define OBJREP_RENDER 2 . . .
. . . typedef struct st_ObjReplacementAccess { LWItemID objectID; LWFrame curFrame, newFrame; LWTime curTime, newTime; int curType, newType; const char *curFilename; const char *newFilename; } ObjReplacementAccess; . . .
In addition to the normal handler functions, the server provids an `evaluate' function which is called for each affected object at each timestep to get new geometry. This function can be called at any time while rendering or setting up animations.
. . . typedef struct st_ObjReplacementHandler { LWInstance (*create) (LWError *, LWItemID); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to, LWItemID); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); void (*evaluate) (LWInstance, ObjReplacementAccess *); const char * (*descln) (LWInstance); } ObjReplacementHandler; . . .
This handler is version 2. Version 1 is included below and is largely compatible with version 2.
. . . typedef struct st_ObjReplacementHandler_V1 { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); void (*evaluate) (LWInstance, ObjReplacementAccess *); } ObjReplacementHandler_V1; . . .
The "FrameBufferHandler" (and "FrameBufferInterface") class is used to display the output of rendering as each frame is completed. This is for the user to view, so the frame buffer should also be able to pause waiting for user input.
A frame buffer is an instance, but it may be very limited. The built- in frame buffers have no UI and no stored state.
The sequence of calls for rendering to the frame buffer can be visualized as a regular expression:
open, (begin, (write)H, pause?)*, close
Any number of frames may be displayed in a session (even zero). Write will always be called for all the lines in the image and pause is optional.
. . . typedef struct st_FrameBufferHandler { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); LWError (*open) (LWInstance, int w, int h); void (*close) (LWInstance); LWError (*begin) (LWInstance); LWError (*write) (LWInstance, const BufferValue *R, const BufferValue *G, const BufferValue *B, const BufferValue *alpha); void (*pause) (LWInstance); const char * (*descln) (LWInstance); } FrameBufferHandler; . . .
This handler is version 2. Version 1 is the same but without the `descln' function.
The "AnimSaverHandler" (and "AnimSaverInterface") class is used to write out animations. The scheme is nearly identical to framebuffers, except that there is no `pause' function and in addition to the image size, LightWave will also pass a filename for the animation file.
. . . typedef struct st_AnimSaverHandler { LWInstance (*create) (LWError *); void (*destroy) (LWInstance); LWError (*copy) (LWInstance from, LWInstance to); LWError (*load) (LWInstance, const LWLoadState *); LWError (*save) (LWInstance, const LWSaveState *); LWError (*open) (LWInstance, int w, int h, const char *filename); void (*close) (LWInstance); LWError (*begin) (LWInstance); LWError (*write) (LWInstance, const BufferValue *R, const BufferValue *G, const BufferValue *B, const BufferValue *alpha); const char * (*descln) (LWInstance); } AnimSaverHandler; . . .
This handler is version 2. Version 1 is the same but without the `descln' function.
The "SceneConverter" class is used in import foreign scene formats. When the user selects a file to load as a scene, LightWave first attempts to load it directly as an LWSC format file. It it cannot, it will pass the filename to each scene converter in sequence. The scene converter will attempt to read the file and rewrite it as an LWSC file. After successful translation the server will pass the name of the new scene back to LightWave. The file will be loaded and the server will be called back again to delete the translated scene file.
. . . typedef struct st_SceneConverter { const char *filename; LWError readFailure; const char *tmpScene; void (*deleteTmp) (const char *tmpScene); } SceneConverter; . . .
When the server is called, only `filename' will be set. It then must set the other three fields to one of the following configurations:
The "LayoutGeneric" class is provided for general layout functionality which does not fit into any of the previous server or handler categories. Servers of this class can be activated by the user from the Layout interface to perform non-rendering functions, such as configuring external devices, performing calculations, etc.
In version 1 of this class the local pointer is unused, but in version 2 it points to a pair of functions which allow the generic plugin to save the current scene and load a new scene. Each function returns zero for failure. The loadScene function takes a physical filename and a logical filename. The `file' will be opened and loaded, but `name' will be used for display to the user and for subsequent saving.
. . . typedef struct st_LayoutGeneric { int (*saveScene) (const char *file); int (*loadScene) (const char *file, const char *name); } LayoutGeneric;
This section contains descriptions of the global data pointers which can be accessed from LightWave's global function. The ID string for each global is given in quotes.
The global ID "LW Item Info 2" returns functions for traversing the entire set of items in the scene and getting information about all of them. This information is common to all items. Any information specific to certain item types is given by separate global functions.
. . . #define LWVECF_0 (1<<0) #define LWVECF_1 (1<<1) #define LWVECF_2 (1<<2) . . .
typedef struct st_LWItemInfo { LWItemID (*first) (LWItemType, LWItemID); LWItemID (*next) (LWItemID); LWItemID (*firstChild) (LWItemID parent); LWItemID (*nextChild) (LWItemID parent, LWItemID prevChild); LWItemID (*parent) (LWItemID); LWItemID (*target) (LWItemID); LWItemID (*goal) (LWItemID); LWItemType (*type) (LWItemID); const char * (*name) (LWItemID); void (*param) (LWItemID, LWItemParam, LWTime, double vector[3]); unsigned int (*limits) (LWItemID, LWItemParam, double min[3], double max[3]); const char * (*getTag) (LWItemID, int); void (*setTag) (LWItemID, int, const char *); } LWItemInfo; . . .
The global ID "LW Item Info 2" is new for the 5.0 release. The ID "LW Item Info" is defined for 4.0 and 5.0 and returns the same structure above but only up to the `limits' field.
The global ID "LW Object Info" returns functions for object-specific information.
. . . #define LWOSHAD_SELF (1<<0) #define LWOSHAD_CAST (1<<1) #define LWOSHAD_RECEIVE (1<<2) . . .
. . . typedef struct st_LWObjectInfo { const char * (*filename) (LWItemID); int (*numPoints) (LWItemID); int (*numPolygons) (LWItemID); unsigned int (*shadowOpts) (LWItemID); double (*dissolve) (LWItemID, LWTime); } LWObjectInfo; . . .
The global ID "LW Bone Info" returns functions for getting bone-specific information.
. . . #define LWBONEF_ACTIVE (1<<0) #define LWBONEF_LIMITEDRANGE (1<<1) . . .
. . . typedef struct st_LWBoneInfo { unsigned int (*flags) (LWItemID); void (*restParam) (LWItemID, LWItemParam, double vector[3]); double (*restLength) (LWItemID); void (*limits) (LWItemID, double *inner, double *outer); } LWBoneInfo; . . .
The global ID "LW Light Info" returns functions for getting light-specific information.
. . . #define LWLIGHT_DISTANT 0 #define LWLIGHT_POINT 1 #define LWLIGHT_SPOT 2 . . .
. . . #define LWLSHAD_OFF 0 #define LWLSHAD_RAYTRACE 1 #define LWLSHAD_MAP 2 . . .
. . . typedef struct st_LWLightInfo { void (*ambient) (LWTime, double color[3]); int (*type) (LWItemID); void (*color) (LWItemID, LWTime, double color[3]); int (*shadowType) (LWItemID); void (*coneAngles) (LWItemID, double *radius, double *edge); } LWLightInfo; . . .
The global ID "LW Camera Info" returns functions for accessing information specific to the camera. A camera has an ID which must be passed to these functions in anticipation of multiple cameras per scene.
. . . typedef struct st_LWCameraInfo { double (*zoomFactor) (LWItemID, LWTime); double (*focalLength) (LWItemID, LWTime); double (*focalDistance) (LWItemID, LWTime); double (*fStop) (LWItemID, LWTime); double (*blurLength) (LWItemID, LWTime); void (*fovAngles) (LWItemID, LWTime, double *horizontal, double *vertical); } LWCameraInfo; . . .
The global ID "LW Scene Info 2" returns a block of information about the scene itself. This is all strictly read-only.
. . . #define LWRTYPE_WIRE 0 #define LWRTYPE_QUICK 1 #define LWRTYPE_REALISTIC 2 . . .
. . . #define LWROPT_SHADOWTRACE (1<<0) #define LWROPT_REFLECTTRACE (1<<1) #define LWROPT_REFRACTTRACE (1<<2) #define LWROPT_FIELDS (1<<3) #define LWROPT_EVENFIELDS (1<<4) #define LWROPT_MOTIONBLUR (1<<5) #define LWROPT_DEPTHOFFIELD (1<<6) #define LWROPT_LIMITEDREGION (1<<7) . . .
. . . typedef struct st_LWSceneInfo { const char *name; const char *filename; int numPoints; int numPolygons; int renderType; int renderOpts; LWFrame frameStart; LWFrame frameEnd; LWFrame frameStep; double framesPerSecond; int frameWidth; int frameHeight; double pixelAspect; int minSamplesPerPixel; int maxSamplesPerPixel; int limitedRegion[4]; /* x0, y0, x1, y1 */ int recursionDepth; } LWSceneInfo; . . .
The global ID "LW Scene Info 2" is new for the 5.0 release. The ID "LW Scene Info" is defined for 4.0 and 5.0 and returns the same structure above but only up to the `limitedRegion' field.
The global ID "LW Image List" returns functions for traversing LightWave's image list and accessing values in the image. Images are identified by an abstract data type.
. . . typedef void * LWImageID; . . .
. . . typedef struct st_LWImageList { LWImageID (*first) (void); LWImageID (*next) (LWImageID); LWImageID (*load) (const char *); const char * (*name) (LWImageID); const char * (*filename) (LWImageID, LWFrame); int (*isColor) (LWImageID); void (*needAA) (LWImageID); void (*size) (LWImageID, int *w, int *h); BufferValue (*luma) (LWImageID, int x, int y); void (*RGB) (LWImageID, int x, int y, BufferValue[3]); double (*lumaSpot) (LWImageID, double x, double y, double spotSize, int blend); void (*RGBSpot) (LWImageID, double x, double y, double spotSize, int blend, double[3]); void (*clear) (LWImageID); } LWImageList; . . .
The global ID "LW Compositing Info" returns a structure describing the state of the built-in compositing function. The three ImageID's are the background image, the foreground image and the foreground alpha image.
. . . typedef struct st_LWCompInfo { LWImageID bg; LWImageID fg; LWImageID fgAlpha; } LWCompInfo; . . .
The global ID "Global Render Memory" returns functions for accessing the Global Rendering Pool. This is shared memory that can be used while rendering. This has two main uses: The first is for read-only tables, like trig or random noise lookup tables which can be shared by textures. The second is for communication areas for textures that wish to cooperate in terms of sharing computed values on a per-pixel basis. LightWave does nothing to manage this shared pool expect to clear it out after rendering.
The memory chunks are pointers to blocks of memory of different sizes. They are identified by arbitrary null-terminated character strings.
. . . typedef void * MemChunk;
. . . typedef struct st_GlobalPool { MemChunk (*first) (void); MemChunk (*next) (MemChunk); const char * (*ID) (MemChunk); int (*size) (MemChunk); MemChunk (*find) (const char *ID); MemChunk (*create) (const char *ID, int size); } GlobalPool;
Three header files describe the whole set of LightWave servers and globals. `lwbase.h' is for the declarations common to both Layout and Modeler, `lwmod.h' is for Modeler only and `lwran.h' is for Layout only (Rendering and ANimation).
/* * LWSDK Header File * Copyright 1995 NewTek, Inc. */ #ifndef LW_BASE_H #define LW_BASE_H #include <moni.h> #include <plug.h> <Common Server Classes> <Common Globals> #endif
/* * LWSDK Header File * Copyright 1995 NewTek, Inc. */ #ifndef LW_MOD_H #define LW_MOD_H #include <lwbase.h> <Modeling Base Types> <Modeling Types> <Modeling Servers> <Modeling Globals> #endif
/* * LWSDK Header File * Copyright 1995 NewTek, Inc. */ #ifndef LW_RAN_H #define LW_RAN_H #include <lwbase.h> <Animation Types> <Animation Servers> <Animation Globals> #endif