Tables Compiling Table of Contents

Introduction

This is the documentation for the LightWave 3D® Server Development Kit (SDK) for versions of LightWave® beginning with 6.0. Although it will refer specifically to LightWave®, other NewTek products may also support LightWave® plug-ins. Documentation for versions prior to 6.0 and strategies for supporting multiple versions and products are discussed on the compatibility page.

I'll refer to myself in the first person in this introduction, just so you know that the documentation was in fact written by a human being, but to give you fair warning, this is a technical reference with more than a few dry spots. It makes significant demands on the reader, and depending on your degree of familiarity with LightWave®, the C language, and 3D graphics programming, many parts might be completely opaque the first time through.

That's normal. Don't be discouraged by it. The information covered in this documentation is inherently difficult. It's not just you.

Because of this, I've tried very hard to be clear, concise and accurate. I've built on the work of others whose knowledge of this material comes directly from writing the host side of the plug-in API (application programming interface), and some of those same people have made an effort to explain the difficult parts to me. I've corrected a number of mistakes pointed out by readers of earlier drafts, but inevitably, errors and omissions remain, and chances are you'll find at least one.

In the following sections, I recommend books that provide the background you'll need, review a particular programming concept that may be unfamiliar to some, and give a quick tour of the plug-in system with links to important parts of the documentation. For developers with pre-6.0 plug-in experience, I'll also highlight some of the major changes that first appeared in LightWave® 6.0.

Ernie Wright
December 2001

Programming Prerequisites

These pages document the C language interface to LightWave®. They assume that you're comfortable with writing C code, so they won't teach you C. Specifically, they won't discuss abstract dynamic library concepts or the writing of re-entrant, thread-safe code. They also won't teach you 3D graphics programming or, for lack of a better term, the LightWave® user paradigm. All of this information is available from other, better sources.

My favorite book for learning C programming is

  • Al Kelley and Ira Pohl, A Book on C, 4th ed., Addison-Wesley, ISBN 0201183994

You might also want a good algorithms book. I have

  • Robert Sedgewick, Algorithms in C, Addison-Wesley, ISBN 0201514257

3D graphics relies heavily on trigonometry and linear algebra, but in most cases you don't need an advanced knowledge of those subjects. You do need to know what sine, cosine and tangent are, and you need to know how to do vector and matrix arithmetic. Many of the books below include an appendix that reviews the basics. For greater depth, visit the nearest university bookstore and pick up whatever textbook they're using for the introductory courses.

Any good introductory textbook on graphics programming should provide an adequate foundation for understanding the fundamental concepts of computer graphics. I like

  • F.S. Hill, Computer Graphics, Macmillan, ISBN 0023548606
  • Alan Watt, Fundamentals of Three-Dimensional Computer Graphics, Addison-Wesley, ISBN 0201154420

but there are others. Note that both of these are more than 10 years old. That's okay, though. The fundamentals really haven't changed much in that amount of time. For more advanced texts, the canon would include

  • Andrew Glassner (series ed.), Graphics Gems, vol. I - V, Academic Press, ISBNs 0122861663, 0120644819, 0124096735, 0123361559, 0125434553
  • James Foley et al., Computer Graphics: Principles and Practice, 2nd ed. in C, Addison-Wesley, ISBN 0201848406
  • Alan Watt and Mark Watt, Advanced Animation and Rendering Techniques: Theory and Practice, Addison-Wesley, ISBN 0201544121
  • David Ebert et al., Texturing and Modeling, 2nd ed., Academic Press, ISBN 0122287304
  • James Murray and William vanRyper, Encyclopedia of Graphics File Formats, 2nd ed., O'Reilly & Associates, ISBN 1565921615
  • William Press et al., Numerical Recipes in C, 2nd ed., Cambridge University Press, ISBN 0521431085
  • Jackie Neider et al., OpenGL Programming Guide (the red book), Addison-Wesley, ISBN 0201632748

Also consider the two compilation volumes of Jim Blinn's articles in IEEE Computer Graphics and Applications and the often ground-breaking papers in the annual ACM SIGGRAPH Proceedings. In addition to these, you'll also occasionally find chapters and articles specific to writing LightWave® plug-ins in trade books and magazines, some of which are written by members of the LightWave® programming team. Don't forget the LightWave® user manual, the best source of information about how the program works. And, of course, you'll find supplementary material at every level of complexity on the Internet.

