The C.O.F.F.E.E. Node Primer

Per-Anders Cinema 4D, Tutorials

Introduction

New to version 8.1 of Cinema 4D is the COFFEE node, many of you will be scared stiff by this little baby, but really there’s no need to. Soon I’ll have you embracing that COFFEE node, and many of you may even take the next step and move onto COFFEE tags or even full blown COFFEE plugins. It really isn’t as difficult as you might think, and in fact you’ll soon find it an indispensable part of your Xpresso armour.

Where to begin? Well, Lets start off with the basics and start by thinking of the COFFEE node as simply a more powerful Function Node.

Basics

So the first thing to do is to examine this node and see how it works. Make a new document and add a Null Object to your scene. Select the Null Object and create a new Xpresso tag on that object RMB->New Expression->Xpresso Expression In the Xpresso workspace add a new COFFEE node RMB->New Node->Xpresso->Calculate->COFFEE.

When the node is new it comes with two input ports (Input1 and Input2) and one output port (Output1). Also if you select the node you will see in the Attributes manager that it already contains a few lines of COFFEE code:

main()
{
Output1 = Input1 + Input2;
}

So what do these lines mean? Well, “main()” and the curly brackets merely tell Cinema that this is the piece of COFFEE code, a “Block” that needs to be executed. The third line is all we are really interested in at this moment in time “Outpu1=Input1+Input2;”.

Most likely you’ve already worked out what this means anyway, but if not lets examine it a bit closer.

The first piece we should look at is “Output1”, you should notice that this is in fact the name of the output port on the COFFEE node, in fact if you want go ahead and rename the Output1 port to something you like.

You will find that you are only allowed to use Numbers and Letters, and you mustn’t use any spaces to name a COFFEE node port. This is because each port is also a “Variable”, and COFFEE isn’t so friendly as to understand “My Output”, as far as COFFEE is concerned “My Output” is in fact two variables”My” and “Output” and a space between them will just make it throw out an error.

It’s worth remembering that COFFEE is also case sensitive, that means it sees upper case and lower case words differently, so “OUTPUT” is not the same as “output” or as “Output”. Which means that you could have three outputs to the node called “OUTPUT”,”output” and “Output” and they could each give you a completely different value. This also means that you should always remember that when you’re talking about “OUTPUT” in a COFFEE program, it’s not the same as “output”.

Rename the port back to “Output1” as during the rest of this tutorial this is the variable name we will be using for this port within COFFEE.

Getting back to the code, the next thing is a symbol “=”, the equals symbol. Equals on it’s own in COFFEE is the same as writing “is equal to” in English, so Output1=Intput1+Input2; becomes “Output1 is equal to Input1 plus Input2”. The semicolon “;” at the end of the line is the same as writing a full stop for COFFEE and almost every line of code should end in a semicolon. Looking at it like this we can see this is pretty simple, this node adds the two input values. We can test this simply by adding a Result node RMB->New Node->Xpresso->General->Result, and connecting the Output1 port to the input of the Result node. Then simply select the COFFEE node and change the values of Input1 and Input2 in the Attributes manager on the Parameter page, the Result node should display the result of the calculation.

Functions

Ok, so that’s not very impressive, we already have a Math node that can do this, well sure, but what if we wanted something a little more complicated? lets change that third line of code. Make it read like the following:

main()
{

Output1=sqrt(Input1*Input1+Input2*Input2);
}

Hey now we’re outputting the length of a hypotenuse for a right angle triangle who’s shorter sides lengths are equal to Input1 and Input2. sqrt in COFFEE gives us the Square Root of whatever is inside it’s brackets.

Compare?

So now you’re saying “Well, we already had the Function node, I still don’t see what’s so great about this.” well we can do more than math stuff with our inputs, we can for instance compare things.

Replace the third line of code with the following:

main()
{

if (Input1==Input2)
{
Output1=TRUE;
}
else Output1=FALSE;
}

Ok so now the node will only output 1 when both inputs are equal, and 0 at all other times. How is this working? Simple, the “if” statement. This works as follows, “if (something in here is true) { then do these things} (else) otherwise {do these things instead}”. So what’s in the brackets has to evaluate to be TRUE before the code in the curly brackets will do anything in our case we’re testing if Input1 is Equal to Input 2, “==” tells COFFEE to test whether these two things are equal, if they are it returns true, if not it returns false, other operators we could have used are “!=” isn’t equal, “>” is greater than, “<” is less than, >=” is greater than or equal to, “<=” is less than or equal to.

