plugin cookbook recipe #1

getting a colour from a gradient element in a description

Gradients are most often seen in a shader, but they can also be useful in a description, when they can return a colour or an interpolated value along a scale. The question is, how do you get the colour from a gradient in a description?

This is more difficult than it seems (or ought to be). You would think that a simple function call to the gradient would be enough, but there is no such single call, and you have to jump through several hoops before you can use your gradient.

As an example, suppose you have an ObjectData plugin and you want to get a colour from a gradient to apply to your generated object. This colour must be dependent on the frame number. So that, in a scene with 90 frames, at frame 60 you want the colour two-thirds of the way across the gradient from left to right. How do you do this?

1. In the description resource

You first set up the gradient in the description resource. This is easy:

GRADIENT MY_GRADIENT { COLOR; }

You will also need to give this element an ID value in the corresponding header file, of course.

2. In the Init( ) function of the plugin

This is where you set up the gradient. You need to allocate a Gradient class and set the initial colour(s) you want it to have – otherwise it will be solid black. Then you assign the Gradient to the description element. The code might look like this:

// set up the gradient
AutoAlloc<Gradient> gr;
if(gr)
{
    GradientKnot gk1, gk2;
    gk1.col = Vector(1, 0, 0);    // red
    gk1.pos = 0.0;        // left edge of gradient
    gk2.col = Vector(0, 0, 1);    // blue
    gk2.pos = 1.0;        // right edge of gradient
    gr->InsertKnot(gk1);
    gr->InsertKnot(gk2);
    data->SetData(MY_GRADIENT, GeData(CUSTOMDATATYPE_GRADIENT, *gr));
}
else
    GePrint("Could not allocate a Gradient.");

3. In the GetVirtualObjects function of the plugin

In this example this is probably where you would want to read the colour. Firstly, you need to get the Gradient class from the description element. No problem: we can use the GetCustomDataType function of the object’s base container. Then we want to use it to get the colour. There is a function in the Gradient class called CalcGradientPixel that looks as if it might do. But if you call this, all you get is black, no matter what colour the gradient really is. To get the colour you have to call the gradient’s InitRender function, then call CalcGradientPixel, then call FreeRender at the end. Which is a lot of (unnecessary in my opinion) code to get a colour value! Anyway, here is the code which would be called from GetVirtualObjects().

BaseObject* MyObjectDataPlugin::GetVirtualObjects(BaseObject* op, HierarchyHelp *hh)
{
    BaseContainer *data = op->GetDataInstance();
    BaseDocument *doc = hh->GetDocument();
    LONG currentFrame = doc->GetTime().GetFrame(doc->GetFps());    // get the current frame number
    // assume the default 90 frames in this document, but you would obviously need to check this in your plugin
    LONG totalFrames = 90;
    Vector grcol = GetGradientColor(data, currentFrame, totalFrames);    // get the gradient colour
    // do whatever you want with the colour now
    // ...
}

Vector MyObjectDataPlugin::GetGradientColor(BaseContainer *data, LONG currentFrame, LONG totalFrames)
{
    Vector grcol = Vector(0.0);
    Gradient *gr = (Gradient*)data->GetCustomDataType(MY_GRADIENT, CUSTOMDATATYPE_GRADIENT);
    if(gr)
    {
        InitRenderStruct irs;
        // calculate the correct position along the gradient, must be between 0 and 1
        Real pos = FCut(currentFrame / totalFrames, 0.0, 1.0);
        gr->InitRender(irs);
        grcol = gr->CalcGradientPixel(pos);
        gr->FreeRender();
    }

    return grcol;
}

You can now use the returned colour however you want. You could also use the returned value as a setting for an alpha, or for any scale that needed a value between black and white, where you could treat black as 0 and white as 1. (In that case, why not just use a conventional slider? The gradient is more complex to use but you can do things like introduce turbulence for more randomised values, or by adding knots you can set peaks and troughs in the returned value. A slider returning a Real value can't do that.)