But in the event none of this has failed to dissuade you from learning to program by writing LightWave® plug-ins, I will review one programming concept that's fundamental to the way plug-ins work and which may not be easily understood solely by osmosis. If you already know what a callback is, feel free to skip ahead.

Function pointers

LightWave® plug-ins make extensive use of function pointers. For people of my programming generation who grew up on BASIC, FORTRAN and Pascal, function pointers seem a bit exotic at first glance. In a linear, self-contained program, there are relatively few reasons to use them. But function pointers are just another kind of variable, and they become quite useful when two separate modules need to execute each other's code.

The type definition for a particular function pointer might look like this:

   typedef int ( *FooFunc )( int, double );

This says that FooFunc is a function that returns an int (note: not an int *) and takes an int and a double as arguments. Given this definition, you can now declare variables of type FooFunc *,

   FooFunc *foo;

You can write a FooFunc function,

   int myfoo( int count, double size );
   {
      return ( int )( size * count );
   }

and assign this to your FooFunc variable,

   foo = myfoo;

You can also pass FooFuncs as arguments to other functions.

   int bar( FooFunc *foo );

Equivalently, you can explicitly prototype the foo function in bar's function header.

   int bar( int ( *foo )( int, double ));

The standard C runtime library contains at least two functions that take function pointers as arguments, bsearch and qsort, both usually prototyped in stdlib.h. The prototypes look something like this:

   void *bsearch( const void *key, const void *a, size_t n, size_t size,
      int ( *compar )( const void *, const void * ));
   void qsort( void *a, size_t n, size_t size,
      int ( *compar )( const void *, const void * ));

For both of these, you write the comparison function that ranks two elements from the array you're sorting or searching, and you pass this function as an argument to bsearch or qsort. You can sort or search almost anything using these functions, as long as you can write an appropriate comparison function.

Callbacks

The comparison function for bsearch and qsort is an example of a callback, a function you write for other modules to call. The C runtime calls your comparison function whenever it needs to rank two elements from your array.

Callbacks are common in user interface code for modern windowed environments, where they're used to handle "events" triggered by user actions. LightWave®'s built-in user interface facility uses callbacks in exactly this way, but callbacks are also used elsewhere in the plug-in API. Layout handler class plug-ins contain callbacks that are called at certain points during rendering, and Modeler plug-ins use callbacks to enumerate the points and polygons of an object.

You refer to callbacks, of course, by using function pointers.

A Quick Tour

This section is a brief, informal overview of the plug-in system and the way plug-ins work. It points to other areas of the documentation so that you know where the details are explained. You might also want to read through Part 1 of the Box tutorial in the Articles section. It covers much of the same ground by a different route, taking you step by step through the creation of a simple plug-in.

If you're a plug-in oldtimer from the days before LightWave® 6.0 and you just want to get caught up, feel free to skip ahead.

Plug-ins are dynamically linked libraries of code that extend LightWave®'s capabilities. LightWave® ships with dozens of plug-ins, and the source code for many of these is included in the plug-in SDK.

Plug-ins are divided into different types, called classes. These aren't actual C++ classes, although the idea is pretty much the same. The different classes plug into LightWave® at different points and do different things. There are classes for

  • loading and saving images, movies, objects and scenes
  • moving items and modifying parameter channels
  • rendering surfaces, textures, volumes and environments
  • image processing
  • creating and manipulating geometry
  • custom color picker and file dialogs
  • displaying rendered output
  • command-based scene alterations
  • controlling other plug-ins, and
  • providing services accessible to other plug-ins.

All plug-ins have access to functions that provide information or services. These are called globals, and they can be used to get item positions, object geometry, surface settings, camera parameters, system and locale information, and a lot of other data. Globals are also used to build platform-independent user interfaces and to display common interface elements like file dialogs, color pickers, messages, and envelope and texture editors. You can even write your own globals.

A few plug-in classes can also issue commands. Most commands parallel actions the user can take through the LightWave® interface. While globals are used primarily to read parameters, commands are used to set them.

More than half of the plug-in classes are handler classes. Unlike plug-ins that run when they're invoked and then exit, handlers have a persistent lifetime. They supply callbacks that LightWave® can call at the appropriate time to perform their tasks. Most handlers are involved in rendering and are called at each frame to, for example, move objects, paint surfaces, or append the frame to a movie  file. A few handlers respond to user interface events and manage interface objects.

