Developing an Actions plug-in for OpenFX

Introduction

The Designer program has two main ways of building, Geometry creating modules..

Building and Files

Each designer geometry plugin is built as a Windows DLL, it will have one or more C or C++ language source file, one or more project specific header files, a Visual Studio project file, a resource file (.RC) ,  a module definition file (.DEF) a file with filename extension (.AFX for actions plugin, .DFX for a tools plugin).  The project will build to a .DLL of the same name as the MFX/DFX file.  The build files can have any location, but the AFX/DFX and DLL files must be placed in the "PLUGINS" folder.

The plugin source files need to include two header files that are located in the "DESIGN" folder.  These are "struct.h" and "dstruct.h" . These define the Designer's data structures and provide prototypes for the Designer's internal functions that can be used from within a plugin.  The Designer's functions that the developer can use are not defined in a library, they are accessed through a pointer that is passed to the plugin at run time. However, they can be used in the plugin as if they were ordinary functions. There is no need to link any of the plugins with a special library. (We will return to the source code later.) 

In the case of an Actions plugin, the resource file (.RC) will usually contain a dialog box description to allow the user to set up the action.  To get a feel for a plugin action we shall look at a plugin for the Delaunay triangulation discussed in my book "Practical Algorithms for 3D Computer Graphics. The Triangulate action takes a matrix of isolated vertices lying an a plane and produces a triangulation that maximises the equi-angularity of the polygons.

 

The behaviour of this tool and its controlling dialog looks like this:

delaunay.png (89221 bytes)

 

The screen on the top shows a group of selected vertices before the action starts. The screen on the bottom shows the results of the action.

The files associated with the Triangulate actions are:

these are located in the design folder, and

located in the plugins folder.

The build project file is located in the "design\vc7" folder

The build process will create a DLL file called delaunay.dll in the plugins folder.  As mentioned above the delaunay.afx file contains the text that will appear in the OpenFX plugin actions menu. In this case the file contains the text: "Triangulate...  "  so this is text that will appear on the menu. For this particular plugin action there is a very simple user interface dialog that just explains the action of the tool.

