creating plugins for c4d: creating a simple channel shader plugin (part 2)

«  1  2  3  »  

coding the plugin

I like to create the header file for the plugin first. This way I don’t get (so many) compilation errors on the first compile! At first all this will contain is the plugin ID and a class definition for the main plugin class.

You get a plugin ID from Maxon’s Plugin Cafe. It is essential that each plugin has a unique ID. If plugin IDs clash (i.e. two or more plugins have the same ID) only one will load, and quite possibly neither will. There are ten numbers, 1000001 to 1000010, set aside for testing, but a unique ID must be obtained before release.

To get an ID, go to Plugin Cafe, create an account if necessary by clicking the ‘Register’ button at the top right, then click ‘Get Plugin ID’. The site asks you to enter a name for the plugin, then assigns an ID to it. From now on only you can use that ID in your plugin.

Important! The code below is compatible with C4D R11.5. If you are working with R12, please note that you may have to change some function names and parameters for it to compile.

the header file

We can then define this ID in the header file:

// BitMap Flipper
// test channel shader plugin
// bmflip.h

#ifndef ID_BMFLIP
#define ID_BMFLIP 1000005 
#endif

Note that I’ve used a test number here, rather than a true ID from Plugin Cafe. This is just in case you compile this plugin and clash with mine!

There is one global function in the plugin code, which we need to declare. This is the function which registers the plugin with C4D, so we can add it to the header:

// forward declarations
Bool RegisterBMFlip(void);

Finally, there is the class definition for the plugin. Here it is:

// class definition for shader
class BitmapFlip : public ShaderData
{
public:
    virtual Bool Init(GeListNode *node);
    virtual Bool Message(GeListNode *node, LONG type, void *data);
    virtual Vector Output(PluginShader *chn, ChannelData *cd);
    virtual LONG InitRender(PluginShader *chn, InitRenderStruct *irs);
    virtual void FreeRender(PluginShader *chn);
    virtual LONG GetRenderInfo(PluginShader *sh);

    static NodeData *Alloc(void) { return gNew BitmapFlip; }

    PluginShader *shader;
    Bool flipX;
    Bool flipY;
};

This needs a bit more explanation. The class BitmapFlip, which contains the code of the shader, is derived from one of the SDK classes – ShaderData – which is the base class for channel shader plugins. It contains six functions declared as virtual. We need to override these functions – i.e. provide the code for them, because C4D will call these functions at some point to do some work. You can guess what some of them mean, but we’ll come back to them later.

Then there is a static function which allocates a new class of type BitmapFlip. It has to be static because it’s going to be called during the plugin registration process, which takes place before any BitmapFlip classes have been instantiated.

Finally there are three class-level variables which the shader will need to access.

main.cpp

Now we need a C++ file containing the code which gets the plugin started. Here’s the complete file:

// BitMap Flipper
// test channel shader plugin
// main.cpp

// includes
#include "c4d.h"
#include "bmflip.h"

// forward declarations
C4D_CrashHandler old_handler;

// crash handler init
void SDKCrashHandler(CHAR* crashinfo)
{
    // don't forget to call original handler
    if(old_handler) (*old_handler)(crashinfo);
}

// start the plugin
Bool PluginStart(void)
{
    // install crashhandler
    old_handler = C4DOS.CrashHandler;                       // save original handler (must be called!)
    C4DOS.CrashHandler = SDKCrashHandler;            // insert new handler

    // register plugin
    GePrint(" ");
    GePrint("--------- Microbion Software ---------");
    if(!RegisterBMFlip())
    {
        GePrint("Failed to register Bitmap Transform shader.");
        GePrint(" ");
        return FALSE;
    } else {
        GePrint("Bitmap Transform shader successfully loaded.");
        GePrint(" ");
        return TRUE;
    }
}

void PluginEnd(void)
{
    // nothing to do but return
    return;
}

Bool PluginMessage(LONG id, void *data)
{
    switch(id)
    {
    case C4DPL_INIT_SYS:
    if(!resource.Init())
    {
        GePrint("BMFlip shader: failed to load resource file.");
        return FALSE;                                                                     // don't start plugin without its resource
    }
    else
        return TRUE;
    break;

    case C4DMSG_PRIORITY:
        return TRUE;
        break;

    default:
        return FALSE;
    }
}

Breaking this down, we need to include the header file we created and a C4D SDK header – c4d.h – which contains declarations for the C4D functions we call in this file. Then there is a section which seems to be included in every plugin code I have seen – the crash handling code. No, I don’t know what it does either but it doesn’t do any harm, so it can be left in.

PluginStart() and PluginEnd()

The function PluginStart() is pretty self-explanatory; this is the main start point for the plugin. C4D will call this function when the plugin is loaded, so it must be provided by the plugin author. In this case, apart from the crash handler stuff which we’ll pretend isn’t there, it just calls one function – RegisterBMFlip() – which we need to provide. This returns a Boolean. If the return value is TRUE, we know the plugin was registered and just for the user’s information we can print a message to the C4D console saying so (using the SDK function GePrint()). This message can take whatever form you like. It could include the author’s name, maybe a web address, the version number and date, and so on. If on the other hand we get a return value of FALSE, something went wrong and we need to inform the user of that as well.

Just as with PluginStart(), there’s also a PluginEnd(), called when the plugin exits. In this case there’s nothing to do , but you could use this to free up resources, such as allocated memory, for example. If you don’t do that, you run the risk of memory leaks.

PluginMessage()

The next function, PluginMessage(), is where C4D sends messages to the plugin telling it something. Other plugins can also send messages to our plugin and they will be received here. The only one we really need to respond to in this case is the message C4DPL_INIT_SYS, which basically tells the plugin to initialise itself. We respond to that by telling the system to load the plugin’s resource – the description we created for it, in this case. (How does Cinema  know which resource to load? We’ll come back to that later.) Assuming the resource is loaded, we can return a TRUE value, or FALSE if not. In practice it doesn’t make much difference; if the resource didn’t load, the plugin won’t have an interface to display.

All this code goes in a file named main.cpp.

On the next page, we'll add the code for the shader itself.

Back to main tutorials page

«  1  2  3  »