Handlers can be applied, or invoked, multiple times. An item motion handler, for example, can control the motion of several different items in a scene. Each invocation of a handler is called an instance, and for each instance, a handler will create some data that it uses to keep track of that instance. The instance data is normally where handlers hold user settings and precalculated parameters, but it can be anything useful to the handler.

Handlers provide callbacks for loading and saving their instance data in scene and object files. The actual reading and writing of data in these files is accomplished through file I/O functions provided by LightWave®. A global allows plug-ins of any class to use these same functions with other files, which can be useful for creating and reading platform-independent configuration files for your plug-in, for example.

The file I/O functions are one of several mechanisms shared by multiple classes with similar needs. Two more are the image I/O system used by image and animation loaders and image savers, and the raytracing functions used by shaders, volume renderers and filters.

Every plug-in has an activation function. This is the entry point for the plug-in, the function LightWave® calls to begin the interaction between the program and your plug-in. For non-handlers, this is where all of the work of the plug-in is done, but for handlers, this is only where the plug-in tells LightWave® how to find the plug-in's callbacks. The activation function has the same form for all plug-in classes, with a single argument that differs for each class.

A plug-in file can contain more than one plug-in. Each file contains an array of server records, one for each plug-in in the file. The server record for a plug-in lists the name and the class of the plug-in and the address of the plug-in's activation function. The server record array is an external data structure, a data block in the file that the operating system can locate by name. When LightWave® first loads a plug-in, it asks the operating system to return the address of the server record array, and then it finds in that array the addresses of the activation functions the file contains. It can later call the activation function and, for handlers, obtain the addresses of other functions in the file.

Most plug-ins provide a user interface, and they normally display it as part of their activation function processing. Handler classes have associated interface classes whose activation functions are dedicated to this purpose. You can build your interfaces using platform-specific elements, but the SDK provides a complete, platform-independent system for building interfaces with elements that have LightWave®'s look and feel. This system is described on the Panels and XPanels pages.

So that you don't have to understand all of this all at once, start by trying to compile the example source that's included with the SDK. Once you're compiling successfully, you can experiment by altering the examples before moving on to creating your own plug-ins.

What's New

If you've written plug-ins for versions of LightWave® prior to 6.0, much of the current API will seem familiar, but a lot has changed.

Classes - There are new classes for anim loading, channels, custom nulls in Layout, environment (backdrop) rendering, multiple plug-in mastering in Layout, custom Modeler tools, procedural textures, and volumetrics. Many of the other classes have been significantly enhanced, and nearly all of them have changed at least slightly to reflect the new architecture.

Globals - The number of globals has nearly doubled, and many of the familiar ones have grown substantially. Among the new globals are a number of user interface components, including the XPanels alternative to classic panels, standard access to the current color picker, and off-screen bitmaps that can be blitted onto panels. A revamped envelopes global is joined by related channels and variant parameter globals. Layout can tell you about the state of its interface, and modified handler instances can update Layout.

Plug-ins can save images through any installed image saver and use file I/O functions for creating and reading block-structured files. They can incorporate any of the installed procedural textures using globals for evaluating them and for displaying a standard texture editor to the user. Globals are available to help with managing presets and displaying previews. Layout now exposes detailed geometry data for every object in the scene and allows plug-ins to read and modify surface settings and manage particle system data.

Handlers - The local arguments to handler activation functions have changed. create, destroy and so on are still in there, but they've been reorganized and standardized. The interface activation functions associated with handlers now receive a structure rather than just their instance data, and many classes will be able to use this structure to draw their interface controls onto LightWave®'s own panels. Some handlers can now be run in Modeler to provide previews, and since the world looks different in Modeler, this case needs to be treated carefully.

Other Changes - The ServerRecord now includes an optional array of tag strings for each plug-in. Among other things, these tags allow you to list language-specific names for your plug-ins. If your plug-in supplies a list of names, the plug-in name LightWave® displays to the user will depend on the locale of the user's system.

The XCALL_INIT macro has been deprecated, meaning that it's no longer required. You can still use it, but it doesn't do anything on any currently supported platform and isn't likely to return in the future.

All class name and global identifier strings have been assigned preprocessor symbols. For future compatibility, you should use these symbols, rather than the string literals, in ServerRecord references and calls to the global function.

Further Information

For updates, additional example source code, contact information, and information about the LightWave® plug-in developers' Internet mailing list, visit NewTek's websites,

http://www.newtek.com
http://www.lightwave-outpost.com