Generating the object

In part 3 we saw how GetVirtualObjects returns the new object for Cinema to display. The actual object generation takes place in a class member function called GenerateObject(), which is listed below.

GenerateObject()

BaseObject* Diamond::GenerateObject(Vector *padr, Int32 pcnt, Int32 vcnt, BaseThread *bt)
{
    PolygonObject *op = NULL;
    Vector *cpadr = NULL;
    CPolygon *vadr = NULL;
    UVWTag *uvtag = NULL;
    UVWHandle uvwptr;
    UVWStruct us;
    Int32 i;

    op = PolygonObject::Alloc(pcnt, vcnt); // allocate a new polygon object with the specified number of vertices and polygons
    if(op)
    {
        vadr = op->GetPolygonW(); // get the array of polygons
        cpadr = op->GetPointW(); // and the array of vertices
        // set point values
        for(i = 0; i < pcnt; i++) // copy over the supplied array
        {
            cpadr[i] = padr[i];
        }
        // for each polygon, set the vertices which it uses (the vertex order is important to avoid reversed normals)
        vadr[0] = CPolygon(0, 1, 5, 5);
        vadr[1] = CPolygon(1, 2, 5, 5);
        vadr[2] = CPolygon(2, 3, 5, 5);
        vadr[3] = CPolygon(3, 0, 5, 5);
        vadr[4] = CPolygon(1, 0, 4, 4);
        vadr[5] = CPolygon(2, 1, 4, 4);
        vadr[6] = CPolygon(3, 2, 4, 4);
        vadr[7] = CPolygon(0, 3, 4, 4);

        // create a UV tag for use if the user makes the object editable
        uvtag = (UVWTag*)op->MakeVariableTag(Tuvw, op->GetPolygonCount());
        if(uvtag)
        {
            uvwptr = uvtag->GetDataAddressW();
            for(i = 0; i < op->GetPolygonCount(); i++)
            {
                us = UVWStruct(Vector(0.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0), Vector(1.0, 1.0, 0.0), Vector(0.0, 1.0, 0.0));
                uvtag->Set(uvwptr, i, us);
            }
        }

        // return the generated object
        op->Message(MSG_UPDATE);
        return (BaseObject*)op;
    }
    else
        return NULL;
}

The function is called from GetVirtualObjects(), which passes the array of point positions, the number of points, and the number of polygons we want to the new object to have. We first allocate a PolygonObject, using the required number of points and polys it must have, then we get pointers to the point and polygon arrays that have been allocated by Cinema for the object.

Next, we need to copy across the supplied point vectors to our new object. Then we need to create each of the eight polygons. Each polygon in the polygon object is made up of four point index values. So for example, if in an object a polygon is formed by points 1, 5, 32, and 445, those are the point index values the polygon must have. Cinema then simply renders the polygon formed by those points.

What we have to do here is work out which points form which polygons. For example, the first polygon will be formed by two of the vertices from the four in the middle of the object, plus the one at the peak. These happen to be points 0, 1, and 5, so these values are inserted into the first entry in the polygon array. Now, this polygon only has three points, in fact they all do - they are all triangles. What value then do we give for the fourth point index? When constructing a triangle you always make the third and fourth point indices the same. You can see that I've done that in each case here.

The order of the points is important, in that the they should proceed clockwise. If you look at the locations of the points 0, 1, and 5, you see that in the first call to CPolygon() the order of the point positions is clockwise. If it's not, you'll get reversed normals in the object.

We just repeat that for all 8 polygons and that's our object done. Next, we add a UVW tag. We don't actually need to do that, but if we don't then when the user makes the object editable, it won't have a UVW tag and they'll have to add one manually (which is a serious irritation if you ever have to do it!). We don't do anything fancy with the UVWs though. After creating the tag we just assign the same value for each polygon. If you look at the UVs of this object in BodyPaint, you'll see that they are all piled on top one another.

Finally, we're done and the generated object can be returned to GetVirtualObjects().

RegisterDiamond()

Here's the code to register the plugin, which is normally called from PluginStart():

Bool RegisterDiamond(void)
{
    // register our plugin with the system
    return RegisterObjectPlugin(ID_DIAMOND, GeLoadString(IDS_DIAMOND), OBJECT_GENERATOR, Diamond::Alloc, "odiamond", AutoBitmap("diamond.tif"), 0);
}

The only thing to note here is that the plugin flag OBJECT_GENERATOR must be set. If it isn't, nothing will be generated. For some ObjectData plugins you might need to set other, or additional, flags - the SDK lists those for you.

main()

As with other plugins this will require functions such as PluginStart() and PluginMessage() to start it up when loaded. These are in the file main.cpp, which I won't list here as there's nothing new at all.

This is clearly a very simple object but I hope it shows you how simple it is to write new parametric objects for Cinema. There's a lot of scope for improvement in this plugin - a new depth parameter, make the height of the bottom and top parts of the object be set independently, introduce a height subdivision setting, make it possible to flatten the top or bottom, increase the number of divisions around the object to get a diamond with more than four sides, etc.

Feel free to modify as much as you like, but do remember to get a unique plugin ID if you 're going to use it for real. If you don't then one day you WILL get a plugin ID conflict - guaranteed.

You can download the full source code and resource file from this link:

Download source files Diamond plugin source files

Page last updated June 23rd 2021