At the end we have an “else” statement, when this is tagged onto an “if” statement this gives us an opportunity to use some alternative code instead for when what’s in the if statements brackets does not evaluate to be “true”. In this case we execute some code that says Output1 will be false, FALSE is equal to 0 in COFFEE.

So you’re thinking, well again we already have the Compare node, so what’s so great about this. Well, lets take this one step further, lets say we wanted our node to only return TRUE if our input values were equal, and if when added up they were less than 100. Lets change the first line of the if statement as follows

main()
{

if (Input1==Input2 && Input1+Input2<100)
{

Output1=TRUE;
}
else Output1=FALSE;

}

Now only when input 1 and input 2 added together are less than 100 and when they’re equal will the output be true. The “&&” means “and” so to Cinema this reads “If input 1 is equal to input 2 and input1 plus input 2 is less than 100 then..” the main other combination command you can use is “||” which means “or”.

Ok now we’re getting somewhere and making something that you can’t simply do with one node normally, yet all we’re written is three lines of code in a COFFEE node.

Adding inputs

Well, how about if we wanted to have another variable or input to control the value beneath which this node will output TRUE rater than being stuck with 100?

Simple, all we do is add a new input to the node, and change the code to incorporate this new input. So click on the Blue square in the top left hand corner of the COFFEE node and from the drop down list choose “Real” a new port will be added called “Input3”.

Now lets change the code to incorporate this new Input.

main()
{

if (Input1==Input2 && Input1+Input2<Input3)
{

Output1=TRUE;
}
else Output1=FALSE;

}

Ok, so now our third input controls the maximum value that Input1 and Input2 added together can be to output a value to True when they’re equal. But that’s not really logical, lets make it so that Input1 and Input2 can go up to the value of Input3, so lets change this:

main()
{

if (Input1==Input2 && Input1<=Input3)
{

Output1=TRUE;
}
else Output1=FALSE;

}

Ok that works, but why? Well simply put if this will only return TRUE if Input1 is lower than or equal to Input3, and it will also only return TRUE if Input1 and Input 2 are equal, then it follows that Input2 must also be less than or equal to Input3 for this whole statement to be true.

Now something cool about COFFEE nodes is that you can add as many ports as you need, COFFEE nodes allow you to create your own nodes. So lets say for instance we wanted to add another Output that added up the number of times that Output1 returned true. How would we do this?

Variables

Firstly we would need to make a new variable, but one that’s internal to the COFFEE node, this will be our “counter”, but we don’t want it to be reset every time the COFFEE node is evaluated otherwise it’s never going to be able to add up. The kind of variable we’re talking about is what’s known as a “Global Variable”, this is a variable that’s declared outside of the”scope” of any function.

How do we create or “declare” variables in COFFEE? We use the “var” command. It works pretty simply in most cases, all we have to do is write “var myvariable;” and then your variable is ready to get going with, you can fill it with whatever and do whatever you want to it. In order to create a Global Variable you have to write this command outside of any function, so our code is going to now look like:

var Counter;
main()
{
if (Input1==Input2 && Input1<=Input3)
{
Output1=TRUE;
}
else Output1=FALSE;
}

Here we declared (or created) our own global variable called “Counter”, because this is outside of any function it wont get reset unless the play head is brought back to the beginning of the animation or you reset it yourself inside of the COFFEE program. Declaring variables is pretty much all you can do outside of any function in COFFEE, and you can’t put anything into any variable that you declare till it’s inside of a function, this means that it wont have any value in it at all at the moment, and COFFEE wont know what type of variable it is we’re dealing with, it could be a Link, or a Real, an Integer, or a Vector, but COFFEE needs to know before it can really start to work with it. Before we set it up it will only have the value of “nil” or “false”which isn’t much of anything really.

In order to set up the variable but not be setting it up every time the COFFEE node is run (which would of course mean that Counter would never actually add up or get beyond it’s setting up value) we can simply test if Counter is false, if so then we can give it some contents so that COFFEE will then be able to deal with, from then on COFFEE will know that our variable contains data of the sort that we put into it.

