Cinema 4D - COFFEE

From NoskeWiki
Jump to: navigation, search

About

NOTE: This page is a daughter page of: Cinema 4D


COFFEE is a scripting language for Cinema 4D (C4D). Code for C4D can also be written using python or an advanced C++ API, however COFFEE offers several advantages:

  • it handles memory management implicitly
  • the COFFEE interpreter does not require you compile any scripts.
  • it's easier to implement than C++ and has a much smaller learning curve

Conversely, the advantages of C++ over COFFEE are:

  • you can interface with Cinema 4D from other (c++) applications
  • you can take full advantage of the c++ standard libraries
  • it has access to some functionality (including collision detection) not available in COFFEE.

The STK

The STK documentation (in HTML format) for COFFEE and C++ can be downloaded from the Maxon website here. Unfortunately there is no online version and the documentation itself is fairly lacking / tricky to browse. The online help forum is about your only option when you run into trouble.


The Basics

NOTE: Much of this text was extracted from the COFFEE STK, but heavily abbreviated. I'd suggest reading the STK for a (slightly) better introduction.

COFFEE is similar in form to JavaScript. Variables are declared with the var keyword. All variables are untyped, although you can use int(), float(), tostting() and vector() to typecast. Objects, arrays and strings are allocated using the new() function:

var object = new(MyObject, firstParameter, secondParameter);

Other familiar keywords include: const, enum, class, private:, public:, protected:, extends and struct.

Only integers, floats and vectors are stored by value in the variable. Everything else is stored by reference. For example:

var a = new(array,1,1);
var b = a;
b[0] = 2;
println("a[0] =", a[0] );    // outputs "2" to the console window

Standard functions include: print(), println(), typeof(), sizeof(), time(), gc() (to trigger garbage collection)

Unlike JavaScript however, there is no "function" keyword, a function is declared as follows:

myFunction(arg1, arg2)
{
  // Do whatever
  return myReturnVal;
}
 
main(doc, op)
{
  myFunction(3, 4);
}

Notice neither return type or argument types are specified.


The Console Window

