section 6 - creating an object plugin (part 2)

«  1  2  3  4  »  

Here is the source code for the plugin, split into manageable chunks. I've added a lot of comments to it so I won't go through it all, just the more important and/or more obscure stuff.

ImportantThis code is compatible with Cinema 4D R13. If you are using another version, you may need to alter some of the code. The parts that will probably need some attention is the code to do with the resizing handles, as this changed significantly in R13. The SDK examples from R12 and earlier will show you how it was done in those versions.

Our plugin has to do four things:

set up

Bool Diamond::Init(GeListNode *node)
{
    BaseObject *op = (BaseObject*)node;
    BaseContainer *data = op->GetDataInstance();

     // give the obejct a phong tag with appropriate values
    op->SetPhong(TRUE, TRUE, Rad(50.0));

     // initialise the description
    data->SetReal(DIAMOND_WIDTH, DEFAULT_DIAMOND_WIDTH);
    data->SetReal(DIAMOND_HEIGHT, DEFAULT_DIAMOND_HEIGHT);

     return TRUE;
}

void Diamond::GetDimension(BaseObject *op, Vector *mp, Vector *rad)
{
    BaseContainer *bc = op->GetDataInstance();
    Real sizeH, sizeW;

     // we need to return the bounding box radius, so we divide the height and width by 2
    sizeH = bc->GetReal(DIAMOND_HEIGHT) / 2;
    sizeW = bc->GetReal(DIAMOND_WIDTH) / 2;

     // the centre of the box wil be the centre of the object
    *mp = Vector(0.0);
    // return the radius
    *rad = Vector(sizeW, sizeH, sizeW);
}

These functions are straightforward.

Init()

Init() does some basic set up, and the only thing to note is that it adds a phong tag. This isn't quite the way the SDK examples do it, but since they fail to add the tag correctly this at least works. Note that the tag is being added to the ObjectData object, not the generated diamond, but we'll take care of that later. If we don't do it this way, there seems to be no way to add a phong tag to the parametric object. SetPhong is nice because it both creates a new phong tag and sets its parameters in one go. I chose 50 degrees as the setting rather than the usual 80 because otherwise the user would have to alter the value after the object was created.

GetDimension()

GetDimension() does exactly what it says: Cinema will call this to get the size of the object's bounding box. It asks for two things: the size of the box, which we calculate and put into the vector pointed to by *rad, and the location of the box's central point. In this case the centre of the box is the same as the centre of the object, so we just return a vector with all values set to zero. Finally, note that Cinema wants the radius of the bounding box, not its actual size, and that in this object the vector's x and z members are the same as it is symmetrical.

drawing the resizing handles

Draw()

DRAWRESULT Diamond::Draw(BaseObject *op, DRAWPASS drawpass, BaseDraw *bd, BaseDrawHelp *bh)
{
    LONG hitid, i;
    Matrix mx;
    HandleInfo info;

     if(drawpass != DRAWPASS_HANDLES) // we only draw the grab handles in this plugin so for anything else, we just exit
        return DRAWRESULT_SKIP;

    hitid = op->GetHighlightHandle(bd); // check if a handle is hit - that is, the user is hovering over it
    bd->SetMatrix_Matrix(op, bh->GetMg()); // converts coordinates into global space

    for(i = 0; i < HANDLES; i++)
    {
        if(i == hitid) // if the handle is hit, draw it in a different color
            bd->SetPen(GetViewColor(VIEWCOLOR_SELECTION_PREVIEW));
        else
            bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
        GetHandle(op, i, info); // get handle info for this handle so we know where to draw it
        bd->DrawHandle(info.position, DRAWHANDLE_BIG, 0); // and draw the handle
    }
    // we've drawn the handles, now for the lines
    bd->SetPen(GetViewColor(VIEWCOLOR_ACTIVEPOINT));
    GetHandle(op, 0, info);
    bd->DrawLine(info.position, Vector(0.0), 0); // draw the line from the centre of the object to the handle
    GetHandle(op, 1, info);
    bd->DrawLine(info.position, Vector(0.0), 0); // and the other line

    return DRAWRESULT_OK;
}

The first thing this code does is check to see if we are being asked to draw the handles. If not, we have nothing to do and can just exit.

Now we check to see if a user is hovering the mouse over a handle, because that handle will need to be drawn in a different colour if so. The BaseDraw matrix is set to convert coordinates to global and then we iterate through all the handles, though there are only two in this case. If the handle is being hit, it's drawn in one colour (white, by default) and if not, in another colour (default is a light orange). We get the handle's position on screen by calling GetHandle(). Cinema will also call that so we have to override it, but we can call it ourselves if we want to .

The handle's position is returned as part of the HandleInfo structure, so we can use the convenient SDK function DrawHandle() to draw the little square that is the handle.

Once we have drawn the handles, we need to draw the lines which connect the handle to the centre of the object. Again, we get the handle's position and use DrawLine() to draw a line from that position to the centre of the object. You wouldn't always need to do this of course, but in this case we do.

GetHandleCount()

This is a virtual function we override to let Cinema know the number of handles on this object. We only have two but you can have as many as you like.

GetHandle() and SetHandle()

These functions either get a handle's position on screen using the width and height of the object (GetHandle) or take the handle's width and height and set the handle (SetHandle):

void Diamond::GetHandle(BaseObject *op, LONG i, HandleInfo &info)
{
BaseContainer *data = op->GetDataInstance();
Real rectw, recth;

    // get the width and height and calculate the position of the handles
    rectw = data->GetReal(DIAMOND_WIDTH);
    recth = data->GetReal(DIAMOND_HEIGHT);

    switch(i)
    {
    case 0:
        info.position = Vector(rectw/2, 0, 0); // this is the position of the width handle
        info.direction = Vector(1.0, 0.0, 0.0);
        info.type = HANDLECONSTRAINTTYPE_LINEAR;
        break;

    case 1:
        info.position = Vector(0.0, recth/2, 0.0); // and of the height handle
        info.direction = Vector(0.0, 1.0, 0.0);
        info.type = HANDLECONSTRAINTTYPE_LINEAR;
        break;
    }
}

void Diamond::SetHandle(BaseObject *op, LONG i, Vector p, const HandleInfo &info)
{
BaseContainer *data = op->GetDataInstance();
Real rad;

    // basically the reverse of GetHandle - set the width and height from a handle position
    rad = p * info.direction;
    switch(i)
    {
    case 0:
        data->SetReal(DIAMOND_WIDTH, rad * 2);
        break;

    case 1:
        data->SetReal(DIAMOND_HEIGHT, rad * 2);
        break;
    }
}

GetHandle()

This retrieves the value of width and height from the attributes manager. This ensures that if the user changes those values, the position of the handle is updated. We know that the width handle will be somewhere along the positive X axis. The actual distance is half the width value since the width is the full width of the object and the handle only extends to one side - half the width. Therefore the position is X = width/2, Y = 0.0 and Z = 0.0, and we insert those values into the HandleInfo structure. The handle direction is along the +X axis, so its direction is set at X = 1.0, Y = 0.0, Z = 0.0. Finally the handle type is set to linear since the we want the handle only to move along a straight line.

The height size handle is calculated in the same way.

SetHandle()

This does the reverse of GetHandle(). If the user has moved the handle, we need to update the width and height settings in the attribute manager. We're given the handle's new position in the vector 'p' and its direction in the HandleInfo structure. We calculate the radius by multiplying the direction by the position, and then multiply that by 2 to give the actual new width or height of the object. That's basically it.

In the third and fourth parts, we will look at how the object is generated.

«  1  2  3  4  »