home
portfolio
services
notebook
links
This way out

 

 

 

CINEMA 4D, Revision 6

 

COFFEE plugin tutorial
Creating a house object

 

(a link will be available for part 8 of this tutorial soon)

coffee tutorial 1:  Introduction
coffee tutorial 2:  Programming basics
coffee tutorial 3:  The nuts and bolts of a plugin
coffee tutorial 4:  Your first dialog box
coffee tutorial 5:  A better dialog box
coffee tutorial 6:  Creating a house object
coffee tutorial 7:  Interactive house modeling
coffee tutorial 8:  International resources

 

For QuickStarters:

* copy and paste the listing text (below) to a simple text editor and save it as a plain text file under the name 'lesson7.cof' into the C4D "plugins" folder.

* restart c4d (or use the menu command "Reload Plugins")

* Open the window "Console"

* execute the plugin-menu command "coffee-lesson-7"

* this time we will interactively change objects with every input using an open dialog box, and also look at recognising objects.


The Listing:


const var cPlugName = "coffee-lesson-7";
const var cPlugHelp = "Interactive house building";
const var cObjName = "House";
const var cHousePntAnz = 10;
const var cHousePolyAnz = 9;
const var cS1 = "Width";
const var cS2 = "Length";
const var cS3 = "Height";
const var cS4 = "Rooftop";
const var cB1 = "new House";
const var cB2 = "reset";
const var cFMax = 1e3;
enum
{ eSlider=4000,eSlider1,eSlider2,eSlider3,eSlider4,
eBut=1000,eButNew,eButInit
}
const var cPluginID = 2000005;
var gDial,gHouse;


fSetActiveObject(vDoc,vObj)
{ var vAObj = GetActiveObject(vDoc);
if (vAObj) vAObj->DelBit(BIT_AOBJ);
vObj->SetBit(BIT_AOBJ);
GeEventAdd(NEW_ACTIVE_OBJECT);
}


// --- House
class oHouse
{ public:
var qObj;
var qB,qL,qH,qD;

fNewHouse(vDoc);
fDimHouse(vB,vL,vH,vD);
fIsHouseSelected();
}
oHouse::fNewHouse(vDoc)
{ var vPointAry,vPolyAry;
var vVarChanged,vBackupTags;
//
// aus SDK: BaseDocument
qObj=new(PolygonObject);
if (!qObj) return FALSE;
qObj->SetName(cObjName);
//
vDoc->StartUndo();
vDoc->AddUndo(UNDO_OBJECT_NEW,qObj);
vDoc->EndUndo();
//
// aus SDK: VariableChanged
vVarChanged = new(VariableChanged);
vBackupTags = new(BackupTags);
//
vPointAry = new(array, cHousePntAnz);
qObj->SetPoints(vPointAry);
vBackupTags->Init(qObj);
vVarChanged->Init(0, cHousePntAnz);
if (!qObj->Message(MSG_POINTS_CHANGED, vVarChanged))
{ vBackupTags->Restore();
return FALSE;
}
//
vPolyAry = new(array, cHousePolyAnz*4);
qObj->SetPolygons(vPolyAry);
vBackupTags->Init(qObj);
vVarChanged->Init(0, cHousePolyAnz);
if (!qObj->Message(MSG_POLYGONS_CHANGED, vVarChanged))
{ vBackupTags->Restore();
return FALSE;
}
vDoc->InsertObject(qObj,NULL,NULL);
fSetActiveObject(vDoc,qObj);
GeEventAdd(DOCUMENT_CHANGED);
//
return TRUE;
}
oHouse::fDimHouse(vB,vL,vH,vD)
{ qObj->SetPoint(0,vector(-vB, 0,-vL));
qObj->SetPoint(1,vector( vB, 0,-vL));
qObj->SetPoint(2,vector( vB, 0, vL));
qObj->SetPoint(3,vector(-vB, 0, vL));
qObj->SetPoint(4,vector(-vB,vH,-vL));
qObj->SetPoint(5,vector( vB,vH,-vL));
qObj->SetPoint(6,vector( vB,vH, vL));
qObj->SetPoint(7,vector(-vB,vH, vL));
qObj->SetPoint(8,vector(0,vH+vD,-vL));
qObj->SetPoint(9,vector(0,vH+vD, vL));
//
qObj->SetPolygon(0, 0,3,2,1); // Floor
qObj->SetPolygon(1, 0,1,5,4); // 4x Wall
qObj->SetPolygon(2, 1,2,6,5);
qObj->SetPolygon(3, 2,3,7,6);
qObj->SetPolygon(4, 3,0,4,7);
qObj->SetPolygon(5, 5,6,9,8); // 2x Roof
qObj->SetPolygon(6, 8,9,7,4);
qObj->SetPolygon(7, 4,5,8,8); // 2x Triangle
qObj->SetPolygon(8, 6,7,9,9);
//
qObj->Message(MSG_UPDATE);
GeEventAdd(DOCUMENT_CHANGED);
}
oHouse::fIsHouseSelected()
{ var vDoc = GetActiveDocument();
if (!vDoc) return FALSE;
qObj = GetActiveObject(vDoc);
// could this be a house?
if (instanceof(qObj,PointObject))
{ if (qObj->GetPointCount()==cHousePntAnz)
{ if (qObj->GetPolygonCount()==cHousePolyAnz)
{ qB=vlen(qObj->GetPoint(0)-qObj->GetPoint(1));
qL=vlen(qObj->GetPoint(1)-qObj->GetPoint(2));
qH=vlen(qObj->GetPoint(0)-qObj->GetPoint(4));
qD=vlen(qObj->GetPoint(8)-(qObj->GetPoint(4)+qObj->GetPoint(5))/2);
return TRUE;
} } }
qObj=NULL;
return FALSE;
}