var Counter;
main()
{
if (!Counter) Counter=0;
if (Input1==Input2 && Input1<=Input3)
{

Output1=TRUE;
}
else Output1=FALSE;
}

The exclamation mark means “not” to COFFEE, so this line reads “if Counter is not TRUE (i.e. it doesn’t exist) then set counter to equal 0:” Setting counter to equal 0 tells COFFEE that counter is a numerical value, so it’s either a Real or and Integer, and it’s more likely to be an Integer.

Now that we set up the variable “Counter” it’s time to actually make it count, change the code as follows:

var Counter;
main()
{
if (!Counter) Counter=0;
if (Input1==Input2 && Input1<=Input3)
{

Counter=Counter+1;
Output1=TRUE;
}
else Output1=FALSE;
}

So we added one new line “Counter=Counter+1;” all this does is add one to the variable Counter each time we return TRUE. Now COFFEE actually allows us several ways to write this, for instance we could also have written “Counter+=1;” instead, and this would have done the same thing, or even “Counter++;”. As you get to know COFFEE and it’s SDK you will learn that often there’s more than one way to do the same thing. This can make your life easier at times, but it can also make your code difficult to read. Keep with what you find easiest to understand for now as you want to make things as clear as possible for yourself as you’re writing code.

While we’re on the subject of clarity, you can add remarks to your code two ways in COFFEE, remarks will be ignored by Cinema, but just make life easier for you, you can also sometimes use them to temporarily switch off lines of code. The way to add a comment or remark is to add to forward slashes “//” then everything beyond that on that line will be comments, and will be ignored by Cinema. If you want to make a larger area of comments over several lines, or want to “comment out” a larger block of code, then you add a forward slash and an asterisk at the beginning of the comment “/*” and a asterisk with a forward slash at the end of the comment “*/”. Make sure to add the “*/” at the end otherwise you may end up commenting out all of your code and your program will do nothing or worse throw up an error.

Adding outputs

Back to the program, so now we’ve got “Counter” counting up, but at the moment we can’t see this, for all we know nothing is happening at all, so let’s make another output for the COFFEE node and add the code in our program to send the value of Counter to our new output. So simply add a new output to the COFFEE node by clicking on the red square in the top right hand corner of the node, and from the drop down we want an “Integer” output, an Integer is a whole number. Now we have a new port “Output2” we need to get the value of Counter to that port, we do this quite simply by adding a new line between lines 8 and 9:

var Counter;
main()
{
if (!Counter) Counter=0;
if (Input1==Input2 && Input1<=Input3)
{

Counter=Counter+1;
Output1=TRUE;
}
else Output1=FALSE;

Output2=Counter;
}

This assigns the value of Counter to the output Output2. Now if we wire up a Result node, to the output Output2 we can see that every time Output1 is made to be TRUE then Output2 counts up, but hang on there’s a problem, if we move the play head or do an action in the editor when Output1 is TRUE (1), then we can see that Output2 is adding up, this isn’t what we want, we wanted to count the number of times that Output1 returned true, so we need to test if not only our input parameters should return a TRUE result, but also if they were returning a FALSE result before, that way we can add to Counter only at the time that the state of Output1 actually changes to true.

In order to do this we should remember that the ports of a COFFEE node are in fact Variables too, not only that, but they’re Global variables, we can use this to our advantage by testing whether Output1 was FALSE when our test of the equality of Input1 and Input2 returns true, we will be able to add 1 to Counter just at the time that the state of Output1 changes from FALSE to true, so change the line “Counter=Counter+1;” to the following:

var Counter;
main()
{
if (!Counter) Counter=0;
if (Input1==Input2 && Input1<=Input3)
{

if (!Output1) Counter=Counter+1;
Output1=TRUE;
}
else Output1=FALSE;

Output2=Counter;
}

Now we’re testing to see that Output1 is FALSE before adding one to Counter, testing that it’s FALSE here is fine because we know that we’re just about to set it to true, so that means we’re catching it at the point where it’s state changes from FALSE to true.

When you hit play and Input1 and Input2 are equal and below Input3.Output2 will add one to it, and it will only add up the number of times that Output1 is TRUE (1).

Cleaning up

