Coding the shader 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.

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 1025517 
#endif

The ID is a true ID from Plugin Cafe so will not clash with any other plugin.

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, Int32 type, void *data);
    virtual Vector Output(BaseShader *sh, ChannelData *cd);
    virtual INITRENDERRESULT InitRender(BaseShader *sh, const InitRenderStruct& irs);
    virtual void FreeRender(BaseShader *sh);
    virtual SHADERINFO GetRenderInfo(BaseShader *sh);

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

    BaseShader *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, which are derived from the BaseShader class. 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"

// start the plugin
Bool PluginStart(void)
{

    // 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.

Page last updated June 23rd 2021