// --- GeDialog
class oDialog : GeDialog
{ public:
oDialog();
CreateLayout();
Init();
CoreMessage(vID,vMSG);
Command(vID,vMSG);
fSetItems(vFlag);
}
oDialog::oDialog() { super(cPluginID); }
oDialog::CreateLayout()
{ SetTitle(cPlugName);
AddGroupBeginV(eSlider,BFH_SCALEFIT,2,"",0);
{ AddStaticText(0,BFH_LEFT,0,0,cS1,0);
AddEditSlider(eSlider1,BFH_SCALEFIT,200,0);
AddStaticText(0,BFH_LEFT,0,0,cS2,0);
AddEditSlider(eSlider2,BFH_SCALEFIT,200,0);
AddStaticText(0,BFH_LEFT,0,0,cS3,0);
AddEditSlider(eSlider3,BFH_SCALEFIT,200,0);
AddStaticText(0,BFH_LEFT,0,0,cS4,0);
AddEditSlider(eSlider4,BFH_SCALEFIT,200,0);
}
AddGroupEnd();
AddGroupBeginV(eBut,BFH_CENTER,2,"",0);
{ AddButton (eButNew ,0,0,0,cB1);
AddButton (eButInit,0,0,0,cB2);
}
AddGroupEnd();
return TRUE;
}
oDialog::fSetItems(vFlag)
{ if ((vFlag)||(!gHouse->fIsHouseSelected()))
{ gHouse->qB= 80.0;
gHouse->qL=120.0;
gHouse->qH= 50.0;
gHouse->qD= 30.0;
}
SetFloat(eSlider1,gHouse->qB,1,cFMax,1.0);
SetFloat(eSlider2,gHouse->qL,1,cFMax,1.0);
SetFloat(eSlider3,gHouse->qH,1,cFMax,1.0);
SetFloat(eSlider4,gHouse->qD,1,cFMax,1.0);
}

oDialog::Init()
{ if (!gHouse) gHouse=new(oHouse);
fSetItems(FALSE);

return TRUE;
}
oDialog::CoreMessage(vID,vMSG)
{ switch (vID)
{ case NEW_DOCUMENT:
case DOCUMENT_CHANGED:
case NEW_ACTIVE_OBJECT:
case ACTIVE_OBJECT_CHANGED:
fSetItems(FALSE);
return TRUE;
break;
}
return FALSE;
}