So now bearing in mind that all the ports are global variables, why should we have to make the variable “Counter” internally, why not just use the global variable of the port Output2? Answer, we don;t have to, we just did that to show that you can, but we could for instance simplify the COFFEE listing quite easily by replacing Counter with Output2, this would also mean we could loose a couple of lines (the lines to be deleted are highlighted in red):

//var Counter;
main()
{
if (!Output2) Output2=0;
if (Input1==Input2 && Input1<=Input3)
{

if (!Output1) Output2=Output2+1;
Output1=TRUE;
}
else Output1=FALSE;

//Output2=Counter;
}

We’re back down to only ten lines of code, it’s important to try to keep your COFFEE listings as simple and short as possible, and to prune out any dead wood, as when you begin to work on longer listings or scenes with many COFFEE nodes then this will speed things up considerably.

The trouble with our counter at the moment though is that it will just carry on adding up pretty much forever, although it will reset when the the current frame is rewound to the beginning of the animation. It would be much nicer if we could simply have another input that if we put in the TRUE (1) we will reset the counter.

First thing to do is to add another input, so add a new “Bool” input, this can take either TRUE or FALSE as a value. Next we need to add the code that will reset the counter “Output2”, now we need to think about where to put the line of code that will reset Output2, COFFEE executes the code from the top line down, so if we put the code in too soon then even though the Output will be reset if Input1 changes to match Input2 for instance at the same time then Output2 will still be equal to 1 for one frame. So we need to put the line of code after the counting actually happens, so here’s how it should go:

main()
{
if (!Output2) Output2=0;
if (Input1==Input2 && Input1<=Input3)
{
if (!Output1) Output2=Output2+1;
Output1=TRUE;
}
else Output1=FALSE;

if (Input4) Output2=0;

}

Renaming ports

As you should see everything is working splendidly well, but the ports on the node aren’t very clearly labeled, and reading the code itself it’s not exactly easy to follow. Really we should have been naming the ports as we went along, but seeing as the code is so small perhaps now would be a good time to rename those ports.

Firstly, rename Input1 and Input2 as Value1 and Value2, then rename Input3 as Maximum, and Input4 as Reset, next rename Output1 as Result, and Output2 as Counter. Now, in the attributes manager code preview window you will notice that Cinema has renamed all the variables accordingly in your program for you! If you’ve been editing in the COFFEE editor, make sure to click the “Open COFFEE Editor” button again so that what’s in the node overwrites what’s in the COFFEE editor.

main()
{

if (!Counter) Counter=0;
if (Value1==Value2 && Value1<=Maximum)
{

if (!Result) Counter=Counter+1;
Result =TRUE;
}
else
Result=FALSE;

if (Reset) Counter=0;
}

Objects and link ports

So, this is all very nice, and we have made a new useful node this way no doubt, but we’ve only scratched the surface right now. What if we were to up and ante and say, using the COFFEE node only lets make an object move up in the Y axis by 10 units every time Value1 and Value2 are equal, not only that, but lets say that we set the value of Maximum by the Y position of the object, then of course we need for reset to still work, so now we’re going to do something fairly major.

Lets start off by getting a way to get object information into our COFFEE node, for this we need to add a new input port of type “Link”, so go ahead and add this to our COFFEE node and rename it to “Object”.