Building is done in Visual Studio .using the Delaunay project. (You can see this in the Designer build Solution (.SLN) file that contains all the modelling plugin projects as well as the main designer.  If you want to start writing a new Actions plugin then we suggest you copy the files from this plugin to a new set. Modify the .VCPROJ files using a simple text editor to reflect the name change and then add that to the designer solution. (or set up an individual solution to contain it.)  

This concludes all we have to say about building. Most of the other plugins in OpenFX follow a similar strategy.

The plugin-code

The plugin code follows the basic design of a Windows DLL . In this section we will examine the parts of the Delaunay action source file that are common to all actions plugins. The most important items to be aware of are the name of the function called by OpenFX's designer module to execute the action called _Xmodeler(), the include files that define the OpenFX designer datastructures "struct.h" and the external function definition and global variable definition header file "dstruct.h"

The OpenFX method of accessing global variables and main program functions   (VERY IMPORTANT)

OpenFX uses a pointer mechanism for allowing the plugins to access functions in the main program and program global variables. Since all OpenFX plugins are DLLs that are loaded into the main applications address space. The plugin DLLs are NOT loaded when the main application starts (many of the built-in actions are also implemented as DLLs that ARE loaded when the program starts) they are loaded when required and unloaded when they have finished.
In the case of plugins that are used in the Designer and Animator AND are needed by the Renderer (e.g. shaders) then the Renderer loads all the needed plugins when it reads the script file during initialisation. The Renderer unloads the plugins when it is finished with the script.

When the Designer requires an external action to be performed it calls  the function _Xmodeler(), with three arguments:

BOOL _Xmodeler (HWND parent_window,HWND info_window,X__STRUCTURE *lpevi){

The first two arguments are handles for the Designer's container window and the Designers information/status window at the bottom of the screen. Getting these handles allows the plugin to send messages to the designer, for example to write some informative text into the status window. The third parameter is the most important.   The "lpevi" parameter is a pointer to the X__STRUCTURE data structure. The X__STRUCTURE structure is a global structure that contains pointers to the main data that the Designer module is currently holding, all the lists of vertices, edges, faces, skeleton, materials, maps etc.  the X__STRUCTURE also has pointers to many of the Designers internal functions, such as those that create vertices, manipulate dialog boxes, etc. etc.

Part of this structure looks like this:

typedef struct tagX__STRUCTURE {
unsigned long *Nvert,*Nedge,*Nface,*Nskel,
...
sruct tagVERTEX **MainVp;
...
long *dummyv[60];
void (*fpDA)(short);
void (*fpCV)(void);
...
void *dummyf[52];
} X__STRUCTURE;

The designer keeps only one copy of this structure, it is initialised at start-up and passed to all the plugins.  By using the members of this structure any plugin can access all the Designer internal data and functions. This method is very simple and very flexible, just by using up some of the dummyV and dummyF variables extra variables and functions can be made available to the plugins in future releases of OpenFX without compromising the behaviour of existing plugins even at the binary level.

In order that the contents of this structure can be made available to all functions and files in a plugin the pointer passed in to _Xmodeler should always be copied into the global variable:

X__STRUCTURE *lpEVI;

This global variable is located in the standard include file "dstruct.h". Thus, the standard entry point for all plugins will resemble something like:

BOOL _Xmodeler(HWND parent_window,HWND info_window,X__STRUCTURE *lpevi){
lpEVI=lpevi;
....
// remainder of code...
return TRUE;  // if effect worked  FALSE if it failed.
}

Accessing variables and functions in a DLL module through direct use of the lpEVI structure pointer is very tiresome.  For example to use the Designer's number of selected  vertices variable (called "NvertSelect")  it would be necessary to use the code (  (*(lpEVI->NvertSelect))    )   to  gain access to this variable.   This is very messy!  To overcome this issue and provide an interface to the designer's functions that make them look (in the code) just like normal function calls, a number of #defines are included in the header file "dstruct.h" , which is one of the two ".h" files that every plugin must include.

Using these defines, calls to functions and use of global variables can be used as if they were part of the main Designer module code.  for example,

To increase the memory space for holding mesh edge data, and then add an edge to this list the code below can be used.:

...
...

if(!UpdateEdgeHeap(Nedge+3))return NULL;
  CreateEdge(vp,w0); e0=Nedge-1;
}
...
...

Keeping this very important information in mind we can return to considering the Delaunay triangulation plugin action and look at the key code constructs.

Essential headers and global variables
#include <stdlib.h>
#include <windows.h> 
#include "struct.h"
#include "dstruct.h" 
static HWND hParent;
static HINSTANCE hThisInstance; 
DLL standard entry code for Visual Studio compiler
BOOL WINAPI DllMain(HANDLE hDLL, DWORD dwReason, LPVOID lpReserved){
switch (dwReason) {
case DLL_PROCESS_ATTACH: {
hThisInstance=hDLL;
if(hDLL == NULL)MessageBox( GetFocus()," NULL instance",NULL, MB_OK);
break;
}
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Exported function to implement the action.
BOOL _Xmodeler (HWND parent_window,HWND info_window,X__STRUCTURE *lpevi){
lpEVI=lpevi;  // set pointer  
// Implement the effect.
}

The Delaunay example

To return to the delaunay example, the algorithm for the Delaunay example and most of the useful code is explained in the book Practical Algorithms for 3D Comptuer Graphics...  In this section we will not examine the algorithm we will just look at a key steps in the code and how they use the Designer's internal functions.

The key actions involve:

This completes our discussion of the Delaunay example. You should look at the code for the other actions plugin, find and identify the similarities with this example and follow the logic of the argument. .

Go back to the developer page...