oDialog::Command(vID,vMSG)
{ var vDoc,vB,vL,vH,vD;
//
StopAllThreads();
switch (vID)
{ case eButNew:
vDoc = GetActiveDocument();
if (!vDoc) return FALSE;
gHouse->fNewHouse(vDoc);
case eSlider1:
case eSlider2:
case eSlider3:
case eSlider4:

case eButInit:
if (vID==eButInit) fSetItems(TRUE);
if (!gHouse->qObj) return FALSE;

vB=GetFloat(eSlider1);
vL=GetFloat(eSlider2);
vH=GetFloat(eSlider3);
vD=GetFloat(eSlider4);
gHouse->fDimHouse(vB/2,vL/2,vH,vD);
break;
}
return TRUE;
}
// --- MenuPlugin
class oMenuPlugin : MenuPlugin
{ public:
oMenuPlugin();
GetID();
GetName();
GetHelp();
Execute(doc);
RestoreLayout(secret);
}
oMenuPlugin::oMenuPlugin() { super(); }
oMenuPlugin::GetID() { return cPluginID; }
oMenuPlugin::GetName() { return cPlugName; }
oMenuPlugin::GetHelp() { return cPlugHelp; }
oMenuPlugin::Execute(doc)
{ if (!gDial) gDial=new(oDialog);
gDial->Open(TRUE,-1,-1);
}
oMenuPlugin::RestoreLayout(secret)
{ if (!gDial) gDial=new(oDialog);
gDial->RestoreLayout(secret);
}
main()
{ Register(oMenuPlugin);
}


The Explanation:

Coffee-lesson-6 provided everything to make this plugin work. Everything which is added now - is pure luxury.

Now it's possible to modify the houses in an interactive way. The modal dialog box remains opened while objects can be clicked, moved or changed. The new parameters are taken over into the dialog box. Vice versa, every input in the dialog box results in a simultaneous adaption of the house-object.


fSetActiveObject(vDoc,vObj)

The command GetActiveObject exists. We don't want to discuss why C.O.F.F.E.E. doesn't support a SetActiveObject command. This function is similar to making an object active by clicking on it.


GeEventAdd(msg)

All actions have to be told to the system.


qObj,qB,qL,qH,qD

The class oHouse has received some variables. It makes sense to name it differently (former vObj), in order to avoid confusion during the programming.
qB means width, qL means length, qH means height and qD means the roof-height of the house.


fIsHouseSelected()

As a result of the interactivity the user is easily able to click other objects which forces the programmer to deal with different situations.


instanceof(qObj,PointObject)

Is the new active object an editable object or something completely different?

qObj->GetPointCount()==cHousePntAnz

Is the number of points identical?

qObj->GetPolygonCount()==cHousePolyAnz

Is the number of polygons identical?

It could possibly be a house then. Yes, indeed, it could be a totally different object with a similar amount of points and polygons. We take for granted, that our user is intelligent enough to avoid clicking on it, ok? ;-)

One problem are the object sizes we took for building the house. These sizes aren't stored, the house object could have been changed since the last activation as well. My solution is to recalculate the sizes from the points.

vlen Calculates the length (distance) between two points.
If the object is no house, qObj will be set to NULL.


fSetItems(vFlag)

If no house is active, the items of the dialog box will be reset to the inital values, otherwise the dialog box shows the calculated numbers from fIsHouseSelected(). An exception is eButInit (vFlag==TRUE), in this case, it's possibly a house, the inital values should be taken nevertheless.


CoreMessage(vID,vMSG)

Every event has to be followed by a reaction. Has the user switched to another document or object, or has he even changed the house with another tool? Our dialog box executes fSetItems() as an universal answer.


case eSlider1:

Our dialog box has only shown a reaction at two buttons. Now we'll implement changes of the four sliders (or input by numbers) directly at the selected house. Now that's fun!
In coffee-lesson-8 we will cover some duty excersises.

----------------------------------------
Text copyright © H. G. Seib 2000, HTML copyright © M. D. Abbott 2001

 

 


Vantage Graphics and Design Limited
9 Vicarage Lane, Harbury, LEAMINGTON SPA, Warwickshire CV33 9HA, UNITED KINGDOM
Telephone: 01926 614211 Fax: 01926 614226 ISDN: 01926 614210
E-mail: studio@vgd.co.uk

Page last updated: 05 July 2001