The “Object” port will allow us a direct link to pretty much all information about any object that we then plug into it, however if we try to get information out of the “Object” variable and nothing’s connected to our “Object” port, then we could end up with an error, while with COFFEE nodes most errors wont cause a crash it’s still good policy to try and catch and possible situation where and error might occur and work a way around it. In this case we can do this by testing if “Object” is FALSE (or rather if it is “not true” if it’s FALSE that means there’s no object there, and we can stop the COFFEE code by using the “return” statement, this statement jumps out of the current function (in our case “main”) and returns the value that it precedes. For us using “return” in the “main” function will cause the COFFEE node to just stop doing anything more, and we don’t actually need to put anything after “return” as a value to return as all we’re doing is breaking out.

main()
{

i
f (!Object) return;
if (!Counter) Counter=0;
if (Value1==Value2 && Value1<=Maximum)
{
if (!Result) Counter=Counter+1;
Result =TRUE;
}
else Result=FALSE;
if (Reset) Counter=0;
}

Now that we’ve protected ourselves against an error occurring if there’s no object actually connected we need to get some information about our object. All objects in Cinema belong to a “class”, objects of the type that we’re going to be connecting to our node like nulls, primitive objects, polygon objects etc all belong to the BaseClass BaseObject, so if we look in the SDK at the page for BaseObject we can see that there’s a function in that class for getting an object’s position, it’s “GetPosition()”, if an object is of a certain class that actually means that it contains all of the functions of that class and that classes parent class, in order to access a function of a class we use the arrow symbol, which is a combination of a minus “-” and a greater than “>” to form an arrow “->” and we use it in between our object and the name of the function so “myobject->function(function parameters);” will call that function.

Getting object parameters

Looking at the SDK you can see that GetPosition() returns a “vector” (on the left before the actual function it says vector), so that means we need to have a variable to put that vector into, so lets add a line where we get the objects position and put that into a new variable:

main()
{
i
f (!Object) return;
var objectPosition=Object->GetPosition();

if (!Counter) Counter=0;
if (Value1==Value2 && Value1<=Maximum)
{
if (!Result) Counter=Counter+1;
Result =TRUE;
}
else Result=FALSE;
if (Reset) Counter=0;
}

Now we’re only interested in the Y position of our object, and that’s inside of the “vector” object that we just retrieved and put into “objectPosition”, now if we look in the SDK at “vector” we will find that it contains three real numbers stored in the variables x,y and z. In order to get a sub-variable out of an object in COFFEE you use what’s called “Dot Syntax”, this is to say, you write the name of your object, then you write a full stop (the dot) and then you write the name of the variable, something like “myobject.variable”. We could simply plug the Y position back into objectPosition and COFFEE would automatically redefine objectPosition as being a real, but for certain reasons that will become apparent shortly it would be better if we just make a new variable for the objects Y position to be stored in, we could also just use objectPosition.y throughout rather than a new variable, but in this case doing things this way means that in the tutorial we will cover more ground.

main()
{
i
f (!Object) return;
var objectPosition=Object->GetPosition();

var objectY=objectPosition.y;

if (!Counter) Counter=0;
if (Value1==Value2 && Value1<=Maximum)
{
if (!Result) Counter=Counter+1;
Result =TRUE;
}
else Result=FALSE;
if (Reset) Counter=0;
}

OK, so now we have the objects Y position, we wanted the value of Maximum to be set by the Y position of our object, so maybe now would be a good time to remove the input port “Maximum” and replace it in the code with our variable “objectY” which contains the Y position of our object:

main()
{
i
f (!Object) return;
var objectPosition=Object->GetPosition();
var objectY=objectPosition.y;
if (!Counter) Counter=0;

if (Value1==Value2 && Value1<=objectY)
{

if (!Result) Counter=Counter+1;
Result =TRUE;
}
else Result=FALSE;
if (Reset) Counter=0;
}

We now want every time Result is going to be TRUE instead of Counter adding up, we’re going to move our object up by 10. So all we have to do is replace the variable “Counter” with the variable “objectY” on the next line:

main()
{
i
f (!Object) return;
var objectPosition=Object->GetPosition();
var objectY=objectPosition.y;
if (!Counter) Counter=0;
if (Value1==Value2 && Value1<=objectY)
{

if (!Result) objectY=objectY+10;
Result =TRUE;
}
else Result=FALSE;
if (Reset) Counter=0;
}

Global variable

Next we have to deal with resetting the Y position of our object when the Reset port receives a TRUE value, we will have to do that by firstly storing the objects y-position the first time the object is connected. To do this we will need a new internal global variable, we’ll call this “originalPosition”, and we’ll set it up as soon as an object is plugged in. Then once reset is pressed, we’ll reset objectY to the value of originalPosition. This also now means that we can simply dispense with the “Counter” output port, and all lines that refer to that in the code.

var originalPosition;
main()
{
i
f (!Object) return;
var objectPosition=Object->GetPosition();
var objectY=objectPosition.y;

if (!originalPosition) originalPosition=objectY;
if (Value1==Value2 && Value1<=objectY)
{

if (!Result) objectY=objectY+10;
Result =TRUE;
}
else Result=FALSE;

if (Reset) objectY=originalPosition;
}

Setting object parameters

So now, we’ve got our node adding 10 to the Y position of our object every time that the value of Result is turned to TRUE. But hang on, there’s nothing actually happening to our object yet… why?

The reason is, all we’ve done is change a few variables, we haven’t actually changed any parameter of our object directly, remember in COFFEE we may actually have several objects linked in that we’re manipulating all with their own parameters, maybe all of their details being put into an array or some other object, to do this we will once again need to use the functions for a BaseObject, and you will see that there’s a function there called SetPosition(), this one just returns a bool, that means if it’s successful in doing what we want, then it returns true, if it fails then it returns false, as with all functions we don’t need to have a variable to put the result into to call it, so next we’re going to add the line of code that calls this function after we’ve changed the value of objectY

Looking at the SDK page again you will notice that in the brackets for the function SetPosition it requires a function parameter to work, a vector, so we need to know how to make a new vector to put in there, we could write a new line of code that simply puts the new objectY position into the objectPosition.y variable, but that would mean adding two new lines, so lets instead write it all out on one line by using the “vector” function, this function should be written as “vector(x,y,z);” and it returns as you may have guessed a vector, so here’s the new line:

var originalPosition;
main()
{
i
f (!Object) return;
var objectPosition=Object->GetPosition();
var objectY=objectPosition.y;
if (!originalPosition) originalPosition=objectY;
if (Value1==Value2 && Value1<=objectY)
{
if (!Result) objectY=objectY+10;
Result =TRUE;
}
else Result=FALSE;
if (Reset) objectY=originalPosition;

Object->SetPosition(vector(objectPosition.x,objectY,objectPosition.z));

}

Getting the object to update

OK so we did everything the SDK told us to, yet the object still isn’t moving. Well there’s one last thing we need to do, you see COFFEE allows us to set lots of parameters for an object all at once, so you could on the new line write “Object->SetRotation(vector(20,40,70)); and it would set the rotation of the object as well, but you may not want COFFEE to actually tell the object to update itself with the new information till you’ve set a few more parameters or got some more data out of the object to affect other parameters. The other thing to remember is that updating an object is actually fairly intensive, this means you don’t want the object to keep jumping around at each command as this would slow things down, rather you want it all to happen once at the end of your program.

COFFEE has a command to actually tell objects when (and sometimes what) to update, and that command is the “Message” command, now, when you look at the full list of commands alphabetically with the SDK you will find that there are actually a whole heap of “Message” commands, and they’re all dependent on what kind of object we’re dealing with. Well, if you were observant you will have noticed that on the BaseObject page at the top it said “BaseList4D” while there’s no message command for BaseObject or Baselist4D there is one for Baselist2D and Baselist2D is the parent class for Baselist4D (baselist4D being the parent class for BaseObject), this actually means you can use all of these functions from our Object, and we can use the BaseList2D::Message function.

Looking at it’s page in the SDK was can see that it accepts an Integer (called type) and possibly some data, now COFFEE has a whole load of preset Integer variables, and looking in the description pane for Message you will see it actually lists them under Message, and has an explanation for each Variable, these variables as known a constant variables, that is you can’t change them, and you’d better not try to make your own variables with the same names otherwise you’re going to end up with an error, so we want to just tell the object to generally update, and the variable MSG_UPDATE is all that we need to do this, so this is how it should go:

var originalPosition;
main()
{
i
f (!Object) return;
var objectPosition=Object->GetPosition();
var objectY=objectPosition.y;
if (!originalPosition) originalPosition=objectY;
if (Value1==Value2 && Value1<=objectY)
{
if (!Result) objectY=objectY+10;
Result =TRUE;
}
else Result=FALSE;
if (Reset) objectY=originalPosition;
Object->SetPosition(vector(objectPosition.x,objectY,objectPosition.z));

Object->Message(MSG_UPDATE);
}

So there you go, now we’ve manipulated an object using COFFEE and the COFFEE node in Xpresso and just a few lines of code. Now remember when the play head is rewound all the global variables will be reset, this means that we will actually loose the objects original Y position, so a nice project for you is to either by using Xpresso, or by using the COFFEE node and adding a couple of new ports make it so that on the last frame of the animation the objects Y position is reset.

As a second project, try making a COFFEE node that changes the child of the object being inputted to the node in some way (Hint, check out BaseList4D).

Happy coding.