Before writing scripts you should first open the Console window (click Window > Console Manager [Shift+F10]. This window displays runtime errors and print() statements, and thus is critical in debugging. Two other features of the console:

  • You can execute commands - eg: If you type var x = 40 + 2; println(x); at the bottom and press enter it will output "42"
  • You can drag-and-drop Attribute labels in to see their ID property - eg: If add a cube, then under its "Basic Properties" click and drag "Name" into the bottom of the console window it will display "temp(1)#ID_BASELIST_NAME"

To make this console window appear everytime you restart C4D I recommend you go: Window > Layout > Save as Startup Layout.


The Command Manager Window

Rather than searching the STK I discovered most commands can be called using "CallCommand()". Open the "Command manager" (click Window > Layout > Command Manager [shift+f12] ), type the function/command you want to use, select it and the ID you need will show near at the bottom. The following code for example, attempts to find and select an object called sphere, then crudely duplicates it by calling copy and then paste:

var sphere = doc->FindObject("Sphere");
doc->SetActiveObject(sphere);
CallCommand(12107);      // "Copy".
CallCommand(12108);      // "Paste".


Types of Plugins

I'm still relatively new to COFFEE, but it seems the two easiest ways to write script are as a Menu Plugin or COFFEE tag. Below I've written a brief explanation of each, and provided a small tutorials to help get you started.


Tutorial 1: Menu Plugin

Menu plugins are scripts which are added to the C4D main menu bar under "Plugins" and execute once whenever the user chooses this menu item.


The easiest way to create a menu plugin is to open the "Script Manager" window (click Window > Script Manager [Shift+F11]). This window allows you to rapidly create "User Scripts" (a special type of menu plugin). Click "New" and create a new script called "Hello World" and enter the code:

println("hello world");    // Will appear in the Console.

This will now appear under Plugins > User Script > Hello World... or you could simply click Execute at the bottom corner of Script Manager. Now we will create a more complex script. In this script we will add a new sphere called "LittleRedSphere", add a new material called "Red", apply the material to the sphere using a texture tag then display a friendly message box. Click "New" and create a new script called "Make Little Red Sphere" and enter the following code:

var sphere = new(SphereObject);
sphere#PRIM_SPHERE_RAD = 20;
sphere->SetName("LittleRedSphere");
doc->InsertObject(sphere, NULL, NULL);  
 
var mat = new(Material);
mat->SetName("Red");
mat#MATERIAL_COLOR_COLOR = vector(1,0,0);
doc->InsertMaterial(mat, NULL);
 
var texTag = AllocTag(Ttexture);
texTag#TEXTURETAG_MATERIAL = mat;
sphere->InsertTag(texTag , NULL);
 
TextDialog("'LittleRedSphere' has been created\n :-)",
             DLG_OK + DLG_ICONEXCLAMATION);

Make sure you hit "Save all" to save your scripts regularly. Under OSX this should save the script to: Cinema4D\library\scripts\Make Little Sphere.CSC, although notice you cannot open this in a text editor. To make a more complex script we could place all this inside main tags, and then be able to add global variables, functions and classes. For the greatest flexibility however, you can use a text editor such as XTool to write menu plugins which extend the MenuPlugin class. These must be saved with a .cof extension and placed in the Cinema4D\plugins\ folder. I have an example of this here: {LINK TO IMOD CONVERTER}

NOTE: Each time you change and save a .cof file, you may have to go: Console Window > Plugins > Reload COFFEE Plugins to update the changes.


Tutorial 2: COFFEE Tags

COFFEE tags are scripts which are attached as a tag to an object, and saved as part of the .cd4 file. Unlike menu plugins, COFFEE tags execute every time the scene is redrawn, and can therefore be used to create animation.

A few warnings however:

  • COFFEE tags are slightly more limited than menu plugins (eg: they cannot call/create user interface elements)
  • COFFEE tags will execute everything inside main() almost every time your scene changes (not just when you hit "Execute") - and therefore it is easy to crash the program accidentally. For this reason it can be helpful to save regularly using: main menu > File > Save Incremental.... To prevent a COFFEE script from executing, select it, and turn off "Enable" in the "Attributes" panel. To control the number of times it executes it can be helpful to use global variables (since this only reset when you press "Execute").

To create a COFFEE tag: you can simply select any object in the object tree, right click this and select CINEMA 4D Tags > COFFEE. The Expression Editor will automatically appear with and you can start coding directly. For the first example code, select "LittleRedSphere" (if this object doesn't exists just add a new sphere), add a COFFEE tag and enter the following code into the Expression Editor:

main(doc,op)
{
  if(op#ID_BASEOBJECT_POSITION:VECTOR_Y >= 200)
    op#ID_BASEOBJECT_POSITION:VECTOR_Y = 0;
  else
    op#ID_BASEOBJECT_POSITION:VECTOR_Y += 1;
}

NOTE: Within the main tag doc is the current document (cd4 file) while op points to the object containing the script.

You can hit "Execute" in the Expression Editor to test one "iteration", but to test the code properly you'll want to press the "Play Forwards" [F8] green arrow on the timeline bar. While it plays your object should move upwards one unit (in Y) each frame then eventually reset back when it reaches a height of 200 units. Now we want to turn this off, so select the tag (in the Objects list), and in the Attributes untick "Enable". Our next example is a little more advanced: we'll want to create something that resembles a "particle emitter" which will clones our "LittleRedSphere" object, but we don't want to attach this code to that object (otherwise it will get cloned along with the object)! Create a new Null Object (or any object) called "SCRIPTS", add a COFFEE tag to it and enter the following code:

GetRandomFloat(min, max);        // Function declaration.
 
main(doc,op)                     // Point of entry.
{
  //## CHECK SPHERE EXISTS:
 
  var sphere  = doc->FindObject("LittleRedSphere");
  if(!sphere)
  {
    println("'LittleRedSphere' was not found \n" +
            "   ... run 'Make Little Sphere' first" );
    return;
  }
 
  //## GET FRAME NUMBER:
 
  var t     = doc->GetTime();
  var frame = int(t->GetFrame(doc->GetFps()));
  println("frame = ", frame );
 
  //## GET AND/OR CREATE NULL OBJECT:
 
  var nullObj = doc->FindObject("Container");
  if(frame == 0 && nullObj)        // If first frame:
  {
    nullObj->Remove();                // Delete container.
    return;
  }
  else if(!nullObj)
  {
    nullObj = new(NullObject);
    nullObj->SetName("Container");
    doc->InsertObject(nullObj, NULL, NULL);
  }
 
  //## MAKE A CLONE OF THE SPHERE:
 
  var newSphere = sphere->GetClone(0);
  newSphere->SetName("Sphere" + tostring(frame)  );
  doc->InsertObject(newSphere, nullObj, NULL);
 
  //## FOR EACH CLONE (UNDER "CONTAINER")
  //## RANDOMLY OFFSET ITS POSITION:
 
  var child;
  for(child = nullObj->GetDown(); (child); child = child->GetNext() )
  {
    child#ID_BASEOBJECT_POSITION:VECTOR_X += GetRandomFloat(0,20);
    child#ID_BASEOBJECT_POSITION:VECTOR_Y += GetRandomFloat(0,5);
  }
}
 
 
var random;                       // Global variable.
 
GetRandomFloat(min, max)          // Function defined.
{
  if(!random)
  {
    random = new(Random);
    random->Init(time());           // Seed random value.
  }
  var randomFloat = random->Get01();  // Random value between 0 & 1.
  var diff = max - min;
  return (randomFloat * diff) + min;
}

Once again, we can test this using "Play Forward" (or use the [F8] shortcut key to play and pause). In this example: each frame we create a duplicate of the object "LittleRedSphere" (from the previous mini-tutorial)and add it to a Null Object called "Container". We then randomly displace each duplicated sphere (ie: each child of "Container") along X and Y. If we are at Frame 0 however, we delete "Container" (and with it all the cloned spheres we created), so that we are effectively back to our "starting position" before we started making changes. Note that when you clone an object, any tag that was attached to that object is cloned too!


See Also


Links