Professional Documents
Culture Documents
Chapter 1
The task I have assigned myself is not an easy one; teach C.O.F.F.E.E.
Not the beverage of course, but the scripting language included in CINEMA
4D. And why is it a daunting task? Because, usually scripting, or anything related
to programming, is considered a subject for geeks, chess club stars, rocket
scientists and non-artistic people in general. This couldnt be farther away from
the truth. Im not a chess club star, although I do know how to play chess. Im
not a rocket scientist, but I did graduate as a graphic designer and I do know
how to code in C.O.F.F.E.E.! OK, I never said I wasnt a geek, but even if many
people consider me as such, I dont think Im one of those :)
What exactly is C.O.F.F.E.E.? Its a scripting language. OK, this is not very
meaningful to all you mouse-drag-mouse-click-doodle-stuff-on-screen guys, is
it? Instead of "scripting language", lets say it is a programming language. Wow!
Now instead of something that you dont understand, it became something you
fear. Let me assure you, there is no reason to be afraid. I intend to make this a
fun experience from beginning to end. Now that you understand C.O.F.F.E.E. is
a programming language, you can, and will, use it to make CINEMA 4D do stuff
that would be, otherwise, very hard or completely impossible to do by hand,
mouse, pen or whatever.
I have no doubt one of the first questions you "artists at heart" asked
yourselves when you first discovered the C.O.F.F.E.E. language:
"Why the hell is it called C.O.F.F.E.E.?
3
Do you need industrial amounts of C.O.F.F.E.E. to learn and tame it? Like
the beverage, is it a "black" art?"
I dont have official confirmation from MAXON, but I assume, and please
DONT quote me on this... I will vehemently deny it, that it is because it is very,
very, very similar to JavaScript, and java is a type of C.O.F.F.E.E.(1).
Oops, I said another "foreign word", JavaScript. JavaScript is another
programming language often used to code stuff, mainly for Internet pages. It
is a very structured programming language based on C and C++. Yes, you got
it... C and C++ (pronounced "see" and "see plus plus") are also programming
languages. As you may have already guessed, there are many, many (a few
hundreds, if not thousands of) programming languages. Until now, to you, all
of them sounded like the forbidden Black Language of Mordor, (sorry for the
Tolkieneske pun) but I promise I will try to make, at least, C.O.F.F.E.E. almost
as easy to understand as plain English. If you already know a bit of JavaScript,
Java, C or C++ you already know a bit of C.O.F.F.E.E. Believe me, they are
almost the same. If you are still a "virgin" at this matter, do carry on reading.
OK, what exactly is a programming language? Its a set of instructions
(commands, functions, operands, operators, etc) that, assembled according
to a specific syntax, instruct the computer (or, in our case, the CINEMA 4D
application) to do stuff. So, what are "commands", "functions", "operands",
"operators" and that "syntax" I talked about? Commands are exactly what their
designation mean: they are "words" that make the application actually perform
an action. Functions are like magic boxes that you place stuff inside and
something different comes out. This means that you feed them with data and,
once some operations are performed on that data, some sort of result will come
out. Operands are the data you work with. It can be a number, a word, a 3D
primitive, a texture, etc. Operators are operations you can perform on operands
(the data, remember?) The "syntax" is the set of rules you must follow to write
correct code, just like the rules of the syntax of your mother language if you
want to write correct sentences. Im telling you all this because, inevitably, I will
have to introduce you to some programming-specific terms (sorry, there is no
escape from that my friends). Even if they sound complex, cryptic or just plain
weird, they will become obvious and even logic after a while.
Lets start with our first program in C.O.F.F.E.E. and, with it, I will introduce
you to all the concepts I presented you with before.
This first program will simply print out the sentence "Hello World". Why?
Well, I cant really explain why but all tutorials about every programming
language, whatever they are, start with a simple program that prints out "Hello
World". I guess it is because all programming languages have, at least, a
command that prints something and, this way, people can get a first "test-drive"
of a programming language in a very soft and easy way. Otherwise, they would
give up at their first try.
(1)
After writing the book, I found out that Maxon coined up an official acronym for C.O.F.F.E.E. which is Cinema Object-oriented Fery
Fast Environment Enhancer. We must all agree that it is not a very good one. but there had to be a reason for the dots between
the letters, besides being an aesthetical decision.
In this case, it defines your main function. This is a very special kind of
function because it is the first function that is executed. All C.O.F.F.E.E.
expressions must have, at least, a main function. Otherwise, you would get an
error and nothing would be executed. This function doesnt return any value,
actually, (remember my previous definition of function?) but it does include two
operands: doc and op.
What are those? Well, they are values that you can use inside the function.
Let me give you an example of another function that you may be more familiar
with: The power of two function. If you recall your math in school, you can raise
any number to the power of two. Its as simple as multiplying the number by
itself. So, for instance, 3 raised to the power of two is 9... or 3 x 3. The same
way, 5 raised to the power of two is 25... or 5 x 5. Easy, isnt it?
5
So, lets give this function the name raise_to_power_of_two. For it to make
its mumbo-jumbo (multiply a number by itself) we must feed it with a number,
right? It will then perform whatever operations are needed and spits out a result.
The number we feed it with it called an operand. So, we could code the raise_
to_power_of_two function like this:
raise_to_power_of_two(x)
{
return x*x;
}
The value that we placed between parentheses, after the name of the
function, is the operand that the function will use. Why cant we place a number
there? Well, we are just defining what the function does, not really performing
any calculation. If we placed, for example, the number 3 there, this function
would always calculate 3 raised to the power of two. Not very useful, you must
agree.
In this case we used a letter x. Its a variable. Why is it called a variable?
Well, because it can contain any value.
So, inside the function the commands between the { and the } we
calculate x times x and we return that value. That is exactly what the command
return x*x; does. I believe that return x*x is obvious enough but... what
about the ; at the end? All commands in C.O.F.F.E.E. must end with a semicolon.
This is a way for C.O.F.F.E.E. to know when a command as actually finished
being defined. This is one of the rules of the syntax of C.O.F.F.E.E. See? You
have already learned what a function and an operand are. You also had a little
glimpse of what a variable is, and you had a little taste of what "syntax" really
means.
We have now seen the { and } symbols twice and I havent explained exactly
what they are or mean. They refine a block of code. Everything between them
defines a set of code instructions that relate to each other. The logic of using
them in a function is that, after defining the name and operands of the function,
everything between the { and the } symbols belongs to that specific function.
This means that, right after the name of the function (and operands, if any),
C.O.F.F.E.E. encounters a { symbol. This defines the start of the code of the
function. When it finds the correspondent } symbol it "knows" that the function
ends there. Logic, isnt it?
main(doc,op)
{
}
...Lets understand what the doc and op are. From what you learned already,
you know they are the operands of the main function, right? And, also, that they
are variables. Before explaining exactly what each one is, I must clarify what a
variable is. Imagine a variable as a storing box. Inside it you can place values.
And what type of values? Well, pretty much everything. You can store numbers,
letters, colors, objects, tags, materials, etc. When I say objects, tags, materials,
etc., I mean a value that points to an actual object, tag, material, whatever,
inside your document. So, variables are invaluably useful. Without them you
would be able to do very little with C.O.F.F.E.E., or any other programming
language.
So, if variables can store so many things, what do the doc and op variables
store? CINEMA 4D politely provides you automatically with the current
document (the one you are working with) in the doc variable and the object that
contains the C.O.F.F.E.E. tag in the op variable. This means that as soon as
you enter the main function you can access info about your document and also
info about the object that contains the C.O.F.F.E.E. tag whose main function is
being executed. This is very valuable information but in this first chapter we will
not need it because we only want to print a simple sentence. No fiddling with
documents or objects is required for now.
If you paid lots of attention to all that you have read here, you may be
wondering why is it that the main(doc, op) doesnt have a ; at the end. If
you asked that question, you are my best student so far!!
Well, because main(doc, op) is not a command. It doesnt instruct the
computer to do anything. It just defines a function. Its an instruction, not a
command. Remember, I said that only commands require the ; at the end.
Syntax rules, you know? ;-)
Place your cursor between the { and the } symbols and type:
Notice that the ln after the print are lowercase L and N, not an uppercase i
and lowercase N.
What have we just typed? The println is a command that instructs CINEMA
4D to print out something. Between the parentheses are its parameters, as in
what you want to print. And why is it between quotes? Well, because what
we want to print is a literal expression, not a variable. Ok, ok... Im talking
gibberish again. What is a literal expression? Well, its something that should
be interpreted as is! Want an example? Ok, no problem...
Imagine you had written
println(hello);
instead of
println("hello");
And you had a variable named hello that is storing the word goodbye.
Should CINEMA 4D print hello or the content of the variable hello that is,
actually, goodbye?
To not confuse CINEMA 4D, we must enclose all literal expression
(as in, they should evaluate exactly as they are presented) in quotes. So,
println(hello); would print goodbye because, since hello is not enclosed
in quotes, it is evaluated as a variable. The command println("hello");
will print hello because, since it is between quotes, it should be taken as it is
written. This is pure syntax at work. See how important the syntax is?
Ok, you just wrote your first C.O.F.F.E.E. script. You must now check if it
has any errors. To do so, click the Compile button on the top of the Expression
Editor window. If it all goes fine, you should get a report of No Errors! at the
bottom of the Expression Editor window.
Now, hit the Execute button at the top of the Expression Editor window.
8
Wow! Amazing! Nothing happened! This Rui guy is a charlatan!! Wait... the
printout is in there... somewhere. You just need to know where to look for it. If
you are using an 8.x version of CINEMA 4D, press Shift+F10. If you are using
a 9.x version of CINEMA 4D or higher, press Alt+F9 or simply choose Console
from the Window menu, in any version of CINEMA 4D.
Now you see your Hello World at the bottom of the window? Ah, this Rui
guy is not a charlatan, after all. Do you see more stuff printed in the Console
window? If you do, that is all the stuff that plug-ins print when they load. Actually,
the Console is a very useful place to go to check to see if something is wrong
with any plug-in that is not loading or simply misbehaving.
Now, from the File menu of the Console window, choose Clear. You are
now staring at a clean Console window. With the Console window still open,
press Play, like you would do to check out an animated scene in CINEMA 4D.
Wow, lots and lots of Hello World sentences! You now know that your script is
executed for each frame of your animation. Actually, it is executed each time
something changes in your scene. Choose Clear again from the Console File
menu and try moving the Null around. Again, lots
of Hello World sentences. This is not particularly
useful right now, but its good to know, for future
reference, how often the C.O.F.F.E.E. scripts are
executed.
One final thing before we wrap-up this first
chapter about C.O.F.F.E.E. Why the hell is the
print command named println and not just
print? In this case, ln (lowercase L and N) stands
for line. You are instructing CINEMA 4D to print
out a line of text. A line of text means that a return
is added at the end, automatically, just like if you
had pressed Enter, otherwise it would print out
all the Hello World sentences "glued" together,
as in:
Hello WorldHello WorldHello WorldHello WorldHello WorldHello WorldHello World...
If you want to try it out, replace the println with print. As soon as CINEMA
4D fills out an internal container of the Console, it will print out a whole line of
neatly glued together Hello World. You can force the print command to add a
return, but why bother if you already have the command println, right?
This is it for this first chapter. I hope I didnt frighten you too much with all this
code stuff. I also hope I was able to interest you enough to keep you reading on
through the rest of the chapters. Lets move on to Chapter 2.
9
Chapter 2
Here we are, back in the C.O.F.F.E.E. classroom once again. I hope you
enjoyed the first chapter and are eager to learn more. I promise we will learn
and do a bit more. As in the previous chapter, this one is a bit challenging. This is
the first time I will write a full textual chapter. Yes, thats right! No pictures in this
one, so the challenge is doubled this time around. Trying to keep you interested
in a tricky subject, and doing it without pictures is quite a task. If I succeed, I will
be more than motivated to go on with writing/teaching this programming stuff.
In this chapter I will present lots of C.O.F.F.E.E. code snippets. To test
them, do like you did in the first chapter: create a Null, add to it a C.O.F.F.E.E.
expression tag and type the code into it. Dont forget to keep the Console
window open. This enables you to check the code messages and any eventual
error message that may show up. Enough with the small talk! Lets move onto
the juicy stuff!
Do you remember what I said about variables in the last lesson? I said that
they were like container boxes that could hold lots of different things. That, by
itself, makes them very powerful and useful. Of course you want examples and
I have no fear of showing them. Imagine you have this little C.O.F.F.E.E. script:
main(doc,op)
{
println("Please select a Null.");
println("If you dont select a Null and a Null only, I
will not be able to go on with this script.");
println("So, please, humor me and select a Null before
you choose this script.");
}
10
From what you learned in Chapter 1, you already know what this script does.
Now, just between us... The word "Null" is repeated a few times, isnt it? What
if you decided that instead of a Null, your script would need to have a Sphere
selected? Mmmmmmm, lots of changes to make, even with the invaluable help
of our beloved couple of friends: Copy and Paste.
What if we had written the previous script this way?
main(doc, op)
{
var obj;
obj="Null";
println("Please select a "+obj);
println("If you dont select a "+obj+" and a "
+obj+" only, I will not be able to go on with this
script.");
println("So, please, humor me and select a "+obj+"
before you choose this script.");
}
Now, you just need to change the line that reads:
obj="Null";
to
obj="Sphere";
or whatever you want and the sentences will all print a different thing.
This is a very basic and, dare I say, stupid example, but it illustrates the
simplest power of variables: its re-usability.
So, what have we done? First we defined the variable with the instruction
var. This instruction is followed by the name of the variable we want to define.
It can have any name you can think of, but it must start with a letter and can only
contain letters, numbers and underscores. No spaces are allowed. Samples of
valid variable names are:
name
number
thingy
whatever
whatever2
yadayadayada
this_is_a_very_long_variable_name
11
is_this_a_variable?
return
This last one may sound a bit tricky, as you dont know the name of all the
commands in C.O.F.F.E.E. Dont worry. If you try to declare a variable with the
name of a command, you will get a syntax error. I will not talk about the errors
now but rest assured, as soon as you make a mistake you will get a warning.
So, no misbehaving! ;-) Also, you should try to refrain on the length of the
variable names. Variables are useful and used very often. You dont want to see
yourself typing look_at_this_humongus_variable_name, whenever you
want to use the look_at_this_humongus_variable_name as the name of
a variable.
You can declare more than one variable using the same var instruction. So,
instead of writing:
var
var
var
var
var
name1;
name2;
name3;
my_number;
another_number;
Ok, once you declare your variables, you can start assigning values to them.
You do that, simply, by typing something like:
name1="Mr.";
name2="John";
name3="Smith";
my_number=1;
another_number=2;
In other computer languages you have to define what type of content the
variable will store. So, if you wanted the variable to contain words you would
have to declare it as a String type (string as in... a string of characters). If you
wanted it to contain integer numbers (numbers with no decimal part) you would
have to declare them as Integer. If you wanted non-integer numbers, you would
have to declare them as Float, or as Single or Double.
Ok... forget all I just said!!! Dont worry about it. C.O.F.F.E.E. is a much
friendlier language. You just define the variable names and you can place
whatever you want inside them. C.O.F.F.E.E. does all the juggling necessary
to deal with all the data types. Nice, isnt it? And the good news doesnt end
here. Even if you do have to follow a restricted syntax, that very same syntax is
flexible enough to allow you to write the same sentences in many different ways
and even combine several lines of code into one. So, it is perfectly possible to
write the following:
var name1="Mr.",name2="John",name3="Smith",
my_number=1,another_number=2;
or
var name1,name2="John",name3="Smith",my_number,
another_number=2;
In this case, name1 and my_number are only declared and have no starting
values assigned.
Now that you already know what variables are and how they are created,
how can you use them, besides simply assigning them values and printing them
to the Console? To really start using variables in a productive way, and to start
harnessing the true power of any programming language, we will have to dip
our feet a little deeper into the C.O.F.F.E.E. commands. Fear not... I will guide
you throughout the entire process. The first thing we will learn is how to deal
13
with conditions. In the real world, conditions work, more or less, like this:
Execute button, in the Expression Editor (first click the Compile button to check
for errors). If you check out the Console, there should be at least one "The
variable a is equal to one" sentence there. Now, try changing the line:
var a=1;
to
var a=3;
Now, press Execute again. No additional sentence is added to the Console
because now the variable a contains the value 3 and the comparison with 1
fails. What if you wanted to execute more operations if the condition tested is
true? Remember, from Chapter 1 that I said that the { and } symbols were used
to define a block of code? So, check out this script to see if it sounds logical to
you:
main(doc,op)
{
var a=1;
if(a==1)
{
println("The variable a is equal to one");
println("That is why the condition evaluated to a true
value");
println("and these sentences are showing on the
Console.");
}
}
You may have noticed that the sentences are indented. I mean... I pressed
the Tab key before each line that is part of the block of code between the {
and } symbols (including the { and } symbols themselves). That is not really
necessary but it makes for clearer code. We will see later why indenting lines is
so helpful. Have you executed this script? It should print the whole sentences to
the console. If not, check for any syntax error.
What if I wanted something else to happen if the variable a was not 1? Well,
I could write this:
main(doc,op)
{
var a=1;
if(a==1) println("The variable a is equal to one");
if(a!=1) println("The variable a is NOT equal to one");
}
15
Inside the conditional expression (the stuff between the parentheses) you
can perform any comparisons. I mean, you can check out everything that would
evaluate to a true or false value. Examples of valid expressions are:
Expression
Meaning
a==1
a is EQUAL to 1
a!=3
a is DIFFERENT than 3
b==1.56
b is EQUAL to 1.56
c!="word"
a>10
a is LARGER than 10
b<-5
c>=20
c is LARGER or EQUAL to 20
a==b
a is EQUAL to b
c!=b
c is DIFFERENT than b
As you can see, lots of possible comparisons are possible. But beware!!
Some things are simply not possible. For example, check out this script:
main(doc,op)
{
var a=10;
var c="10";
if(a==c) println("The variable a is equal to variable
c");
}
This will NOT print the sentence "The variable a is equal to variable c"
simply because they are not equal. The variable a stores a numeric value and
the variable c stores an alphanumeric value. Wow!! A new word: alphanumeric.
Well, alphanumeric is, in this case, just another way of saying "string", as in
"a string of characters". So, the variable c stores the character "1" and the
character "0", not the numeric value of 10.
Before we wrap up this chapter, the only thing missing from the comparison
expressions is a way to test for more than one condition at the same time.
As I stated in my previous textual examples of conditional expressions, you
could test for something AND something and something OR something.
17
19
Chapter 3
Once again we meet here on these pages. I truly hope that the previous two
chapters were not boring, since it is such a dry theme to most of you artisticallybiased-people. Anyway, just to make sure Im not bothering you much, I decided
to break the rules a bit and teach you something more exciting in this chapter.
I say that Im breaking the rules because, following the correct time-continuum
of programming teachings, I should be giving you more insights about more
general programming concepts. But, you guys (and girls, I hope) have been
such good students that I decided that it was about time you started doing more
productive stuff and jump right into specific C.O.F.F.E.E. stuff.
To do so, I must introduce you to object-oriented programming. Dont
confuse this "object" with the objects you have in CINEMA 4D (Cubes, Spheres,
Cones, Lights, Splines, etc). Even so, they are closely related, as you will soon
understand.
So, what is an "object" in programming? (in our case, in C.O.F.F.E.E.)
Put in a very simple way, its an assembly of data and tools to manipulate
that data that can also inherit or bequeath data and tools from/to other objects.
Doesnt seem like a very simple explanation, does it? I will give some
examples. Objects can be anything and, in my first example, I will say that my
object is a car. To define a simple car (our object), I can have several items and
those items are my data or, more precisely, the data of the car object. Lets keep
it simple. The data I want to use to define my object is:
- base_color
- weight
- year_of_manufacture
20
What? Just that? Yes... because Im just defining a car and ALL cars have, at
least a color, a weight and a year when they were made. You may have noticed
that the base_color and year_of_manufacture use the variable naming syntax
(the underscore instead of a space). This is because all data associated with an
object is, in fact, a variable... as in, it stores values. Attached to the object car
we can have a tool that calculates, for example, the value of devaluation of the
car, based on the year it was made.
How do we access the data from the car object? Simple... like this:
car.name_of_data
for example:
car.color
or, to access a tool:
car->name_of_tool();
for example:
car->devaluation()
Now we want to define a more specific car. Lets say, a family car. So, we
will define a new object named family, based on the object car. This is VERY
IMPORTANT!! It is a new object based on another object. We define an object
named family and we add to it some more data:
- number_of_seats
- number_of_doors
- trunk_volume
Lets just keep it this way... I want to keep it simple.
Attached to the object family we can also define a couple more tools.
Let us say, for example, that we have a tool that calculates the maximum
weight of the car called, appropriately, maximum_weight(). Notice the opening
and closing parentheses! This means that maximum_weight is a "tool" (a
function), not some data (a variable). Now comes the fun part. Since we based
the family object on the car object, the family object, automatically, inherited all
the data and the tools from the car object. So, the family object also has a color, a
weight and a year of manufacture. The family object also has access to all the tools
21
that the car object has. So, if we want to define, yet, another object, lets say a
more specific family car, we can do the following: we define a new object named
renault (hey! Im European... I can very well choose a European brand. You can
add to it a few more data:
- country_of_origin
- main_factory_location
And, just to wrap it up nicely, we define yet another object. This time a very
specific car. This one, based on the renault object, we will call it twingo (it was
my first car, so bear with me, ok?) We can add to it a lot more data now:
- custom_color
- licence_plate
- max_speed
- number_of_cylinders
etc...
You could also define many tools associated with the object twingo. For
instance, average_speed or fuel_consumption. Notice that the tools names
must also conform to the functions naming syntax. But, they are exactly the
same as the variable naming syntax so, no worries here.
Now that we have four objects defined, all of them based on a more generic
object we can create a new object. Notice the difference between define and
create!! First we defined the objects: we defined what data could they contain
and what tools they could have attached but we havent created any real object,
just the definition of it. Now we create a new object and feed it with some real
data. In C.O.F.F.E.E. that is done with the new command but Im just giving you
a very generic example of what objects are so dont worry about programming
language terms, ok?
So, lets say I want to create an object that describes my own car. So, I create
a new "variable" whose type is, lets say, twingo... since my car was a twingo...
dah!!!
So, I call it my_car. If we were to do it in C.O.F.F.E.E., I would write something
like:
var my_car;
my_car=new(twingo);
Dont type these C.O.F.F.E.E. scripts!! They are just for the sake of example.
22
are objects. Both have data and functions attached. Wanna see something fun?
Create a Null object (yes, this time a real CINEMA 4D geometric object) and
add to it a C.O.F.F.E.E. tag and type this:
main(doc,op)
{
println(op->GetName());
}
Press Execute and check out the Console. The last entry there should be the
name of the object that contains the C.O.F.F.E.E. tag, probably, "Null Object".
Try the following: Create a few different objects (cubes, spheres, cones, lights,
splines, etc.). Now, keeping the Console window open, drag the C.O.F.F.E.E.
tag to each of the objects you created. On the Console, you should see each
object name appear printed. Cool, isnt it? What exactly does op->GetName()
do?
GetName() is a function that is attached to the object op. And what is the
object op? Its a (programming) object that "contains" the (geometric) object
whose C.O.F.F.E.E. tag is running the main function. I closed the word contains
between quotes because it doesnt really contain anything... it is just pointing
to the object, as in, referencing it. You should pay special attention to the
capitalization of the names of the functions that are attached to objects. For
example, getname will not work. Neither will Getname or even GETname. Only
GetName will execute the relevant function that, in this case, simply returns the
name of the object it is attached to.
And, is doc an object too? Sure it is. Lets write a more complex script to
prove that:
main(doc,op)
{
var obj;
obj=doc->GetFirstObject();
println(obj->GetName());
}
This script will print the name of whatever object is on the top of the Object
Manager list. Wow!! We are already using variables and objects to do something
more than print silly stuff!
The object doc is different from the object op, of course, even if they follow
the same rules. The doc object is a kind of object that deals with CINEMA
4D documents and the op object is a kind of object that deals with CINEMA
24
I believe the only thing that is a bit weird - although, somehow familiar, if you
remember the last lesson - is the (!obj)
Do you remember what was the expression for NOT EQUAL? It was !=, right?
So, the exclamation point stands for NOT, right? In that case, we could read
the line:
if(!obj)...
as
If Not Object...
Ok, ok... it is not correct English but I guess you got the point, didnt you?
This means that "if not obj" or... if the obj variable has a value of Nil, we do
something. Actually, using a reverse syntax, if you check for the non-existence
of a value inside a variable by typing !variable_name, you can check that it
contains something by simply typing the variable_name after the if command.
Like this:
main(doc,op)
{
var obj;
obj=doc->FindObject("Torus");
if(obj) println("What the heck!! There is an object named
Torus!");
else
println("Oh, it was a wild-goose chase... no Torus in
here.");
}
This is getting more fun now, isnt it?
By the way, the FindObject() function returns the first occurrence of the
object you searched for, if it finds any. So, if you have more than one object
named "Torus", the FindObject() function will return the one that is more on
top.
I will introduce another type of object now. Let us see if you can spot which
object is. Type this C.O.F.F.E.E. script, but DONT execute it!!
main(doc,op)
{
var obj,pos;
obj=doc->FindObject("Torus");
if(obj)
26
{
pos=obj->GetPosition();
println("The object Torus is placed at position ("
+pos.x+","+pos.y+","+pos.z+")");
}
}
Before executing this script (if you havent already, you hasty coder!!) let us
see if you spotted the new type of object. The doc variable holds an object of
the type "document". The obj variable holds an object of the type "primitive".
This naming ("document " and "primitive") is arbitrary, of course... its only for
you to understand what type of data each object deals with.
The new type of object is held by the pos variable and is of type "position". It
has three data values attached: x, y and z. Since you assigned it to the variable
pos, you can access its data by typing pos.x, pos.y and pos.z
So, the object of type "primitive" (that is held by the variable obj) has a
function attached to it, called GetPosition(). As you can see by the opening
and closing parentheses, it requires no parameters but it returns an object of
type "position" that holds the position coordinates of the object it points to.
Understood? It may sound hard at first, but its quite logical. Really! Believe
me, if you dont, just read the last explanations a few more times and, if
necessary, scribble some doodles to "graphicalize" the concepts.
Now, execute the script. Oops... there is an error message in the Console.
It reads something like:
C.O.F.F.E.E. ERROR!
(5) Incompatible values... STRING / FLOAT
File:expression
Line:9
It is telling you that there is an error in line 9 of the file expression. A file?
Well, C.O.F.F.E.E. code can appear in tags (they are considered expressions)
or in files (they are, usually, plug-ins). So, it is telling you that this code is from
an expression. And it also tells you that is an error number 5, namely, you are
using a value that is not compatible with that particular context. What could it
be?
Well, the println command requires a string of characters and that is what
we are feeding it with... until the point we typed pos.x
27
Oops, pos.x holds a numerical value, not a real text. So, we must transform
the number it holds into a string of characters, in order to make it compatible
with what the println command requires. Luckily, there is such a function in
C.O.F.F.E.E.. Its called tostring and it changes the parameter we feed it with
into its equivalent representation with characters.
For example, tostring(3.14) will return "3.14" or, in other words, the
character "3", followed by the character ".", followed by the character "1" and,
finally, the character "4".
To get rid of the error, retype the offending line:
println("The object Torus is placed at position ("
+tostring(pos.x)+","+tostring(pos.y)+","+tostring(pos.
z)+")");
As you can see, we can place any value that evaluates to a number as
the parameter for tostring. It could be a literal number like the example
above (yes, the 3.14) or any variable or expression that evaluates to a number.
This is true for all functions. If a function requires a string, you can feed it with
whatever value that evaluates to a string, be it a literal expression, a variable,
an expression or even another function whose result is a string.
You should have, printed in the Console, something like this:
The object Torus is placed at position (0.000000,0.000000,0.000000)
Try moving the Torus around. Oh, I assume that, for all this to work, you
DO HAVE, in fact, an object named "Torus" in your document.
The C.O.F.F.E.E. tag can be attached to any object (even the Torus itself).
Since it is searching for an object, there is no special requirement as to where
the tag must be placed.
Moving the Torus around, you should get something like this in the
Console:
The object Torus is placed at position (0.000000,0.000000,0.000000)
The object Torus is placed at position (3.834658,-6.183756,5.047592)
The object Torus is placed at position (6.098374,-3.027564,8.905743)
The object Torus is placed at position (9.983744,-10.658736,6.234875)
The object Torus is placed at position (12.347572,-15.657584,9.577455)
The object Torus is placed at position (14.665849,-17.023374,10.123497)
The object Torus is placed at position (18.835734,-19.002347,12.039457)
28
...
Just to finish, and to keep all this coherent, now that we know how to read
the position of a primitive, how do we set its position to a specific value?
As you will notice as you learn C.O.F.F.E.E., when we have a function that
reads a value, there is usually an equivalent function to set the correspondent
value. So, if there is a function named GetPosition, there is also another
named SetPosition.
Let us now write a script that prevents an object that is a child of another, to
move in the Y axis. This is useful for objects that we want to keep on the ground,
for example.
Create a Cube, add to it a C.O.F.F.E.E. tag and type the following:
main(doc,op)
{
var pos;
pos=op->GetPosition();
pos.y=0;
op->SetPosition(pos);
}
Of course, the SetPosition function requires a parameter. After all, it has
to know what position we are assigning to the primitive. The GetPosition
function requires no parameters... it simply returns a value.
You may have also noticed that the parameter we used in the SetPosition
function is the whole pos variable. No, we cant simply give it the Y position; even
if that is the only coordinate we want to change. The SetPosition function
requires a parameter of type "vector" and that parameter holds all axes: x, y
and z. So, you simply give it the whole object (in this case, stored inside the pos
variable) and the SetPosition function uses whatever it needs from it.
You can drag the C.O.F.F.E.E. tag that is assigned to the Cube to any other
object and it will work with whatever object it is attached to. That is because we
used the op object that holds the primitive whose C.O.F.F.E.E. tag is executing.
You can even clone the tag to as many objects as you want. It will work with all.
Wanna see a nice trick? Make sure the C.O.F.F.E.E. tag is assigned to
the Cube. Now create a plane and place the Cube inside it, as a child. Now,
rotate the Plane. The Cube rotated accordingly, as expected, right? Now move
the Cube around. Wow!! It ALWAYS stays on the plane ground level!!! As you
29
can see, the GetPosition and SetPosition functions work with the local
coordinates of the primitives. But Im starting to wander... All those concepts fit
much more nicely in a future chapter, when you already have a more in-depth
knowledge of C.O.F.F.E.E..
Im gonna wrap this chapter now. I believe that, after reading it, you have a
lot more info to deal with. Finally, you are starting to do interesting stuff arent
you?
My final advice is to read this chapter as many times as you need if you
didnt fully understand the concept of objects. Because, like I said before: if
you master the concept of objects, understanding all the following C.O.F.F.E.E.
commands and functions will be much easier.
30
Chapter 4
Remember those classes, in school, when then teacher would give you reruns of previous classes? Usually, that happened because there was a test
approaching or simply because some subjects were, in fact, too complex
and required extra attention. This is exactly what is going to happen in this
chapter: we will revisit our "old friends", the objects. Why? Because this is in
fact a complex subject and since I have the luck of getting feedback from my
"students", I realized that I should not advance any further into new subjects
until this very important concept is fully understood.
In the last chapter, I introduced you to objects, as in "object-oriented
programming". Since its an abstract concept, dealing with data and functions
accessed like they were super-variables, I gave you all a more graphical
example, making use of an analogy with cars.
Since the "ice" was already broken with the last chapter, I decided that I
could explain the concept again but, this time, using the "real-life" objects,
used by CINEMA 4D. This way, the ones that already understood the concept
of objects will not get disappointed with this "repeated" lesson. For the ones
whose concept of objects is still drifting outside your heads, fear not! This time,
I will use some pictures!
Ok, lets start (again) with this objects stuff. What the hell is an object?
(Objects of the programming kind of course... the CINEMA 4D geometrical
objects will be referred to, from now on, as primitives.)
31
Imagine an object as a structural entity. It can contain data and functions that
deal with these data or any additional data that we may provide.
That structure is simply a definition of the object, not the object itself. It
defines what type of data the object stores and what functions it has. Those data
fields (still empty) have names (in our sample object, the names are #A1, #A2,
#A3 and #A4). The functions also have names, of course (in our sample object,
the names are #A5, #A6 and #A7). Its by those names that we access the
data and the functions. When you create new objects (usually they are created
automatically, without you even noticing it, as you perform other operations),
what you are creating are, in fact, clones of that initial "master" definition. The
powerful part comes when you start creating objects based on other objects.
For example, object B is defined like this:
But, when it was created it was based on object A. So, object B will inherit all
the attributes of object A (its data structure and functions).
32
It is a more powerful object than object A because it can do all that object A
does and, in addition, some more things.
You can create an object C, also based on object A. but provide it with
different data structures and functions. You now have two objects, B and C that
have all what object A has, but also, each one, with something more.
33
If you now create an object D, based on, for example, object B, it will have
all the characteristics of object A and B and, of course, you can add to it some
more data structures and functions.
If this is still difficult to understand (I hope I dont have to repeat this many
times more) I will now present a real world example of objects, inside CINEMA
4D.
Let us take, for example, a spline primitive; of those that you draw with your
mouse or pen. So, you have drawn a little wavy curve and now you have a
new spline primitive in your Object Manager. As everything else inside CINEMA
4D, this primitive is an object. What type of object? Internally, the spline is
defined as a SplineObject. The name is logical enough? Dont forget that the
SplineObject is the name of a type of object. When you created the spline
(using your mouse or pen), CINEMA 4D created a clone of the internal definition
it has of a SplineObject and filled it with all the relevant data.
What type of things can you do with a SplineObject? Well, for example,
you can get its length with the function GetLength() because GetLength()
is a function that is attached to all SplineObjects. You can also get the number
34
of segments of the spline (if you have drawn the spline yourself, without any
further editing, you should only have one segment but splines can have many
more). You can also calculate a position along any of the spline segments.
There are many other functions associated with the SplineObject.
But, you may think, a spline primitive has more than just segments. It has
control points, for example. Does a SplineObject also provide ways to have
access to those points?
Yes, of course. A SplineObject was derived from another class, called
PointObject. And the PointObject type of object provides lots more
functions. Functions to get the number of points of an object, to get or set the
location of any point, to get or set the selection of points, etc.
Besides points and segments, a spline primitive also has a few more
parameters. It has a name, a position, a scale, a rotation, etc.
Can we access all those parameters too? Sure we can, because the
PointObject object is derived from yet another class called BaseObject.
This type of object has many, many functions. As its name implies, a
BaseObject is a class that holds all those informations (and functions) that
are common to all types of primitive objects inside CINEMA 4D. As you know,
all objects have, at least, a name, a position, a scale, a rotation, and an editor
and render state (those semaphores that define if an object is visible or not, in
the editor or when rendering), etc.
This is big "family" already, isnt it? We have the SplineObject class
that is a descendent of the PointObject class that is a descendent of the
BaseObject class. Are there any more members of this family? Sure there
are: the BaseObject class is a descendent of the BaseList4D class. This
class gives us access to the parent (if any) of the object or to its first child (if
any). What are those? You know... in the Object manager, you can place objects
inside others, creating hierarchies. Well, the BaseList4D functions allow you
to have access to those hierarchies, namely the parent and child of our object
and a couple more functions related to them.
Finally, the BaseList4D class is a descendent of the BaseList2D class.
This last class controls even more basic stuff. Stuff like little bits of internal
information (if an object is selected in the Object Manager for example) the
editor color of the object, the type of object and some basic search functions,
etc.
You didnt have to do anything more than just draw the spline, but CINEMA
4D internally created all this. Well, it just had to create a new SplineObject.
All the other objects and their correspondent attributes are already implicitly
attached to it. So, its like the newly created SplineObject already has all this
35
amazing amount of functions and data attached, just because it inherits all the
stuff from all the members of its "family".
Let us now review the whole family again:
What if you had created a new polygonal object? A polygonal object can
be created in several ways but one of the simplest and fastest ways is to
create a primitive (a cube, a sphere, a plane, etc.) and hit C to convert it to
a polygonal object. How is it described internally? Its exactly the same as the
SplineObject, until the PointObject part. Then, instead of it deriving into a
SplineObject, it derives into a PolygonObject. Since the PolygonObject
derives from a PointObject, it has all the characteristics of a PointObject,
a BaseObject, a BaseList4D object and, finally, a BaseList2D object.
Are you starting to get all this object and inheritance stuff?
To put all this theory into practice, lets see some actual code that does
something. Create a new Null primitive, add to it a C.O.F.F.E.E. tag and type
this script:
main(doc,op)
{
var new_sphere;
if(!(doc->FindObject("My_Sphere")))
{
new_sphere=new(SphereObject);
doc->InsertObject(new_sphere,NULL,NULL);
new_sphere->SetName("My_Sphere");
}
}
36
If all goes well (no typing errors), as soon as you execute the code (or do
anything to your document, like dragging the time slider or zooming in or out),
a new Sphere primitive named "My_Sphere" should appear in your viewport
and, consequently, at the top of your Object Manager list. It is a faceted sphere
because it has no Phong tag attached. We would have to add it ourselves.
Dont forget that we are creating everything from scratch, using C.O.F.F.E.E.;
not using the shortcuts CINEMA 4D provides you.
So, what is happening in this script?
First, we define a new variable named new_sphere. It will store the object/
primitive we want to create. Yes, it will be an object because, as we learned at the
beginning of this lesson, all primitives are, in fact, objects (of the programming
kind... this is the last time I will say it).
Now we must check if there is already an object named "My_Sphere" in
the document. If we didnt do this, the script would create a limitless amount
of sphere primitives and fill the whole document with them. That is not a good
thing, is it?
So, how do we check for the presence of an object with a specific name?
Since we are searching the document and the doc variable - kindly provided
by the main function - already points to the current document, we can use one
of its functions. Wait!! A function of a variable?!? Do variables have functions?
They were supposed to only store data, right? Right, but if a variable is storing
an object, it behaves like an object and objects DO have functions. So, the doc
variable is, in fact, an object of type BaseDocument with all the data values and
functions related with documents already attached.
One of the functions present in a BaseDocument object is FindObject.
It requires a parameter that is the name of the primitive we want to search
for. It will return an object of type BaseObject pointing to the first primitive it
finds with that name. If it finds none, it returns Nil.
So, the line:
if(!(doc->FindObject("My_Sphere")))
could be read as:
if NOT found in the document an object named "My_Sphere"
Sorry for the lame English, but its hard to keep the same sequence of
statements present in the script and translate that into a coherent English
sentence.
37
Did you notice all of the parentheses? They are necessary and its very
important to assure that they are balanced. With this, I mean that there must be
as many opening parentheses and there are closing parentheses. Otherwise,
you will get a syntax error.
The most inner parentheses are the ones surrounding the "My_Sphere"
parameter. They are required because all functions require that their parameters
be enclosed in parentheses, even if the functions have no parameters. Really!!
For those you simply type an opening and a closing parenthesis, like this: ()
The next set of parentheses enclose the doc->FindObject("My_
Sphere") statement. They are not really required but, since I want to test for
the negative of this whole expression with the operator !, this whole expression
becomes more legible. If I hadnt placed the parentheses surrounding this
expression, it may look as if I was testing for the negative in the document, not
for the presence of a primitive. Its just a matter of style.
The outer parentheses surround the whole expression and they are required
by the if statement. Imagine that the if statement is a function and it also
requires that its parameters are inside quotes (but this is just to serve as an
example because the if statement is NOT really a function).
Ok, we tested for the presence of a primitive named "My_Sphere" in the
document and, if there isnt any, all the code inside the { and } symbols after
the if statement is executed.
The first instruction to be executed is:
new_sphere=new(SphereObject);
The new command creates a new instance of an object of type SphereObject
and this new object is stored in the new_sphere variable. The SphereObject
object is already internally defined and it is just like the SplineObject example
I presented at the beginning. It already has all the "family" of objects attached,
up to the BaseList2D object. Luckily, CINEMA 4D also provides with it, all the
default parameters already stored inside the data part of the SphereObject.
What this means is that the newly created sphere already has the default
"Sphere" name, the default 200 units of radius, the default 24 segments, etc.
We have a new sphere object stored inside a variable but it still has to be
placed into the document. That is done by using the statement:
doc->InsertObject(new_sphere,NULL,NULL);
38
renaming it. It stays with the new name you typed but, instantly, a new sphere
named "My_Sphere" appears in the document. Wanna fool CINEMA 4D? Create
a new primitive that is NOT a sphere, a Null for example. Name it "My_Sphere"
(without the quotes, of course). Now delete the sphere named "My_Sphere".
No more spheres are created. Why? Because the C.O.F.F.E.E. script found a
primitive named "My_Sphere", even if is not a real sphere, and, like instructed,
created nothing. We could check out if the object with that name is, in fact, a real
sphere but we can leave that for later.
Oh, you may have noticed that, like I said before, the newly created spheres
are faceted. That is because we only created a primitive, not any tags, namely
a Phong tag. When you click the primitive icons, CINEMA 4D fills out lots of
stuff for you, in advance. This is usually useful because, most of the time, the
defaults are what most people require. But, in C.O.F.F.E.E., you have complete
control in everything and you only create what you need, as it is needed.
I have been talking about all these objects and functions, but you may have
already wondered, where did all this information come from? Isnt there a book or
something about C.O.F.F.E.E. that explains all this? About this question, there is
good news and bad news. The good news is that there is, in fact, documentation.
The bad news are that it is not a book and it is written by technicians so, if you try
to learn C.O.F.F.E.E. from this documentation without some sort of support (like
the one on these pages), it would be like being blindfolded, taken to an airplane,
fly for a couple of hours and being dropped in a completely foreign country.
So, to finalize this chapter, I will tell you where to get that information. You
may download it and start browsing it. It will do you no harm... on the contrary
it will give you some insight on how the information is structured. In the next
chapter, I will teach you how to use that documentation to find out what you
need. Just for you to understand how important this documentation is, 90% of
what I know about C.O.F.F.E.E. came from it and I always keep it open when Im
coding any script or plug-in. I always keep it near, as it is invaluable.
To get it, go to www.
plugincafe.com and click
on the link that says SDK
Downloads R9.5
You should, now, download
the C.O.F.F.E.E. SDK 95. You
have the choice of downloading
the HTML version or the
Windows HTMLHelp version.
40
Choose the one you prefer. Oh, by the way, SDK stands for Software
Development Kit.
If you downloaded the HTML version, you simply have to drag the index.
html file to a browser (or double click it).
If you downloaded the Windows HTMLHelp version, then you are on your
own. I dont work with Windows (I would only do it if threatened by some sort of
weapon) and I dont know how to install the help files. But if you are a Windows
user, chances are that you do know how to do that. Any way you do it, you will
41
42
Chapter 5
This is already the fifth chapter about C.O.F.F.E.E. and I do hope that you all
are following it with interest. More than that, I hope you understand everything
thus far. In the last chapter I told you where to get support documentation. I hope
you already have the documentation in a place where it is easily accessible
because, in this chapter, we will learn how to use all that information and a few
tools to make your life easier.
Open the index.html file, inside the SDK 95 folder. You can use any browser
for that. When you click the Reference link in the main C.O.F.F.E.E. SDK page,
you are directed to a page that displays a list of topics. Below each topic you
have more specific subjects to click on. When you do, you are directed to a page
describing that particular item. On each page that describes an item, at the top
you have a short description of that item, followed by its definition. This is where
you know what type of item that is and what is his parent, if any. Remember
the hierarchical nature of objects? Objects can have parents, inheriting all their
characteristics.
For example, when you click the PointObject item, its definition starts
with something like this:
class PointObject : BaseObject
{
public:
PointObject();
43
[int] GetPointCount();
[vector] GetPoint([int] num);
[bool] SetPoint([int] num, [vector] p);
...
As you can see, at the top, you have a statement that reads class
PointObject : BaseObject
This means that the class named PointObject derives from the class
BaseObject.
If you click the BaseObject name (its in blue, so it is a link like on a regular
Internet page) you are directed to the page describing the BaseObject.
In it you can see that its definition starts with something like this:
[BaseObject] AllocObject([int] type);
class BaseObject : BaseList4D
{
public:
BaseObject();
...
Notice the class BaseObject : BaseList4D ? This tells you that the
BaseObject class derives from the BaseList4D class. You could go on
clicking the parents links all the way up until there are no parents left. This is an
easy way for you to understand all that is attached to any specific object.
Oh, I have been talking about classes, but what exactly is a class?
Well, a class is just another way to say object. A class defines all that
is inherent to an object: its parent, all its parameters, all its functions and
commands, etc. So, when I say class (the terminology used in C.O.F.F.E.E.
and most other programming languages), Im referring to objects... the stuff you
have been learning on the last two chapters.
Ok, now I will present to you a short description of what you can find inside
each topic showed in the Reference page.
Document classes
Inside this topic you can find all functions and commands that deal with your
document. Stuff like getting the current document, access to the objects inside
your document, insertion of new objects, access to the current tools, Undo/
Redo, access to the time/animation, etc.
44
Objects
With the functions and commands inside this topic you can manipulate the
objects inside your document. You can get or set their position, scale or rotation,
their name, their semaphore mode, etc. You also have access to some specific
characteristics of some special kinds of objects, namely polygonal (as in, nonparametric) objects or spline objects.
Tags
In here reside the functions and commands that allow us to create new tags
and read and write data into them.
Animation
Lots of functions and commands dealing with the timeline. Unfortunately,
it is still impossible to create new tracks or keys in C.O.F.F.E.E., but we can
manipulate the ones already there.
Materials
Want to create new materials or manipulate the ones that already exist?
This is the place to find all you need to do it.
Plugins
If you want to create new plug-ins and not just simple C.O.F.F.E.E.
expressions, this is where you can find all the necessary information about
doing so.
Shaders
Shaders are also plug-ins. But they appear at the materials list. In this topic
is all the information you may need to create your own shaders.
GUI
This is where all the commands for creating dialogs - the interface elements
that appear in windows - are explained.
Resources
The same as the previous subject, but this time using resources to define
the dialogs instead of C.O.F.F.E.E. commands. What are resources? Resources
are files that describe dialog elements. They have several advantages over
defining the dialogs with commands. First, they are independent of the code
itself so, if we want, we can edit the dialogs without ever touching the code. One
other advantage is that the text is separated from the rest of the description of
the dialog so, if we want to change any text its easier. Also, translating the text
to different languages is straightforward.
45
Utilities
Sundry commands and functions related to different subjects. They deal with
bitmaps, movies, time, containers (more about containers in the next chapter),
and selections of points and/or polygons, render parameters, etc.
Functions
A big collection of functions that deal with stuff like modeling commands,
textures (paths), events (an event is something that happens inside your
document like, for example, notifying that an object just changed or was
deleted), etc.
It also contains functions that return useful information, like which version
of CINEMA 4D the C.O.F.F.E.E. code is running, on what platform is the
C.O.F.F.E.E. code being executed, loading of documents, execution of external
applications, opening of external files, etc.
Files
If you need to access files this is where you can find all the commands and
functions to do so. You can, for example, rename files, delete files, check the file
type, open a file for reading, write in a file, get paths from a file, etc.
Math
This is usually the most feared subject.
But it really does contain a set of very useful mathematical functions.
Stuff like trigonometric functions, square roots, raising to powers, rounding,
generation of random numbers, conversions between degrees and radians,
conversions between color models, etc.
Casts
With these functions you can convert an integer number into a floating
point number and vice versa. You can also change numbers into characters,
for example.
Types
Like I said in previous chapters, variables can be of many types. They can
be floating point numbers, integer numbers, strings of characters, a vector (a
group of three floating points as in X,Y,Z or R,G,B), etc. This section shows the
available types.
Memory
Computers have memory, right? It is usually called RAM. Inside that
memory, huge amounts of numbers get stored, swapped, managed somehow.
With the functions inside this section you can manipulate chunks of data, using
a memory management metaphor.
46
String
A string of characters is a type of data that C.O.F.F.E.E. can deal with. Using
the functions and commands of this section you can manipulate those strings.
You can add them together, you can compare them, you can get a specific
character from somewhere inside them, etc.
Vector
Just like strings, vectors are another type of data that C.O.F.F.E.E. can deal
with. There are a few functions that deal specifically with vectors and this is
where you can find them.
Standard Functions
Half a dozen commands/functions that do miscellaneous things. For
example, the println() command that you already know very well can be
found here. Also, ways to know the size of variables or what their type is can
be found here.
Class
Like I said before, objects are called, in programming terms, classes.
So, objects and classes can be considered the same. Access to classes is
dealt with these functions.
Program Structure
Here you can find all the commands you could also find in regular JavaScript
or C++ scripts. These are not specific to C.O.F.F.E.E. and allow you to define
the workflow of your script. If you want to create conditional expressions or
cycles, for example, these are the commands to use. More on these at the end
of this chapter.
Declaration
When you need to create new variables, constants, data structures or
objects (known as classes as you know now), these are the commands to use.
Exception Handling
The commands in this topic are mostly useful when debugging - debugging
is the process of hunting down programming errors and solve them. It deserves
an entire lesson just for that. With these commands you can try out blocks of
code that could be prone to errors and you can intercept those errors to manage
them in any special way you want.
I know that this explanation is very sparse but it should give you an idea of
what you have in your hands. Almost everything you need to write C.O.F.F.E.E.
code is in there. Unfortunately, it is almost impossible to explain everything
47
that is in the SDK, in these chapters. But, as soon as you start to use the
SDK, everything will become easier and easier. Believe me, I know... the same
happened with me.
Now, to wrap this chapter, I would like to teach you all a few more commands,
this way this lesson will not be completely theoretical.
You usually want to use programming because you want to automatize
repetitive tasks. For example, you may want to go through all the points of
an object to check for something. For those tasks we use loops. Loops, in
programming language, are cycles that repeat a certain number of times. We
do have tools to create loops that we know, in advance, the number of iterations
or loops that will repeat until a certain condition is met. Let us get to know both.
If you already know, in advance, how many iterations you need to perform, the
best choice is a for loop.
How does it work? Well, the for sentence (its not a command, nor a
function... its simply a sentence) has the following syntax:
for(initialization; verification; update) code_to_execute;
Before explaining all the parameters of the for loop, I need to tell you that a
counter is required. For that, the for loop uses a simple variable. That means
that the variable used as a counter inside the for loop already needs to have
been declared. But you already know how to do that, using the var sentence.
The best way to explain the parameters of the for loop is to give you a real
life example:
main(doc,op)
{
var a;
for(a=0;a<10;a=a+1) println(a);
}
If you execute this script, in the console you will see that a column of numbers
will appear, like this:
0
1
2
3
4
5
6
48
7
8
9
What happened in the script? First, a variable was declared:
var a;
You can use any variable name, as long as you stick with the rules of variable
naming, presented in chapter 2.
After that, we create a for loop:
for(a=0;a<10;a=a+1)
I believe the sentences between the semicolons are quite obvious but I will
explain them anyway. First, the variable is initialized with a=0. This sets the
initial value of the variable. The next sentence checks if the cycle as reached
the end. In this case, it checks if the value inside the variable a is less than 10.
While this condition is true, the cycle will repeat itself. As soon as this sentence
becomes false, the cycle will end. Finally, the variable has to be updated
somehow; otherwise the verification of the end of the cycle will never become
false. So, in this case, we increase the value inside the a variable by one (using
a=a+1).
After the for sentence you place the code you want to get repeated. If you
have been paying attention to all the chapters, you may have noticed that the
for sentence lacks a semicolon in the end. That is because, like I said before,
the for is NOT a command. Only commands have semicolons at the end.
But, the command(s) after the for cycle DO HAVE to end with semicolons if
they are in fact commands. In our example, the command that is repeated is
println(a);
If you need to repeat more than one command, you need to enclose them
inside { and }, like this:
main(doc,op)
{
var a;
for(a=0;a<6;a=a+1)
{
println("The variable a holds the value:");
println(a);
println("-----");
}
}
49
is only necessary that you know how it works and what it is used for.
What if you dont know when you must finish a loop?
For that you can use a do...while loop. It works like this:
main(doc, op)
{
var a;
a=0;
do
{
println(a);
a=a+1;
} while (a<100);
}
Of course, in this example I know that I want to repeat the loop while a is still
less than 100. But imagine I would increase the variable a by an arbitrary value
like, for example, the Y coordinate of a set of objects. This way, I would not know
in advance when the loop condition would be met. So, the loop would repeat
until the sum of all Y coordinates of the objects would add to 100 or more.
The loop could also be written in a different way:
main(doc, op)
{
var a;
a=0;
while(a<100)
{
println(a);
a=a+1;
}
}
As you may have noticed, this form of creating a loop requires no do, just
the while. Also, it has a difference from the first version. Since the test for the
end of the loop is performed before the code that is to be executed, this code
may never be executed, if the test fails. In the first example, the code runs at
least once, and then the test is performed. This may be very important!! You
may want this behavior to help you out, so decide wisely when choosing the
required method. If you want your code to run, at least once, use the do {...}
while method. If you want your code to never get executed if the test fails, use
51
This script will search all objects inside a document. Well, not all... it will stop
as soon as it finds an object named "Light". If it does, it prints "Found a light." to
the console and breaks the loop, executing the code right after it. In this case, it
prints "End of search." because that is the code right after the loop. If the name
of the object is not "Light", it prints the name of the object followed by "is not
a light". Then it advances to the next object. This is VERY IMPORTANT. If we
dont advance to the next object, the script will go on checking the first object of
the document and it will never get out of the loop.
So, this loop can end for two reasons:
when it finds an object named "Light" (in that case, it breaks the loop)
when the obj variable ends up with the value nil (that is what the
while(obj) checks)
That will happen if there is no object in the document (very hard to happen
because you need, at least, one object in your document to hold the C.O.F.F.E.E.
tag that contains this script) or when the obj->GetNext(); function reaches the
end of the objects list.
Ok, this is it. Now you know how to create loops. Loops are VERY important
in programming, so get used to them.
52
Chapter 6
to mess up the position of these points. We could increase the Tolerance value
but that would not allow us to work with very dense models, as some points that
are not supposed to be welded would be merged together.
So, I decided to create a script that would allow us to model with any tools
we want (move, rotate, scale, magnet, brush, etc.) and still have complete
control without the fear of messing up our model too much. Besides, I do model
many times using this Symmetry technique and this is a script that is particularly
useful to me. I will not write a step-by-step tutorial about how to create the mesh
and how to assign the C.O.F.F.E.E. expression to it. I assume you already know
how to do that. Let us just say that you will have to end up with something like
this:
To type the C.O.F.F.E.E. expression you just need to have an object to attach
it to. It can really be any type of object because the C.O.F.F.E.E. expression will
be smart enough to understand if it is affecting a polygonal object, and if not,
will do nothing.
Create a Null and add to it a C.O.F.F.E.E. expression tag. Before typing
any code, we need to create some User Data in this tag. Start by adding a
Boolean User Data named On/Off. This will allow us to turn on and off the
expression. Yes, we could simply turn the Enable option on and off from the
Basic Proprieties tab of the C.O.F.F.E.E. tag. But adding this as a User Data will
place all options in a "central" place, making it easier to manipulate.
Now, add a new User Data of type Real, named Tolerance. You can set its
upper limit to whatever value you believe is good enough. Since this will be the
distance from the symmetry plane after which the points will not be considered,
you may want to set the upper limit to no more than 200, and this is already a big
value. You can set its interface to whatever you feel better with (I like sliders).
Now, add a new User Data of type Integer, named Axis. This one will include
a "trick" that will make this User Data field into a drop-down list. Before hitting
OK, type the following in the Options field:
0;YZ
1;XY
2;XZ
54
You need to type Enter after each line (except the last one, of course).
This will create a drop-down list with the items YZ, XY and XZ. When the
user chooses YZ, a value of 0 (zero) will be returned. When the user chooses
XY, a value of 1 will be returned. And when the user chooses XZ, a value of 2
will be returned.
You will end up with a User Data tab that looks something like this:
You can now press OK and type the following script in the C.O.F.F.E.E. tag
itself.
55
Wow!! This is a big one, isnt it? At least it is the biggest one in all C.O.F.F.E.E.
chapters, so far. Now we will explore each line of code to understand what it
does.
56
The first lines should be pretty clear right now (I will not even mention the
main(doc,op) sentence). In these lines I simply declare a few variables that
will be necessary inside the code. I will explain what each one will be used for as
we use them, inside the script. Since this is a C.O.F.F.E.E. lesson let me tell you
that I didnt know in advance how many and what kind of variables I would be
needing. So, as I was creating the script, and as I was needing more variables,
I was adding them at the beginning. Dont forget that programming is a dynamic
process and you will be adjusting your scripts (sometimes having to edit very
different parts of them) as you progress.
You may ask, why did I distribute the variables in three lines and didnt
use a single line to declare all of them? I could have done that but as a matter
of personal style (you will develop your own, dont worry), I like to, at the end
of writing the script, group the variable declaration by "theme" or what type of
action they will perform inside the script. In this case, the first line declares all
variables that deal with the list of points of the object. The second line declares
all (in this case, just one) variables used for programming structure, like cycle
counters or variables that hold temporary values. Finally, I declare all variables
that deal with the tag itself and all its values (this tag will include User Data).
After declaring the variables I start doing some checks.
First thing I check is if the object is selected. If it isnt, we are not editing it
(as in... modeling), so the script has nothing to do. How do we check if an object
is selected?
We check for some special bits of information. Actually, we do REALLY check
for bits, because that information is stored inside a few bytes of information that
contain lots of information in each one of their bits. For those that dont know
much about computer science, a byte is a storage unit. It can hold a number
between 0 and 255, or in binary, between 00000000 and 11111111. Why am I
talking about binary?!? Because its very important that you know that a byte
is made out of eight bits (like a meter is made out of 100 centimeters and a
centimeter is made out of 10 millimeters). When we need to store values that
can only have two possible states, it would be a waste of space (as in, a waste
of computer memory) to store such a value as a whole byte. To store these
true/false states, if we use bits, we can stuff eight states of information inside a
single byte. The selection state of an object is one of those things that can only
be true or false (an object cant be just "slightly" selected).
Each object has a set of bits of information and the selection state is stored
somewhere in there. Actually, the selection state is stored in bit number 2 but
Cinema 4D has that number stored inside a variable for easy recollection. It is
stored inside the variable BIT_AOBJ. Warning!! The case does matter!! BIT_
AOBJ is not the same as Bit_Aobj or bit_aobj.
Now that we know that, we only need to know what function returns the state
of a specific bit of an object. That function has the obvious name GetBit().
57
npoints=op->GetPointCount();
We must now check if the polygonal object is not empty. This could be
empty if we had just created a new Polygon object from the Objects menu.
Those polygon objects have no points and we must create some, before they
have any faces or edges. If there are no points, we leave...
if(!npoints) return;
Ok, now that we know that everything is fine with the object itself, we must
check the C.O.F.F.E.E. expression tag. Since the data that the C.O.F.F.E.E. tag
gives us is only the document (variable doc) and the object where it resides
(variable op), we must search for the C.O.F.F.E.E. expression tag itself.
So, we start by getting the first tag of the object with:
C.O.F.F.E.E._tag=op->GetFirstTag();
This sounds logical, doesnt it? Now the first tag of the object is stored in
the coffee_tag variable. If the object had no tags, the coffee_tag variable
would be holding a value of nil (meaning nothing). But this is very unlikely
because the object needs to have at least one tag: the C.O.F.F.E.E. expression
tag!! Since the C.O.F.F.E.E. expression tag may not be the first one, we must go
through all of them until we find what we need.
That is done with:
while(coffee_tag)
{
As long as there is a valid tag stored in the coffee_tag variable, execute
this loop...
if(instanceof(coffee_tag,CoffeeExpressionTag)) break;
As soon as the coffee_tag variable stores an instance of a C.O.F.F.E.E.
expression tag, we get out of the loop with the coffee_tag variable pointing
to the correct tag. Do you remember that everything inside CINEMA 4D is an
object? So, the C.O.F.F.E.E. expression tag in this object is just an instance of
the globally defined C.O.F.F.E.E. expression tag object. That is why we must
check for an instance with the function instanceof.
coffee_tag=coffee_tag->GetNext();
}
59
Since we are still inside the loop, we must get the next tag and repeat
the loop. As soon as we reach the last tag, the expression coffee_tag>GetNext() will return a result of nil. This is stored inside the coffee_tag
variable and when the loop check is made (with while(coffee_tag)), it will
fail. That will also get us out of the loop but this time with the coffee_tag variable
holding the value nil.
We now check to see if we found a C.O.F.F.E.E. expression tag, with:
if(!coffee_tag) return;
This is VERY UNLIKELY TO OCCUR, but we should make these checks
anyway, as it is good practice to check out everything... just in case.
I should point out that we should make another check here... imagine
that you have more than one C.O.F.F.E.E. expression tag assigned to the
object. This simple search loop stops as soon as it finds the first occurrence
of a C.O.F.F.E.E. expression tag. But nothing assures us that the first one is
the correct. We could now, for example, check out the name of the tag, and
assuming that we had named it a proper name, search for another one if this
was not the correct one.
But let us not complicate this further.
Now that we have a variable pointing to the C.O.F.F.E.E. expression tag, we
can start getting the User Data values. The first one to check is, of course, if
the expression is turned on or off. To get the values of User Data fields we must
type something like:
coffee_tag#ID_USERDATA:1
This will get us the value of the first User Data field. But there is an easier
way to "type" this, without typing it all. Just type the part that refers to the object
that points to the object that contains the User Data, like this (in our case):
if(! (coffee_tag
Now, make sure that the Attribute Manager is displaying the User Data tab
and drag the title of the User Data you want to use to the location you want to
place it, like this:
60
p_list[0].x
p_list[1].x
p_list[2].x
p_list[3].x
p_list[4].x
...
,
,
,
,
,
p_list[0].y
p_list[1].y
p_list[2].y
p_list[3].y
p_list[4].y
,
,
,
,
,
p_list[0].z
p_list[1].z
p_list[2].z
p_list[3].z
p_list[4].z
Until the very last elements (the number of points minus one, because like
I said, the arrays start at index zero).
Now that we have all the data that we need, we may start a cycle that goes
through all the points of the object. We do that with the help of a for loop, like
this:
for(f=0;f<npoints;f++)
{
Inside the for loop parameters, we initialize our counter variable (the f
variable) at zero. Then we define that it will repeat the loop as long as the
variable f is less than the number of points. Finally, for each iteraction of the
cycle, we increase the value of variable f. We do that with f++ because its
faster than typing f=f+1. Its one of those useful shortcuts that C.O.F.F.E.E.
allows us to use.
Now, inside the loop, we store the value of each point in the variable pt.
Why do we do that? Well, because we are going to make several checks on
many elements of each point and it is much easier to type pt.x or pt.y instead
of p_list[f].x or p_list[f].y. Also, the code will run faster because
each time we use an expression that deals with an array, C.O.F.F.E.E. has to
calculate where each specific element is located (multiply the index by the size
of the elements of the array and retrieve that value). Since we only do that once
when we read each point of the array into a single independent variable, all
subsequent calculations will run much faster.
Now I will present to you a new, very powerful, command: the switch.
With just one switch you can perform a huge set of conditional verifications,
replacing a lot of if commands. Also, the readability of your code will be
much clearer. At least a whole lot more so than if you had used lots of if
commands.
63
case value2:
stuff to do
break;
case value3:
stuff to do
break;
default:
this
In our case, we must perform different actions depending upon the axis we
choose in the User Data parameters. So, our switch structure looks like this:
switch(axis)
{
case 0:
if(abs(pt.x)<=tolerance) pt.x=0.0;
break;
case 1:
if(abs(pt.z)<=tolerance) pt.z=0.0;
break;
case 2:
if(abs(pt.y)<=tolerance) pt.y=0.0;
break;
}
What this does should be pretty obvious, but I will explain it anyway.
64
You can now close the Expression Editor window and test out your script. To
do this, create a Cube with, lets say, 4x4x4 subdivisions, make it editable and
delete half of it. Place your half-cube inside a Symmetry object. Depending on
which half you deleted, choose the correct Mirror Plane in the Symmetry object.
Now before dragging the C.O.F.F.E.E. Expression tag into your polygonal object,
make sure it is turned off and that the Axis value is the same as the Mirror Plane
parameter of the Symmetry object.
Also, make sure the Tolerance value is a very low value. It can even be
zero.
Why all these worries? Because as soon as you drag the C.O.F.F.E.E.
Expression tag to your polygonal object, it will adjust all the points that fall within
the Tolerance distance from the chosen Axis. You can always Undo, but its a
pain to see your object collapse even before you start editing it.
OK, now that all is correctly set, drag the C.O.F.F.E.E. Expression tag from
the Null to your polygonal object. You can now turn the expression on and adjust
the Tolerance.
I hope you have learned a lot more about C.O.F.F.E.E. with this practical
example, and at the same time, I hope you find this tool a useful addition for
your modeling sessions.
66
Chapter 7
When these chapters were being released, one each month in 3D Attack
The CINEMA 4D Magazine, I was asked to add an Undo function to the
script I presented in the last chapter. Well, I will not... not because I dont want
to, but because it is not possible. At least not with the current way things are
being done. Let me explain a bit further. This is a script that is executed as
an expression (it is inside a C.O.F.F.E.E. Expression, after all). All expressions
are executed automatically every time something in your document changes.
And what CINEMA 4D considers as a change can be a lot of different things:
changing a parameter (either by typing a value or adjusting a slider), dragging
an object around the editor (changing its position, size or rotation), selecting a
new object, a texture, a tag, a face, an edge, a point, etc., scrubbing the time
bar, etc... If any expression has the Camera Dependent option turned on, the
simple act of rotating the camera or zooming in or out will trigger the evaluation
of that expression.
So almost anything triggers a new evaluation of expressions. On my
machine, the script I presented in chapter 6 will be executed, roughly 20 to 30
times each second. So if I would include some code to store the current state of
the object every time the expression is evaluated, I would end up with 20 to 30
new clipboard states each second. If you set the Undo Depth in the CINEMA 4D
Preferences to, lets say, 30, the undo buffer would fill up pretty fast (in roughly
a second, to be more precise).
Some of you may ask if I could not store the current state of the object only if
fiddling with the parameters of the expression would cause a real change in the
67
state of the object (mainly, increasing the tolerance). Yes, I could, but for that I
would have to store the current state of all the points of the object and compare
that state with the potentially modified point list. That, besides making the script
a whole lot slower, is a bit beyond the scope of these lessons. I mean... it is a
bit too advanced for the level we are now. So, sorry folks... no undo with this
expression.
In this chapter I will present you with yet another utility expression. This
way we will learn C.O.F.F.E.E. at the same time as we increase our arsenal of
tools.
I will also make it in a way that is a bit different. I assume now that if you have
been following these lessons, you already have, at least, a less than superficial
knowledge of CINEMA 4D. So, I will not explain EVERY SINGLE STEP needed
to recreate this tutorial. We all need to learn the juicy stuff, and wasting time and
space with the little details is not very productive.
Before we begin, I must warn you that the C.O.F.F.E.E. code we will use will
only work for CINEMA 4D version 9.5 and up. If you still use an older version,
you can still learn from this lesson, of course, but you will not be able to make the
code function. So, enough with the yada-yada... on to the stuff that matters.
In an empty document, create a new instance. It will have a red cross on its
icon because it still has no object to reference. No problem. The C.O.F.F.E.E.
code will provide all that it needs.
Now, you need to create a lot of User Data fields for this Instance Object.
The fastest and easiest way is to choose Manage User Data... from the User
Data menu. You will be presented with a window where you can create all the
necessary User Data fields. Just follow the directions of the following table:
68
Oh, about the first User Data value, the maximum value should be adjusted
according to the needs of your scene. I defined it as 100000 units but this could
be changed, as you need. Just make sure you DONT SET THE MINIMUM TO
ZERO!! We will understand why later.
Now that you have all the required User Data attached to the Instance object,
add to it a C.O.F.F.E.E. tag.
Type the following code:
distance(p1,p2)
{
var x,y,z;
x=p2.x-p1.x;
y=p2.y-p1.y;
z=p2.z-p1.z;
return sqrt(x*x+y*y+z*z);
}
main(doc,op)
{
var max_dist;
var obj1;
var dist2,val2,obj2;
var dist3,val3,obj3;
var curr_obj,curr_cam,curr_dist,perc;
var matrix,mat1,mat2;
69
if(!instanceof(op,InstanceObject)) return;
max_dist=op#ID_USERDATA:1;
Distance
if(!max_dist) return;
//
the
user
data
named
Max.
70
("+tostring
if(perc>dist2) curr_obj=obj2;
if(perc>dist3) curr_obj=obj3;
op#INSTANCEOBJECT_LINK=curr_obj;
}
Ok, now lets dissect the code. I will not explain the parts that you should
already know. And yes, you should already know enough, if you have been
following these lessons.
The first part of the script is:
distance(p1,p2)
{
var x,y,z;
x=p2.x-p1.x;
y=p2.y-p1.y;
z=p2.z-p1.z;
return sqrt(x*x+y*y+z*z);
}
This is a sub-routine. This means that it is a kind of "sub-program" or a
routine that will be called elsewhere inside the main routine. You provide it with
a name (in this case, distance) and the parameters (in this case, p1 and p2).
Then, inside the { and } you define what the sub-routine does. Its almost as
if you defined a completely new command or function. So, if inside the main
routine you type something like distance(a,b), CINEMA 4D will call the
distance sub-routine while storing the value of the variable a inside the variable
p1 and the value of the variable b inside the variable p2. Then, it will perform
whatever actions it needs with those variables and returns a value, using the
return command.
Oh, why did I define this sub-routine before everything else? Because
CINEMA 4D needs to know how it is defined and what parameters it uses.
Otherwise, when you use it in the main routine it would not know if you provided
it with the correct parameters.
You could also define the sub-routines after the main routine, but to do
so without CINEMA 4D returning any errors you need to declare them all to
CINEMA 4D at the beginning. In our case, it would be something like this:
71
distance(p1,p2);
main(doc,op)
{
...
}
distance(p1,p2)
{
var x,y,z;
x=p2.x-p1.x;
y=p2.y-p1.y;
z=p2.z-p1.z;
return sqrt(x*x+y*y+z*z);
}
This allows for cleaner code, but if you define all your sub-routines at the
beginning (in which case you dont need declarations, just the sub-routines
themselves), the code will run slightly faster. Dont get me wrong... when I say
slightly it is really just slightly - just a few microseconds faster. But if your subroutine is called many, many times inside a big loop, that could be a bit more
relevant.
This sub-routine calculates the distance between two points (p1 and p2).
I will not explain the mathematical explanation of how to calculate that (just to
save time and space here), but you can get a good explanation here:
http://freespace.virgin.net/hugo.elias/routines/r_dist.htm
or
http://www.purplemath.com/modules/distform.htm
Now, on to the main routine. The first part is:
main(doc,op)
{
var max_dist;
var obj1;
var dist2,val2,obj2;
var dist3,val3,obj3;
var curr_obj,curr_cam,curr_dist,perc;
var matrix,mat1,mat2;
72
if(!instanceof(op,InstanceObject)) return;
max_dist=op#ID_USERDATA:1; // the user data named Max.
Distance
if(!max_dist) return;
dist2=op#ID_USERDATA:5; // the user data named Dist.2 (%)
dist3=op#ID_USERDATA:9; // the user data named Dist.3 (%)
You should already know what all this stuff does. Just notice that I added
a few comments to the code, they are preceded by the characters //. These
comments tell you what User Data parameters you should drag into the
Expression Editor, like you learned in the last lesson.
Did you know you could drag the User Data parameter names into the
Expression Editor window? Like this:
Why would you drag the User Data instead of typing it all?
Two reasons:
First because it is faster and less prone to errors.
Second, because if you didnt define all the User Data fields in the same
order as I did, the numbers after the ID_USERDATA: will not match and the
code will not work properly. That is why I provided the code with the comments
that tell what User Data parameters you should drag into the code.
73
So, we change that to just two with the parameter ".2f" that stands for "float
with just two decimal places".
Now we get all the possible objects that will be instantiated from the relevant
User Data fields, with:
obj1=op#ID_USERDATA:3; // the user data named Object 1
obj2=op#ID_USERDATA:7; // the user data named Object 2
obj3=op#ID_USERDATA:11; // the user data named Object 3
And now we get the current camera with:
curr_cam=doc->GetRenderBaseDraw()#BASEDRAW_DATA_CAMERA;
The BaseDraw is an internal structure of each document that holds lots of
data. Usually, the user doesnt care less about this, as CINEMA 4D deals with
it without us even noticing it. But we can access it to get useful information, like
in this case, the current camera.
So, we access the value by addressing the BaseDraw structure of the
document (doc->GetRenderBaseDraw()) and getting a specific parameter
from it (#BASEDRAW_DATA_CAMERA). This has the added benefit of not only
getting the current camera that is a real camera object, but also the virtual editor
camera. Usually you cant access that camera (as it is not a real object), but in
C.O.F.F.E.E. you can.
Now we must calculate the distance between the current camera and the
object (our Instance object). To do that, we must get the global position of both.
The global position is the position of an object in world coordinates. It is always
the same, no matter how deep inside a hierarchy it is. Its global position is
stored inside its global matrix - the explanation of what its matrix is, is beyond
the scope of this lesson, but you can read about it in the SDK.
So, we get the matrix of the Instance object with:
matrix=op->GetMg();
Then we get the position of the object that is stored inside the matrix with:
mat1=matrix->GetV0();
Now we get the matrix of the camera (we can use the same variable matrix
as we already have the position stored in another variable):
75
matrix=curr_cam->GetMg();
Then we get the position of the camera that is stored inside the matrix with:
mat2=matrix->GetV0();
Finally, we calculate the distance between them with the function we defined
at the beginning of our script:
curr_dist=distance(mat1,mat2);
If the distance is larger than the maximum distance allowed (the one we
define with the User Data), we make it equal to the maximum allowed.
if(curr_dist>max_dist) curr_dist=max_dist;
Now, we transform this distance into a number between 0 and 1 with:
perc=curr_dist/max_dist;
This is why we cant have a minimum value of the maximum distance equal
to zero. If, by any chance, we divide by zero, the script will stop with an error.
This is because dividing something by zero will lead to an infinity error.
As added information for the user, we now display the current distance
between the camera and the Instance object and what percentage that is in
relation to the maximum distance allowed. This way, it will be easier to adjust
the values of Dist. 2 and Dist. 3. This information is supplied with:
op#ID_USERDATA:13=tostring(curr_dist,".2f")+"
+tostring(perc*100.0,".1f")+"%)";
("
76
Now we store the first object (the one that is allocated to the instance when
the distance from the camera is less than Dist. 2) into the curr_obj variable:
curr_obj=obj1;
Now, if the current distance (mapped to a value between 0 and 1) is larger
than Dist. 2, we must change the object to the one defined by obj2.
if(perc>dist2) curr_obj=obj2;
But, if the current distance is even larger than Dist. 3, we must change to the
object stored in obj3.
if(perc>dist3) curr_obj=obj3;
Ok, we now have the correct object (depending on the distance) stored in
the curr_obj variable. It is now simply a matter of loading this object into the
Reference Object field of the Instance object. That is done with:
op#INSTANCEOBJECT_LINK=curr_obj;
I hope you found it all to be pretty logical, because... it is!!
Now, when you select your Instance object (that I named D-Instance, in a
smart moment of inspiration), you will have a list of User Data that should look
like this:
77
In the Max. Distance field you will input the maximum distance from which
the lowest resolution object will be displayed. This means that, at least from this
distance on, you know that the lowest resolution object will be used for sure.
In the Object 1 field you drag the object that has the most resolution. This is
the object that is displayed as long as the distance from the camera is less than
Dist. 2. As soon as the current distance reaches Dist. 2, the Object 1 is switched
to Object 2. Between Dist. 2 and Dist. 3, Object 2 is used. As soon as Dist. 3 is
reached (and from that distance on), Object 2 is switched to Object 3.
What is the best way to organize your scene? I usually use something like
this:
I create a Null for which I turn the display off for both the editor and render.
Inside it, I place the variable resolution objects that I will use inside the Instance
object. This way, they will not display in the editor or render because they inherit
the display attribute of the parent Null, but the objects themselves have their
display attributes turned on and they will display properly once instantiated by
the Instance object.
I really hope you have learned a bit more about C.O.F.F.E.E. in this chapter.
As you may have noticed, we are getting deeper and deeper into C.O.F.F.E.E.
78
Chapter 8
In this chapter, I have two surprises to you. The first one is that I feel we
are ready to start coding a full length plug-in. Yes, a plug-in that will reside
inside CINEMA 4D with its own icon and parameter window (interface). Since
it requires more than just a simple script like the ones we have been doing, I
decided that the best way is to split the explanations in two chapters. So, in this
chapter, we will learn how to create the interface. That is exactly my second
surprise... we will not learn any C.O.F.F.E.E. in this C.O.F.F.E.E. lesson. In the
next chapter, we will learn how to code the plug-in using the interface we will
create now.
To create an interface, you can use C.O.F.F.E.E. commands. Actually,
C.O.F.F.E.E. has a whole class of commands with the specific purpose of
creating user interface elements. The other possible way to create an interface
is to use resources. What exactly are resources? Well, they are external files
that define and describe what user interface elements are used and how they
should be displayed. Personally, 99% of the time, I use resources because they
are much more powerful than defining the user interface with commands. With
resources, since they are external to your code, you can adjust the interface
without ever touching a single line of your code. You can even adjust the
interface after the code has been compiled (changed into a non-editable, nonhumanly-readable format, suitable for distribution). Also, additional languages
can be associated to the interface dialog. So if you want, the same interface can
show up in several different languages, depending on the main language set by
the CINEMA 4D application.
79
Interfaces defined with C.O.F.F.E.E. commands are "set in stone" and cant
be changed. The only real advantage I can see in using C.O.F.F.E.E. to define
an interface is that you can create "dynamic" interfaces that can have different
elements depending on some specific conditions. Besides that (not a very
usual situation), I strongly recommend using resources to define your plug-ins
interface.
So, how do you create interface resources? Easy - you use ResEdit (the
name is quite self-explanatory, isnt it?)
You can get it from:
http://www.maxon.net/pages/support/plugincafe_downloads_e.html
Search for Resource Editor, or get it directly from:
ftp://ftp.maxon.net/pub/sdk/91/resedit17.zip
...and you install it just like any other CINEMA 4D plug-in (inside the plugins
folder). It includes several tools, as you can see from the drop-down menu of
ResEdit, but to create interfaces, you just need the Resource Editor.
Choose it from the ResEdit drop-down menu and you will be presented
with...
80
Wow!! What a mess! To me, this is a bug but it has always behaved like this,
so following Microsoft philosophy, its not a bug, its a "feature" ;-)
Luckily, this "feature" it easily fixable. Just close one of the windows that
appeared.
And now choose "Execute Last Plugin" from your Plugins menu.
Now the toolbox window (that is unmovable in MacOS) is out of the way and
you can rearrange all other windows to your taste.
81
will be the name of our plug-in) and inside it, create the following hierarchy:
OK, back to CINEMA 4D where you should have all your Resource Editor
windows opened. The first thing you should do is to change the name of your
main dialog into something related to your plug-in. So, instead of the name
IDD_DIALOG1, change it to IDD_INBETWEENER, like this:
Now, press the Check Box item from the Toolbox window three times.
83
You should now have a Structure window and Dialog window looking like
this:
Select the first Check Box item in the Structure window (the first one below
the Dialog item).
In the Properties window, you should read an Element ID with the name
IDC_CHECK1 and a Name of Check Box. Change the Element ID to POS and
the name to Position.
Now select the second Check Box item in the Structure window. Change
the Element ID to SCL and the name to Scale.
Finally select the third Check Box item in the Structure window. Change
the Element ID to ROT and the name to Rotation.
Now hit the Update Preview button in the Properties window and you
should end up with a Structure window and Dialog window looking like this:
84
Now its a good time to save your work. Choose Save as... from the File
menu of the Structure window. In the save dialog, navigate to the InBetweener/
res/dialogs/ folder and save.
Now, a second dialog will appear asking you to locate your resources path.
Simply select the main folder (named InBetweener, in our case) and hit Save.
To check if everything is ok, close any of the Resource Editor windows. It
will close all of them. Now choose Resource Editor again, from the Plug-ins
menu. All windows should appear in the position they were before you closed
them.
Now, from the File menu of the Structure window, choose Open. Navigate
to InBetweener/res/dialogs/ and open the IDD_INBETWEENER.res file. Your
interface should reappear with all the check boxes and the buttons.
But the buttons should appear side by side, not one on top of the other. By
default, all items appear in a new "line", so to place items side by side, we
need to create a Group. Click the
item from the Toolbox window. A new
85
item named Group appears in the Structure window, but apparently nothing
has changed in the Dialog window. Groups have no visual feedback, by
default. Actually, their main purpose is to serve as structural helpers. So, drag
the Nevermind item and the Oh yeah! item into the Group item, like this:
You do this just like you create hierarchies in a CINEMA 4D scene, in the
Object manager. Nothing changed in the Dialog window... yet! Select the
Group item and in the Properties window, set the number of Columns to 2.
Ah!!! Now the Dialog window shows the two buttons side by side.
We could end the dialog definition right now, but I want to make it look a
little better. So, create another Group and drag it (in the Structure window) all
the way to the top so that it appears below the Dialog item and on top of the
Position item. Drag all Check Box items inside this Group.
With the Group item selected in the Properties window, set its name to
InBetween and the Border type to Thin in (its set at None, by default).
You now have all the Check Boxes inside a nice little rectangle with a title
named InBetween. But its all the way to the left, leaving lots of empty space at
the right. Ugly!! What would be nice was if it would fill the width of the window,
no matter what size it was, right? So, set its Horizontal Alignment to Scale Fit.
86
Now while you are at it, select the other Group and set its Horizontal
Alignment to Scale Fit, too. Also, set the Horizontal Alignment of the two
buttons to Scale Fit.
Only one thing is bothering me now - the limits of the graphic elements of
the interface are touching the limits of the window. It would all look much better
if there was some space around them, wouldnt it?
So, create another Group and drag it (in the Structure window) all the way
to the top so that it appears below the Dialog item and on top of the InBetween
item. Drag the other two Groups into it, so that you now have a Structure
window content looking like this:
87
Now, select the new Group item and in the Properties window, set its
Border space parameters to:
I dont usually increase the Top space because it already looks fine. But all
others need some adjustments and I usually set them to 4.
Choose Save from the File menu of the Structure window. Close the
Resource Editor (closing any of its windows) and choose Execute Last Plugin
from the Plugins menu. The Resource Editor window should appear again
(this time, with no interface showing, of course). To test out the new dialog,
resize the Dialog window to the smallest size it allows. Now choose Open
from the File menu of the Structure window and navigate to InBetweener/
res/dialogs/ and open the IDD_INBETWEENER.res file. Your newly defined
interface should look like this:
Well, if you are in Windows or simply using a different scheme, the look will
be slightly different. But what matters is that you just finished creating your very
first dialog resource. Keep it in a safe place (the InBetweener folder) and we
will start coding our plug-in in the next chapter.
Dont bother with the Element ID names we chose in this lesson. They will
become clear when we start coding. Also, if you open the files that ResEdit
created inside the folders you created with a text editor, you will realize that the
dialogs are defined with a kind of programming language. The dialog resource
(the IDD_INBETWEENER.res file) looks like this:
// C4D-DialogResource
DIALOG IDD_INBETWEENER
{
NAME IDS_DIALOG; CENTER_V; CENTER_H;
GROUP IDC_STATIC
88
}
GROUP IDC_STATIC
{
NAME IDS_STATIC; ALIGN_TOP; SCALE_H;
BORDERSTYLE BORDER_NONE; BORDERSIZE 0, 0, 0, 0;
COLUMNS 2;
BUTTON B_CANCEL { NAME IDS_BUTTON; ALIGN_TOP; SCALE_H;
And the file that stores the names of your items (the IDD_INBETWEENER.
str file, inside InBetweener/strings_us/dialogs/) looks like this:
// C4D-DialogResource
DIALOGSTRINGS IDD_INBETWEENER
{
IDS_BUTTON
"Nevermind";
IDS_BUTTON1 "Oh yeah!";
IDS_CHECK
"Position";
IDS_CHECK1
"Scale";
IDS_CHECK2
"Rotation";
IDS_DIALOG
"Dialog";
IDS_STATIC
"Group";
IDS_STATIC1 "InBetween";
IDS_STATIC2 "Group";
}
89
This means that if you want (and know what you are doing), you can even
use a text editor to create or modify an interface of a plug-in. That is the simplicity
and beauty of resources.
Well, thats all for now. Explore the Resource Editor items and options.
I assure you... its lots of fun.
90
Chapter 9
Now that we are approaching the end of this C.O.F.F.E.E. series, Ive decided
to play some mind games on you. Remember I told you that I would teach you
all how to program a full plug-in in this chapter? Well... I wont. But I will...OK,
let me explain.
I will present you with the listing of the plug-in but I will only explain it, line
by line, in the next chapter. This way I will give you time to go through the code
and try to understand it... or get very scared. I assure you, there is no reason
to get frightened by the code. It may seem complex at first, but most of the stuff
in there you only need to understand once because it will be almost the same
for all plug-ins you may be creating. So most of the coding from now on will be
a matter of Copy/Paste, with only a part of the code being specific to your plugins. The rest of the code (the stuff that may scare you at first) is just for structural
purposes, being absolutely fundamental for the definition of the plug-in itself.
You can create several kinds of plug-ins. For example, you can create menu
plug-ins that are plug-ins that you can pick from the Plugins menu. These usually
have a dialog and do their mumbo-jumbo when you press the OK button. You
can also create shader plug-ins. These will, of course, create new shaders that
appear in the shaders list in the Material Manager. You can also create Tag
plug-ins and many other types of plug-ins. You can check out what possibilities
you have by clicking the Plugin types link, in the SDK.
The ones that are most useful (and easier to create) are Menu and Tag plug91
ins. And our first plug-in will be one of those, namely a Tag plug-in.
There are two types of Tag plug-ins: plug-in tags and Expression plug-in
tags.
The first one has no dialog and is usually used to add custom data to your
objects. For example, the Anchor tag is one such tag plug-in.
The second one is the one that interests us
more. Most tags that you know have some type
of parameters to adjust and perform some action
on the objects they are attached to, depending on
those parameters.
Another nice characteristic of plug-in tags or
Expression plug-in tags is that they are executed
each time something in your scene changes, just
like the C.O.F.F.E.E. expressions you have been
using so far. So, we will be creating an Expression
plug-in tag!
First of all, make sure you have the hierarchy
presented on the right, somewhere on your
disk (you should have it, if you followed the last
chapter).
92
Inside the res/dialogs folder, there must exist a file named IDD_
INBETWEENER.res, whose content is:
// C4D-DialogResource
DIALOG IDD_INBETWEENER
{
NAME IDS_DIALOG; CENTER_V; CENTER_H;
GROUP IDC_STATIC
{
NAME IDS_STATIC2; ALIGN_TOP; ALIGN_LEFT;
BORDERSIZE 4, 0, 4, 4;
COLUMNS 1;
SPACE 4, 4;
4;
GROUP IDC_STATIC
{
NAME IDS_STATIC1; ALIGN_TOP; SCALE_H;
BORDERSTYLE BORDER_THIN_IN; BORDERSIZE 4, 4, 4,
COLUMNS 1;
SPACE 4, 4;
LEFT;
LEFT;
LEFT;
93
You can also create this file using any text editor, if you didnt use ResEdit.
Also, inside the res folder itself, there must exist a file named c4d_symbols.h
whose content is:
//*****************************************************
****************\
// File name
: c4d_symbols.h
// Description
: symbol definition file for the
plugin
// Created at
:
// Created by
: Resource editor
// Modified by
:
//*****************************************************
****************/
// WARNING : Only edit this file, if you exactly know
what you are doing.
// WARNING : The comments are important, too.
enum
{
_FIRST_ELEMENT_
= 10000,
// Dialog definitions of IDD_INBETWEENER start here
IDD_INBETWEENER,
POS,
SCL,
ROT,
B_CANCEL,
B_OK,
// Dialog definitions of IDD_INBETWEENER end here
// End of symbol definition
_DUMMY_ELEMENT_
};
You can also create this file using any text editor, if you didnt use ResEdit.
Inside the strings_us/dialogs folder, you must have a file named IDD_
INBETWEENER.str, whose content is:
// C4D-DialogResource
DIALOGSTRINGS IDD_INBETWEENER
{
94
IDS_BUTTON
IDS_BUTTON1
IDS_CHECK1
IDS_CHECK2
IDS_CHECK3
IDS_DIALOG
IDS_STATIC1
IDS_STATIC2
"Nevermind";
"Oh yeah!";
"Position";
"Scale";
"Rotation";
"Dialog";
"InBetween";
"Group";
I know this is getting a little boring but you can also create this file using any
text editor, if you didnt use ResEdit. But its more fun to create all these files
using ResEdit, like I showed in the last chapter.
Now, inside the res folder you must also place the icon of our plug-in. Since
it will be a tag plug-in, this icon will be the one that will appear in the Object
Manager when you add the tag to any object. You have a few restrictions when
creating this icon, though. It must have a size of 24 by 24 pixels (because it
will be the icon for a tag, otherwise, it has to be 32 by 32 pixels) and it must be
saved in a format that CINEMA 4D can read. Usually, if you save in TIFF it will
be just fine. Just make sure it only has the usual three channels, R, G and B,
and no additional alpha channels. Also, save it without compression. If you are
using a Mac, you could save it with compression because Quicktime deals with
it but if you want to make sure your plug-in works fine on Windows too, save
as a non-compressed TIFF because you cant be sure if Quicktime is installed
in Windows. Actually, having Quicktime installed will assure that CINEMA 4D
will be able to read a whole lot more formats than it natively can. So, Windows
users... hint, hint. ;-) Also, if you want the tag icon to seamlessly integrate with
the background of the Object Manager, make sure its background is set to RGB:
153,153,153.
95
You can use whatever application you prefer to create the icon.
The one I provide in the Goodies folder was created in CINEMA 4D and
edited in Photoshop. But nothing stops you from using Paint Shop, Paint,
FreeHand, Illustrator, Corel, etc. So, create the icon and save it inside the res
folder with the name inbetweener.tif
Now you should have this hierarchy:
Its now time to write our plug-in.
Use your favorite text editor to type
the code. Attention!! I said text editor,
not a word processor. If you use a
word processor, like Word, make sure
you save your file as a plain text file,
with no formatting. On a Windows
machine, you can use NotePad or
WordPad which come natively with it.
As a nice freeware solution, you
can use EditPlus (perform a Google
search for it). There may be many more
freeware text editors for Windows,
but since I dont use Windows often
(actually, I try to keep away from it as
much as I can), I cant really tell you
about more options.
If you are on a Mac, you can use TextEdit that comes natively with the
system (dont forget that you must save as a plain text document).
But there are many more options. For example, you can use mi that you can
download from http://www.asahi-net.or.jp/~gf6d-kmym/en/.
Another option is iText, from http://members.aol.com/iText/. Another great
option is TextWrangler, from http://www.barebones.com and yet another is
SubEthaEdit, from http://www.codingmonkeys.de/subethaedit/.
Anyway, always make sure you save your file as plain text. Also, make sure
the extension is .cof and not .txt
On a Mac, to make sure the extension ends up being just .cof, select your
file and press Command+I from the Finder and check for the name in the
Name&Extension section of the Info window. If you simply add .cof to the end
of your file, you may end up with a file named inbetween.txt.cof.
When you have finished typing the code and saved it as inbetween.cof, place
the file inside your inbetween folder, right next to the res and string_us folders.
96
Place the inbetween folder inside the plugins folder that resides inside
the Cinema4D folder. Now run CINEMA 4D. When it finishes loading, open
the Console window to check for errors. If it all runs fine, you should see,
somewhere in the Console window, the sentence:
Inbetweener v1.0 was successfully loaded.
If not, quit CINEMA 4D, open inbetween.cof in your text editor, and check
it against the listing presented here. The line number of the error should be
displayed in the Console window, so it is easy to hunt for the offending bug.
Without any further delays, here is the code of our plug-in:
include "c4d_symbols.h"
var PLUGIN_ID = 1020857;
var inbetweener_version = "1.0";
var
var
var
var
var
var
icon_map;
dialog_res;
owner;
doc;
inbetweenerTag;
cControls;
GetActiveTagOwner(op)
{
while (op)
{
var active_tag=GetActiveTag(op);
if (active_tag) return (active_tag);
active_tag=GetActiveTagOwner(op->GetDown());
if (active_tag) return active_tag;
op=op->GetNext();
}
return NULL;
}
GetTheActiveTag(doc)
{
return GetActiveTagOwner(doc->GetFirstObject());
}
GetOwner()
{
doc = GetActiveDocument();
if (!doc) return NULL;
var active_tag=GetTheActiveTag(doc);
if (active_tag)
{
var ob=active_tag->GetObject();
return ob;
97
else
}
{
return NULL;
}
case B_OK:
SaveControls();
Close();
GeEventAdd(DOCUMENT_CHANGED);
break;
default:
98
}
return false;
}
inbetweener_gui::LoadControls()
{
var ret;
ret = SetItem(POS, cControls->GetInt(POS, false));
ret = SetItem(SCL, cControls->GetInt(SCL, false));
ret = SetItem(ROT, cControls->GetInt(ROT, false));
}
inbetweener_gui::SaveControls()
{
cControls->SetData(POS, GetItem(POS));
cControls->SetData(SCL, GetItem(SCL));
cControls->SetData(ROT, GetItem(ROT));
inbetweenerTag->SetContainer(cControls);
}
class inbetweener_plug : ExpressionPluginTag
{
public:
inbetweener_plug();
}
GetID();
GetName();
GetHelpText();
GetIcon();
UseMenu();
MultipleAllowed();
Edit();
Save(hf);
Load(hf);
Execute(doc,op);
inbetweener_plug::inbetweener_plug()
{
var bc;
super();
bc=GetContainer();
bc->SetData(POS,false);
bc->SetData(SCL,false);
bc->SetData(ROT,false);
SetContainer(bc);
}
inbetweener_plug::Save(hf)
{
var bc=GetContainer();
hf->WriteContainer(bc);
99
}
inbetweener_plug::Load(hf)
{
var bc=hf->ReadContainer();
SetContainer(bc);
}
inbetweener_plug::GetID() {return PLUGIN_ID;}
inbetweener_plug::GetName() {return "Inbetweener";}
inbetweener_plug::GetHelpText() {return "Interpolates Position,
Scale and/or Rotation of the children of the object it is applied
to.";}
inbetweener_plug::GetIcon() {return icon_map;}
inbetweener_plug::UseMenu() {return true;}
inbetweener_plug::MultipleAllowed() {return false;}
inbetweener_plug::Edit()
{
owner = GetOwner();
var dlg = new(inbetweener_gui);
dlg->Open(true, -1, -1);
return true;
}
inbetweener_plug::Execute(doc,op)
{
var bc,pos_var,scl_var,rot_var;
var pos1,pos2;
var scl1,scl2;
var rot1,rot2;
var first_obj,last_obj,num_obj,step,f;
op=op->GetDown();
first_obj=op;
num_obj=0;
while(op)
{
num_obj++;
last_obj=op;
op=op->GetNext();
}
if(num_obj<3) return;
num_obj--;
100
step=1.0/num_obj;
pos1=first_obj->GetPosition();
scl1=first_obj->GetScale();
rot1=first_obj->GetRotation();
pos2=last_obj->GetPosition();
scl2=last_obj->GetScale();
rot2=last_obj->GetRotation();
first_obj=first_obj->GetNext();
bc=GetContainer();
pos_var=bc->GetData(POS);
scl_var=bc->GetData(SCL);
rot_var=bc->GetData(ROT);
for(f=1;f<num_obj;f++)
{
if(pos_var) first_obj->SetPosition(Mix(pos1,pos2,(f*
step)));
if(scl_var) first_obj->SetScale(Mix(scl1,scl2,(f*step))
);
if(rot_var) first_obj->SetRotation(Mix(rot1,rot2,(f*
step)));
first_obj=first_obj->GetNext();
}
}
main()
{
icon_map = new(BaseBitmap,24,24);
Register(inbetweener_plug);
println(" Inbetweener v"+inbetweener_version+" was
successfully loaded.");
}
You can use whatever object you want but just for testing, make them all the
same. Pyramids or Cones are great for a simple test.
Now, add an Inbetweener tag to the parent Null.
Select whatever options you want. For example, Position and Rotation.
Press "Oh, Yeah!" and then move the first or last child of the Null. All the objects
in between will interpolate the selected properties. A word of warning, though!
If you select Scale, you must use the Object tool to scale the objects, not the
Model tool. So, have fun with your new tool and... until the next chapter.
102
Chapter 10
We finally reach chapter number 10 and that means we are also reaching
the end of the C.O.F.F.E.E. series. Before wrapping things up, like promised, I
will explain what each line of code of the plug-in I presented in the last chapter
does.
I hope none of you had any trouble making the plug-in work, and above
all, I truly hope that you understood most of the actual code that does the dirty
work inside the plug-in. I assume that the stuff you didnt understand was the
"boring" stuff that actually defines the structure of the plug-in. Luckily, once you
learn what that "boring" stuff is (and what it does), you can re-use it on all your
subsequent plug-ins. You will only need to re-adjust the variables and a few
names, and from now on, most of the plug-ins you may do will mainly be a
matter of Copy/Paste. Of course, the structure I present here is for Tag plug-ins,
so this structure will only be able to produce that kind of plug-ins. But worry not!!
Before we end these lessons, I will present you with another type of plug-in:
the menu plug-in. These are the ones that are evoked from the Plugins menu
and perform some action on your document. Fortunately, the menu plug-ins are
almost a carbon copy of the Tag plug-ins as you will see later. Ok, enough with
the small talk... on to the explanations.
The first line of the code is:
include "c4d_symbols.h"
This will search for a file named "c4d_symbols.h" (without the quotes, of
course) that should reside inside the res folder that is inside your plug-in folder.
If it doesnt find that file, the plug-in will not load, presenting you with an error
103
in the Console instead. But we are very professional and we DO have a file
named "c4d_symbols.h" inside our res folder, so that file is included in the
beginning of our code. What exactly does it mean, that the file is include(d)?
Well, it is as if the line...
include "c4d_symbols.h"
... was replaced with the content of the file it refers to. In this way, the code
is clean and more flexible. If you need to change anything inside the "c4d_
symbols.h" file, you will not need to touch the plug-in code itself. And you can even
change it after your code is compiled (more about that in the next chapters).
And what exactly is inside the "c4d_symbols.h" file? The file contains this
information:
//*****************************************************
****************\
// File name
: c4d_symbols.h
// Description
: symbol definition file for the
plugin
// Created at
:
// Created by
: Resource editor
// Modified by
:
//*****************************************************
****************/
// WARNING : Only edit this file, if you exactly know
what you are doing.
// WARNING : The comments are important, too.
enum
{
_FIRST_ELEMENT_
= 10000,
// Dialog definitions of IDD_INBETWEENER start here
IDD_INBETWEENER,
POS,
SCL,
ROT,
B_CANCEL,
B_OK,
// Dialog definitions of IDD_INBETWEENER end here
// End of symbol definition
_DUMMY_ELEMENT_
};
The stuff that is important to us is what is between the { and } symbols that
follow the enum statement (the rest are comments, even if they are important
too, to the structure of the file).
104
The statement enum serves to enumerate a list of values. You only need to
explicitly assign a numerical value to the first element of the list. All others will
be sequentially numbered. So, the first element of the list is named _FIRST_
ELEMENT_ (yes, its a dummy name) and is numbered 10000. After that, all other
elements get a sequential number, so IDD_INBETWEENER will be numbered
10001, POS will be numbered 10002, SCL will be numbered 10003, and so on.
Finally, the last element, with the suitable name of _DUMMY_ELEMENT_, finishes
the list.
This list names the elements of the graphical user interface of the window of
your plug-in. This way, you will be able to refer to them by names, instead of by
numbers. Easier, isnt it?
So, after including the list of GUI elements, we start defining variables:
var PLUGIN_ID = 1020857;
var inbetweener_version = "1.0";
var
var
var
var
var
var
icon_map;
dialog_res;
owner;
doc;
inbetweenerTag;
cControls;
you decide to develop your plug-in further and create more advanced versions.
After that, six additional variables are defined. I will explain them as they are
used, later in the code.
Now I define a procedure. This procedure returns the active tag of an object
that is passed into it. If it has none, it will search inside its children. If there is no
active tag in any of its children, it will go on searching until it reaches the end
of the objects list of your document. This procedure is a recursive procedure.
Wow!! A new word!! Recursive! This means that the procedure calls itself to
perform its task. You may wonder how it is that we dont end up inside an infinite
loop, if the code calls itself. Well, all recursive procedures must have a condition
that, if met, will terminate them. I will explain how that happens, using comments
on the code (the stuff in green, after the // symbols at each line of code):
GetActiveTagOwner(op)
{
while (op) // as long as we have a valid pointer to
an object (it will not be when the end of the document or
the end of a "family")
{
var active_tag=GetActiveTag(op); // we get a
variable pointing to the active tag of the object
if (active_tag) return (active_tag); // if there
is any, we can return it. Finished!!
active_tag=GetActiveTagOwner(op->GetDown()); //
if we dont, we call the procedure again, but this time
pointing to the child of our current object and store the
returned value inside the same variable as before.
if (active_tag) return active_tag; // if there
is any active tag, we can return it. Finished!!
op=op->GetNext(); // still no active tag found.
We move to the next object of the list
} // this will return to the beginning of the
while loop
return NULL; // no active tags were found. Return
NULL
}
There is one thing you must know about recursive procedures, if you havent
asked yourself this already. How do they know if they need to return to the
instruction after the call to themselves, or if they need to return to the instruction
after the place where they were called in the main code? To understand that,
I need to introduce a new concept: the stack. The stack is exactly what its
106
had in previous lessons about objects (as in... object oriented programming)?
Objects have data and procedures attached and can inherit characteristics from
the objects that generated them, can delegate characteristics to objects that are
generated from them, and so on, and so on... Ok, its time for us to create a new
class of objects. This new class will be generated from the class GeDialog
that deals with... guess what... Dialogs. Obvious, isnt it? Since this new class
will deal with the dialog of our plug-in, we will name it something relevant, like...
inbetweener_gui.
We start by defining it, based on the GeDialog class and, inside it we define
a few public procedures.
class inbetweener_gui : GeDialog
{
public:
inbetweener_gui();
CreateLayout();
Init();
Command(id,msg);
LoadControls();
SaveControls();
}
By public, we mean that they can be accessed from anywhere and their data
is also available everywhere. They could also be private and, in that case, they
would only be available inside the scope of the inbetweener_gui class. They
could also be protected and in that case they could not even be modified... but
1
that is not important here, really. We only need public stuff, at this point .
So, we declare the constructor function, named just like the class that
contains it. The constructor is a special function that creates a new instance
of the class. We also declare a few additional procedures (CreateLayout,
Init, Command, etc.).
Actually, the GeDialog class already defines all of these procedures but
they are generic. We want them to deal with our plug-in stuff, specifically!! So,
we overload them. What does that mean? Overloading a procedure means that
we are defining a new version of it, replacing the one that is already defined.
This means that, for example, if we hadnt overloaded the CreateLayout
procedure, there would already be one defined, but it would only open an
empty window (thats its default behavior). Since our plug-in has a dialog, with
graphical user interface elements in it, we need to create a new CreateLayout
procedure that loads our dialog resource and displays it on the window. And that
is exactly what we will do, in a while.
1
See the Editors Note in the Appendix on page 139 for a thorough explanation.
108
So, in a nutshell, we created a new class for our plug-in dialog that is based
on the GeDialog class (internally defined by CINEMA 4D). This new class
has all the characteristics of the GeDialog class, but since we need some of
its characteristics to be specific to our plug-in, we overload (overwrite) some
of those characteristics. The ones that can be left alone, we dont even bother
with them.
Now that we declared the procedures we want to overload, we need to
define what exactly they do.
inbetweener_gui::inbetweener_gui() {super(PLUGIN_ID);}
This one was simple. It simply calls its super (the original procedure,
defined by its parent class, GeDialog) with the parameter it requires: the plugin ID number.
The next one, since it is longer, is commented.
inbetweener_gui::CreateLayout()
{
var dialog_path = GeGetRootFilename(); // we get the
path to the plug-in that is running
if (!dialog_path) return false; // if something went
wrong, return
dialog_path->RemoveLast(); // remove the last part of
the path because the path includes the name of the plugin itself
dialog_res = new(GeResource, dialog_path); // we get
the resource from the res folder and create a new class
from it
var ret = LoadDialogResource(IDD_INBETWEENER,dialog_
res,BFH_SCALEFIT | BFV_SCALEFIT); // the resource we need
is the dialog resource and that is what we get. Read more
below...
SetTitle(" Inbetweener v"+inbetweener_version); // we
now set the title of our plug-in preview, adding it the
version number
return ret; // return the dialog return
}
When we load the dialog resource, we use the name of the resource file
(without the .res extension, of course). We load the resource file into the
dialog_res variable (one of the variables that were defined at the beginning
109
of the code). Also, we set the dialog to fit horizontally and vertically into the
available space of the window. You can find a list of these parameters in the
SDK, in the GeUserDialog session.
Now we define the initialization procedure:
inbetweener_gui::Init()
{
inbetweenerTag = GetActiveTag(owner); // get the
active tag of the current object. See below how to we
define the "owner"
cControls = inbetweenerTag->GetContainer(); // we get
the container of the tag and store it in the cControls
variable. More about this below...
LoadControls(); // The controls of the dialog are
loaded
return true; // return true. This means that the
initialization was successful
}
Its now time to introduce to you all of the concepts of a container. A
container, as its name implies, contains something. Usually, it contains data,
lots of data, a lot of different types of data. Containers are very important in
CINEMA 4D because almost everything in CINEMA 4D contains a container
(no pun intended).
For example, when you create a sphere, the sphere object has a container
that holds its Radius, the number of segments, what type of sphere it is and if it
has to render as a perfect sphere or not.
As an example, try the following... oh, by the way, this has nothing to do with
the plug-in. Its just an example for you to better understand what a container
is (it will be displayed in red, so that you dont confuse it with the plug-in code,
in blue).
So, in an empty document, create a sphere. Now add to it a new C.O.F.F.E.E.
expression tag and type the following:
main(doc,op)
{
var cnt;
cnt=op->GetContainer();
println(cnt->GetData(PRIM_SPHERE_SUB));
}
110
If you modify the number of segments of the sphere, you will get a read out
of the current number of segments, in the Console window.
I used the function GetData since this function can retrieve any type of
data, but if I wanted to force a specific type of data, I could use other types
of functions. For example, if I wanted to get the value of the segments as a
floating-point number, I could use GetFloat instead of GetData. But GetData
is much more versatile.
Of course, if you try to read a type of data using a function that relates to a
completely different type of data, you will get nothing, or even worse, useless
data. For example, if I had used GetString to get the number of segments,
I would get nothing.
So, to get data out of the container you use an expression like:
container_variable->GetData(name_of_parameter)
You may ask how I knew that the segments parameter was named PRIM_
SPHERE_SUB. I confess that I didnt know. Well, at least I didnt remember when
I typed the code, but its quite easy to know once I typed...
println(cnt->GetData(
... I pointed the cursor at the Segments parameter in the Attributes Manager
(the actual name Segments, not the numeric field in front of it) and dragged it
in front of the ( symbol. CINEMA 4D typed the name of the parameter for me,
like this:
println(cnt->GetData(#PRIM_SPHERE_SUB
I just had to delete the # symbol and finish typing the )); symbols.
Besides reading data out of a container, you can also write data into it, as
you will see below.
Now, inside the Command procedure, we can define what each element of
our interface does.
inbetweener_gui::Command(id,msg)
{
switch (id)
{
case B_CANCEL: // if the Cancel button was
pressed... (B_CANCEL is the name of the button that we
set when creating the interface in ResEdit)
Close(); // we close the window
111
structure
case B_OK: // if the Ok button was pressed...
(B_OK is the name of the button that we set when creating
the interface in ResEdit)
SaveControls(); // we save current state of
the GUI elements
Close(); // we close the window
GeEventAdd(DOCUMENT_CHANGED); // notify
Cinema4D that the document needs updating
break; // jump out of the switch structure.
Not really necessary but nice to keep things looking good
default: // if anything else happens... well,
nothing else could happen, really ;-)
}
return false; // return false because everything is
OK. Weird, but believe me... it should be like this :)
}
Now we define the procedures that load and save the controls in and out of
the interface, respectively.
inbetweener_gui::LoadControls()
{
var ret;
ret = SetItem(POS, cControls->GetInt(POS, false)); //
we load the value of the Position check box from the POS
item of the tag container into the POS item of the dialog
container
ret = SetItem(SCL, cControls->GetInt(SCL, false));
// we load the value of the Scale check box from the SCL
item of the tag container into the SCL item of the dialog
container
ret = SetItem(ROT, cControls->GetInt(ROT, false)); //
we load the value of the Rotation check box from the ROT
item of the tag container into the ROT item of the dialog
container
// we should check the return value of the ret variable
to check if the assignment was successful. But I think I
can trust everything went just fine.
}
112
inbetweener_gui::SaveControls()
{
cControls->SetData(POS, GetItem(POS)); // we load the
value of the Position check box from the POS item of the
dialog container into the POS item of the tag container
cControls->SetData(SCL, GetItem(SCL)); // we load
the value of the Scale check box from the SCL item of the
dialog container into the SCL item of the tag container
cControls->SetData(ROT, GetItem(ROT)); // we load the
value of the Rotation check box from the ROT item of the
dialog container into the ROT item of the tag container
inbetweenerTag->SetContainer(cControls); // load the
changed values into the actual container of the tag
}
The GetSomething function (the Something here can be Int, Float,
String, etc.) can have one or two parameters. The first parameter is always
the name of the element from which we want to get a value out of. If a second
parameter is given, that will be the returned value if the element that is named
in the first parameter is empty. So...
GetInt(POS,false)
...will return the value of the POS element of the dialog (it can be either one
or zero, depending on the state of the check box being on or off, respectively). If
the POS element is empty, a value of false (zero) will be returned. How can an
element be empty? Well, it could be in an undefined state when the tag is first
created, but since we are good coders, we will initialize the elements to suitable
values. But it is a good practice to code "on the safe side".
Well, all the stuff that we need to deal with the dialog is done. Now we will
define a new class: the plug-in itself. It is in this class that we will create all the
stuff that will make the plug-in tag really work. Just like before, we will create a
new class derived from an already defined class. Then we will overload all the
stuff that we need to be specific to our plug-in.
Depending on the type of plug-in you want to create, you need to derive this
new class from different parent classes. Since we want to create a Tag plug-in,
we need to derive the class from the ExpressionPluginTag class. You can
see all the other types in the SDK. and like I promised, we will create another
type of plug-in before finishing the C.O.F.F.E.E. lessons, so you will see how
different (or how similar) that will be.
113
GetID();
GetName();
GetHelpText();
GetIcon();
UseMenu();
MultipleAllowed();
Edit();
Save(hf);
Load(hf);
Execute(doc,op);
Theres nothing very new in the listing above; its just like what we did for the
GUI class. We declare some public procedures and name the ones we will be
overloading, in addition to the name of the constructor itself (named after our
inbetweener_plug class, derived from the ExpressionPluginTag class).
Actually, that is exactly what we will do first... define the constructor
procedure:
inbetweener_plug::inbetweener_plug()
{
var bc;
super(); // call the parent procedure (the one
defined by the ExpressionPluginTag class)
bc=GetContainer(); // get the container of the tag
bc->SetData(POS,false);
// set the POS element
to a value of false
bc->SetData(SCL,false);
// set the SCL element
to a value of false
bc->SetData(ROT,false);
// set the ROT element
to a value of false
SetContainer(bc); // load the adjusted values back
into the container
}
The Save and Load procedures will save the container inside a hyper file.
What is a hyper file? Its a file that is associated with the document and serves
114
to store values that cant be saved in a container. This means that even if you
close the document, when you open it again, the values you have set for the
plug-in options will get loaded again, properly. The hyper file is stored inside the
C4D document, so you will never actually see an icon for this file.
inbetweener_plug::Save(hf)
{
var bc=GetContainer();
hf->WriteContainer(bc);
}
inbetweener_plug::Load(hf)
{
var bc=hf->ReadContainer();
SetContainer(bc);
}
Now we define the other procedures. I will comment them to explain what
each one does:
inbetweener_plug::GetID() {return PLUGIN_ID;} //
obvious!! returns the plug-in ID number
inbetweener_plug::GetName() {return "Inbetweener";} //
This is the name that will show up in the menu
inbetweener_plug::GetHelpText() {return "Interpolates
Position, Scale and/or Rotation of the children of the
object it is applied to.";} // This is the text that
appear in the info area, when the cursor hovers over the
plug-in in the menu or its icon in the layout. Actually,
this is not possible with tag plug-ins but it is a good
habit to include this in the code
inbetweener_plug::GetIcon() {return icon_map;} //
return the icon of the plug-in tag
inbetweener_plug::UseMenu() {return true;} // setting
this to true makes the plug-in tag appear in the Tags
menu of the Object Manager
inbetweener_plug::MultipleAllowed() {return false;}
// since it is not logical that multiple inbetweener
tags could be assigned to the same object, we set this
to false. But some tags can appear more than once, for
example, the Selection tags.
115
The Edit procedure is called when the user needs to change something (by
double-clicking the tag or when it opens for the first time). Here it is what it does:
inbetweener_plug::Edit()
{
owner = GetOwner(); // we get the owner of the tag.
It will be easy to find because the inbetweener tag should
be the only one active at this point. Why? Because the
Edit procedure is called with the user double clicks the
tag or when it is first created.
var dlg = new(inbetweener_gui); // we create a new
instance of the dialog class (the one we defined above)
dlg->Open(true, -1, -1); // we open the window. The
first parameter of true means that the dialog will open as
a nonmodal window. This means that we can still do other
stuff while the window is open. The other two parameters
are the coordinates where we want the window to appear.
Since they are both set to -1, the window will appear at
the mouse location.
return true; // everything went fine so we return true
}
FINALLY!! The code that follows is what actually does the stuff that the
plug-in should do. All the code before and after it is just to define the functional
structure of the plug-in itself. The following code is what will really differ a lot
from plug-in to plug-in. As usual, I will comment the lines of code but you should
already understand most of it after all these lessons about C.O.F.F.E.E.
inbetweener_plug::Execute(doc,op)
{
var bc,pos_var,scl_var,rot_var;
var pos1,pos2;
var scl1,scl2;
var rot1,rot2;
var first_obj,last_obj,num_obj,step,f; // all the
variables used to perform the actions are declared
op=op->GetDown(); // we get the first child of the
object that contains the tag
first_obj=op; // store it again in another variable
because we will need it again, later
num_obj=0; // start a counter at zero
while(op) // as long as there is a valid object...
116
variable
{
num_obj++; // the counter is increased
last_obj=op; // store the last object in a
op=op->GetNext(); // get the next object
}
118
Chapter 11
render with, lets say, 700 pixels wide, you will have to calculate what the new
vertical size should be (to maintain the same aspect ratio). The same applies if
you want a new vertical size and you will have to calculate the new horizontal
size. What would be great was if you could simply say that you wanted a new
(horizontal or vertical) measure and the other dimension would adjust itself
automatically, maintaining proportions. That is exactly what the plug-in does.
It is named Proportional Render Size, or PRS for short. When you choose it,
from the plug-ins menu, you will get this dialog:
At the top you have a display of the current render size, and below it, you
can set the new render size. When you first open the plug-in, the new render
size is the same as the current render size. As soon as you adjust one of the
dimensions, if you press Enter (or Tab to move to the next field), the other
dimension will proportionally adjust itself. If you hit Cancel, the render size
will remain the same, but if you hit OK, the new proportional dimensions will
automatically be set in the Render Settings dialog.
Ok, enough with the chit-chat... on to the code:
include "c4d_symbols.h" // you can see what this does
in the previous lesson
var PLUGIN_ID = 1021121; // this ID is unique,
requested from www.plugincafe.com
// now, all variables that are used inside the plug-in
are declared,
// except for the ones that are exclusive to classes
var prs_version = "1.0";
var PLUGIN_RES, PLUGIN_PATH, RENDER_DATA, RD, BASE_
BITMAP;
120
var WIDTH_VAR,HEIGHT_VAR;
var TEMP_X,TEMP_Y;
var doc,icon_map;
var dialog_res,dialog;
// **************************************************
// this is the first class we define. It will be used to
deal with the dialog interface
class prs_gui : GeDialog
{
private:
var bc;
ContainerToDialog();
DialogToContainer();
Ok, this is our first stop. I know I already talked about this in the previous
chapter but it is the first time we really deal with such a thing as private
variables and methods.
Defining a variable and/or method as private means that they are only
accessible (and relevant) inside the scope of the class in which they are defined.
This means that the variable bc is only accessible by methods of the class
prs_gui. By the way, different classes can use the same variable names and
they will only use their own variable, not the ones defined inside other classes.
As we will see, we will define yet another class that will have its own variable
called bc. Each class will use its own variable, even if they share the same
name.
As you can see, we also declare two methods as private. You know they
are methods because they dont have a var statement preceding them and
they also have opening and closing parenthesis after their name.
Now we go on defining some overloading methods (if you want to know what
that is, read the previous chapter).
public:
prs_gui();
CreateLayout();
Init();
Command(id,msg);
121
GetContainer();
SetContainer(bc2);
prs_gui::prs_gui()
{
super(PLUGIN_ID);
bc=new(BaseContainer);
}
The previous method is the contructor. It is named after itself ;-)
The first thing it does is to call its super which does whatever generic stuff
it needs to do. Then we use the private variable bc to store a new container.
Remember all the talk about containers in the previous chapter? Well, we will
use our own container to store a copy of the values of the interface elements
of our dialog.
It is usually a good rule to store the values in some place safe (and a container
is the best choice because it can contain several types of data) because, this
way, you can manipulate the data without it messing up the interface. When you
have all the data neatly arranged, you can load it all at once into the dialog.
prs_gui::GetContainer() {return bc->GetClone();} //
this function returns a copy of the container.
prs_gui::SetContainer(bc2) {bc=bc2->GetClone();} //
this function stores in bc, a copy of the content of the
parameter passed into it.
prs_gui::CreateLayout() // you can see what this does
in the previous chapter
{
var ret = LoadDialogResource(IDD_DIALOG_PRS,
PLUGIN_RES,BFH_SCALEFIT |BFV_SCALEFIT);
SetTitle(" PRS v"+prs_version);
return ret;
}
prs_gui::Init() // this method will initialize all the
values inside the dialog
{
doc=GetActiveDocument(); // we get the active
document
122
ContainerToDialog();
The following two methods load the contents of our container into the dialog
and vice-versa, respectively.
prs_gui::ContainerToDialog()
{
SetString(CURR_SIZE_STR,bc->GetData(CURR_SIZE_STR));
SetInt(NEW_X,bc->GetFloat(NEW_X),1,16000,1);
SetInt(NEW_Y,bc->GetFloat(NEW_Y),1,16000,1);
}
I believe an explanation would be helpful at this point. Why did I use the more
specific function GetFloat for some cases and the more general GetData for
others? Well, the GetData is advisable because it can get ANY kind of data, no
mater what type it is. But if we want to force a specific type of data, we should
use the specific functions (GetFloat, GetInt, GetString, etc.).
To set values inside a dialog, we cant use SetData. Instead, we must
use specific commands (SetFloat, SetInt, SetString, etc.). Besides,
the commands that set numbers - like SetFloat or SetInt - also define the
minimum, maximum and step values of a field. Like this:
SetFloat(FIELD_ID,value,minimum,maximum,step);
The step value is how much the value increases or decreases when the
user clicks the arrows of the numerical field.
Now, to load the values to a general container (not the specific case of a
dialog container) we can/should use the SetData and GetData statements:
prs_gui::DialogToContainer()
{
bc->SetData(CURR_SIZE_STR,GetString(CURR_SIZE_STR));
bc->SetData(NEW_X,GetFloat(NEW_X));
bc->SetData(NEW_Y,GetFloat(NEW_Y));
}
The following is the method that is called when the dialog is displayed and
the user is interacting with it.
124
prs_gui::Command(id,msg)
{
DialogToContainer(); // we get the values of the
dialog into our container
switch (id) // new we decide what to do, depending
on what element of the dialog the user is interacting
with.
{
case BUT_CANCEL: // these are the names of the
interface elements, defined when creating the dialog in
Resedit
Close(); // the user pressed the Cancel
button, so we only need to close the dialog window
break;
case BUT_OK: // the user pressed the Ok button
so we need to update the render settings values
TEMP_X=bc->GetFloat(NEW_X); // we get the
value of the new width, set by the user
TEMP_Y=bc->GetFloat(NEW_Y); // we get the
value of the new height, set by the user
RD->SetData(RDATA_RESOLUTION,0); // we set
the Resolution list value to Manual
RD->SetData(RDATA_XRES,TEMP_X); // we set
the Resolution width value to the value set in the plugin
RD->SetData(RDATA_YRES,TEMP_Y); // we set
the Resolution height value to the value set in the plugin
RD->SetData(RDATA_FILMFORMAT,0); // we set
the Film Format list value to Automatic
RD->SetData(RDATA_XFILM,TEMP_X); // we set
the Film Format width value to the value set in the plugin
RD->SetData(RDATA_YFILM,TEMP_Y); // we
set the Film Format height value to the value set in the
plug-in
RENDER_DATA->SetContainer(RD); // the
container, with the adjusted values, is loaded to the
Render Settings
GeEventAdd(DOCUMENT_CHANGED); // notify
Cinema 4D that the document was changed
Close(); // we can now close the dialog
window
break;
case NEW_X: // the user pressed Enter while
in the NEW_X field or just left the new NEW_X field by
pressing TAB
TEMP_X=GetFloat(NEW_X);
TEMP_Y=(TEMP_X/WIDTH_VAR)*HEIGHT_VAR; //
125
main()
{
icon_map = new(BaseBitmap,32,32); // the plug-in
icons have 32 by 32 pixels (the tag plug-ins have 24 by
24, remember?)
var file = GeGetRootFilename();
if (!file) return;
file->RemoveLast();
PLUGIN_RES=new(GeResource,file); // we store the
location of the res folder in the variable PLUGIN_RES
file->AddLast("res");
file->AddLast("prs.tif"); // we get inside the res
folder, locate the file "prs.tif"...
icon_map->Load(file); // ...and load the icon
// you should already know what the following lines
do because it has been done several times before
doc=GetActiveDocument();
RENDER_DATA = doc->GetFirstRenderData();
RD=RENDER_DATA->GetContainer();
WIDTH_VAR=RD->GetFloat(RDATA_XRES);
HEIGHT_VAR=RD->GetFloat(RDATA_YRES);
Register(PRSPlugin); // finally we register the
plug-in, making it appear in the Plugins menu
println("prs v"+prs_version+" was successfully
loaded."); // ...and signal it was loaded successfully
}
And this is it. To check it out, just load the folder from the Goodies into the
Plugins folder in CINEMA 4D. This is the basis of all menu plug-ins so you can
use it - adjusting it accordingly - to code any menu plug-in. Besides, I have
additional good news. If you download the SDK from version 9.5 (I strongly
recommend that you have both SDKs, for 9.5 and 9.6), there is a folder in
there, named examples. Inside this folder, there is another folder, named Basic
Frameworks. Inside it, you will find basic structures for all types of plug-ins you
can code. For example, the plug-in I present in this lesson, started out by using
a duplicate of the file menuplugin.cof.
You can also find another folder named XLent Framework inside the
examples folder. This one is even more versatile, because inside the same file
you have the code for all types of plug-ins. You just have to delete what you
dont need and then adjust what is left. Inside the examples folder there are
quite a few additional files that are quite useful to dissect.
128
Chapter 12
Finally we reach the end of this series about C.O.F.F.E.E. I dont intend to
fool you (or even myself) into thinking that these twelve lessons are all that you
need to become a proficient C.O.F.F.E.E. coder - far from it. But now you have
the basis - maybe even a little more than that - to start coding your own scripts
and plug-ins.
You will step on a lot of difficulties and doubts, just like I still do. Yes, I still
have to ask a lot of stuff in the forums, do lots of research and a whole lot of
trial & error attempts, but I told you from the start of these lessons that Im not a
programmer, remember? Knowing now that you will still face some difficulties,
let me also tell you that the joy of finally, having something we did, working just
the way we wanted, is tremendously rewarding. Anyway, when you get stuck in
some situation that apparently has no solution, you can always ask someone or
perform a search at the forum at www.plugincafe.com.
So, what will I teach in this last chapter? I decided to talk about miscellaneous
things... the stuff that is usually referred to as hints and tips. The stuff that you
all like, isnt it? :-)
Ok, lets start...
Imagine you created the greatest plug-in of the universe - well, at least a
nice one - and you want to distribute it (freely or commercially, that is up to
you). But you would like to keep your code to yourself, preventing others from
finding out how you accomplished the tasks your plug-in performs. To be able
to do that, you compile your plug-in. Compiling is the process of converting
all your source code - from a format that (hopefully) you understand - into a
129
compact binary format that only CINEMA 4D understands. When you open a
compiled C.O.F.F.E.E. script in a text editor, you get what is usually referred to
as gibberish. Here is a sample of it:
C4D-C.O.F.F.E.E.-100008}x
o??????
^^^^^^
RRRRQQQQ
RRRRQQQQ
RRRRQQQQ
???????????????????????????? ??? ??
/:0A 0%-7@
GNU[agoz
.5;AGOZchrx*5AKT[fr"-7AKValv!)19AIQZclxxxxxxzxxxxxxxxxxx
xxxzzzzzzzzzzzzxxxx%x-!!--))-/11//99/1AA11II13EJEEJE3EOJEEW_
p3ppWtpx~pp~!~-)~-1~/9~/A~1I~1Q~-Z~/c~1xxxpxppWO!xxxx!!)19AI_!(x
3pxxx3xp33tp3!3)31393A3I3Q3Z3c9pppHOp9pOppWX`fpxpppO`ppppWp9pTT
T
[f"-7Ar-7A-7A-).@KSK^invTTT
KValvVlvVlv/).@KSK^inv!!T8!!TT!8 !-8888888888888888!!!1-).@KSK^invBOX^dj|BBBBBB*X^d!B [
%f -r 7
@
G
N" U- [7 aA g
K
V
a
l
v
.
5
;
A
G
c
hjB
X
^
d
[
@
G
N
U
[
a
z
g
.
5
;
A
Z
c
Gf @
G
N
1" 2@ 2G 2N
@
G
N" @
G
N 2[7
2g
U
[
a
z
g
1KVa 2
2
2
K
V
a
2v 2
.
c
1
2
2 2.
.
. 2; 2G
5
;
A
Z
c
Gr
"
2@
2G
2N
@
G
N
U
[
a
z
g
...
130
XMIN=3,
YMAX=4,
YMED=5,
YMIN=6,
ZMAX=7,
ZMED=8,
ZMIN=9,
XAXIS=10,
YAXIS=11,
ZAXIS=12
}
var PLUGIN_ID=1011605;
var MENU_NAME="Aligner";
var HELP_STR="Aligns, distributes and adjusts object
positions.";
...
This is a snippet of the actual code that I wrote. So, how do you compile a
plug-in? The good news is that there is a plug-in that compiles other plug-ins.
You have it inside the examples folder that resides inside the SDK folder for
CINEMA 4D 9.5:
So, just drag (or copy) the Compiler folder into your CINEMA 4D plug-ins
folder. Once you restart CINEMA 4D, you will get a new item in the Plugins
menu.
131
Choose it and you will be presented with a file selector dialog. Navigate to
the folder where your plug-in resides and pick the file with the extension .cof.
After hitting Open, you may think that nothing happened, but navigate to the
folder where your plug-in resides and you will see that a new file was created
there. It has the same name as your .cof file but with an extension of .cob,
standing for Binary C.O.F.F.E.E.
I advise you to keep a folder with the .cof file saved in a safe place and
keep the folder with the .cob file inside your CINEMA 4D plug-ins folder. You
132
{
println("Name of child: "+child->GetName());
num++;
}
Our num variable starts out with a value of zero and the child variable starts
out pointing to the first child of the op object (we can be sure about that because
the script returns if no child is found). We fill in the values of the variables below
the corresponding column and also add a little arrow to designate the child that
the child variable is pointing to, like this:
Now we follow the script... it prints out the name of the child, increases the
value of the num variable by one and repeats the cycle. So, we annotate the
current value of the variables, like this:
Ops... shouldnt the child variable be already pointing to the next child?
Ok, it seems that we found the bug. Since the child variable never gets
updated, the while cycle never reaches the end, so we end up in an endless
135
loop. So we just have to add a single line of code to the script, like this:
while(child)
{
println("Name of child: "+child->GetName());
child=child->GetNext();
num++;
}
It is now safe to run the script. But now, after printing the name of all children,
we get an error telling us:
C.O.F.F.E.E. ERROR!
(5)Incompatible values... STRING / INTEGER
File: expression
Line: 16
The line number 16 is:
println("The object "+op->GetName()+" has "+num+
" children.");
The error is telling us that we are using a value that is incompatible with
the instruction we are using. Mmmmmm, what could be wrong? The println
instruction requires a string of characters, right? We are feeding it an
expression.
The first part is a true string: "The object ".
Then we add to it a function: op->GetName()
Since GetName() returns a string, all is ok.
Then we add to it another true string: " has ".
Then we add to it the variable num. But num holds a number!!! That is our
culprit. We need to change the number into a string of characters that represents
that number. Luckily, we have such a function, so change the line to:
println("The object "+op->GetName()+" has "+tostring(num)+"
children.");
Tadah!! Our script is now working fine. Those two bugs are very common
and the first one (the lethal one) is harder to track. But as you can see, with a
little methodical thinking, we can track down most bugs.
Other types of bugs can be even subtler, like trying to use a value in degrees
when it should be in radians, or vice-versa. You must pay special attention to
136
the types of data you must provide to the functions and what type of data they
return.
Dont forget you can always place println instructions in strategic places
in your code to display the content of specific variables or simply to check
if some segment of code is being executed (for those purposes, a simple
println("+++"); will do).
You may also type your expressions incorrectly. For example:
var1+var2*var3
is not the same as
(var1+var2)*var3
You must pay special attention to the order of evaluation of expressions,
otherwise you will get different values than what you expected.
Another possible bug is that the method you chose to do what you want
is simply wrong. Well, that is not exactly a bug, but it will make for a nonfunctioning script. Make sure you document yourself enough - there are lots
of good resources on the net - and make as many
hand-written sketches/layouts as you find necessary.
Dont forget that you have a huge help reference
in the SDK documents. Explore them as much as
possible and all the folders that they include.
If you are really into this coding stuff, there are
a few books that can help you a lot. One of them is
"The C Programming Language" by Brian Kernighan
and Dennis Ritchie (http://www.amazon.com/CProgramming-Language2nd/dp/0131103628/ref=pd_bbs_2/102-5942985-013
2913?ie=UTF8&s=books&qid=1179834018&sr=8-2)
The other one is "The C++ Programming Language",
by Bjarne Stroustrup (http://www.amazon.com/
C%2B%2B-Programming-Language-Special-3rd/
dp/0201700735/ref=pd_bbs_sr_1/102-59429850132913?ie=UTF8&s=books&qid=1179834042&
sr=8-1)
137
The first one will teach you all the basics about the C language and the
second one will give a more deep knowledge about C++ (C.O.F.F.E.E. is a type
of C++).
Finally, dont forget about the www.plugincafe.com site. It is the home of a
proficient community of C.O.F.F.E.E. and C++ programmers and they will do
their best to help you out when you are in trouble or simply lost.
Its now time to wrap things up. After twelve lessons about C.O.F.F.E.E., I
hope you guys have at least the desire and curiosity to dig a little deeper into
coding. I also hope that I was able to destroy the misconception that coding is a
very complex thing that is only for those with a degree in computer science. Its
not as simple as picking flowers, I agree, but it is not that multi-headed monster
that you thought it was before reading these lessons, is it? Rest assured, the
more you code, the better you become at coding, like almost everything else.
Have a lot of good cups of C.O.F.F.E.E. :-)
138
Appendix
Editors Note
If youre wondering why everything isnt always declared as public so that
it would always be available wherever it might be needed, thats a good
question, but a little beyond the scope of this book.
It could take a whole curriculum on software design to really drive that point
home. But to give you a quick glimpse into the subject, imagine that a bunch
of really smart guys with names like Gandalf, Obi-wan, some Stoustrup
fellow, and so forth, all thought it over long and hard and came up with a
way to design complex software to be maintainable and extensible for future
enhancement. In doing so, they conjured up the concept of abstraction which
is based upon something called implementation-hiding which, in turn, is
established using access-specifiers. You guessed it: theyre known as public,
protected, and private.
You see, if every portion of code knows and depends upon the specific details
of how other parts of the code are implemented, then you get to a point where
you cannot make any changes without affecting - and often breaking - the
parts of code that have come to depend upon those implementation details.
So sophisticated techniques have been developed to hide implementation
details in a black box approach where one part of the code just asks for
something to be done and doesnt get involved in the specifics of how it
actually gets done. When skillfully employed, that can alleviate the problem of
having many parts of code unnecessarily dependent upon other parts of code.
139
Acknowledgements
I would like to thank Tavy Ann (http://www.3dattack.us) for doing the first revision
of the text. Without her preliminary work, this book would be filled with flagrant
mistakes in misspelling. Im sure it would be a funnier text to read but this type
of book should be as accurate as possible, you all must agree.
Unfortunately, some modifications were made to the text after Tavy finished
proofing it.
I would like to thank Jeff Andrews (http://www.chromecity.com) for doing the final
review, going through the whole text of this book - including the code listings and correcting my English down to each individual comma or period.
Since English is not my native language (Im Portuguese, for those who didnt
know that), I knew I would misspell something or, even worse, mess up the
grammar.
Once again, a big thank you to Tavy Ann and Jeff Andrews for making this book
a more pristine work.
140
Chapters Index
Chapter 1.......................................................................................................................3
Introduction to programing languages, focusing specially on C.O.F.F.E.E.
Presentation of some basic concepts related to programming languages.
Chapter 2.....................................................................................................................10
Practical examples of some basic programming in C.O.F.F.E.E.
Introduction to variables, conditional and logical expressions.
Chapter 3.....................................................................................................................21
Introduction to one of the more important concepts of C.O.F.F.E.E. programming: objects
Chapter 4.....................................................................................................................31
Lots more about objects.
Chapter 5.....................................................................................................................43
Learn how to use the SDK. Introduction to the concept of loops and cycles.
Chapter 6.....................................................................................................................53
Fully working example of a script, using all the concepts learned so far
while introducing a few more.
Chapter 7.....................................................................................................................67
Fully working example of a more complex script, using all the concepts learned so far.
Chapter 8.....................................................................................................................79
Learn how to use Resedit, the official tool to create GUI dialogs for plug-ins.
Chapter 9.....................................................................................................................91
Fully working C.O.F.F.E.E. tag plug-in.
Chapter 10.................................................................................................................103
Complete and exhaustive explanation of the plug-in presented in the previous chapter.
Chapter 11.................................................................................................................119
Fully working C.O.F.F.E.E. menu plug-in with explanation of the whole code.
Chapter 12.................................................................................................................129
Wrapping up... Learn how to compile your plug-ins and hunt for bugs.
141
Alphabetic Index
Symbols
!=............................................................................................................................15, 17
[ ... ]..............................................................................................................................62
{...}..................................................................................................................................6
==...........................................................................................................................14, 17
3D Attack.................................................................................................................... 67
&& (and)..................................................................................................................... 18
> (larger than)............................................................................................................. 17
>= (larger than or equal to)......................................................................................... 17
< (less than)................................................................................................................ 17
<= (less than or equal to)........................................................................................... 17
! (not)............................................................................................................................26
|| (or)........................................................................................................................... 18
; (semicolon)................................................................................................................. 6
A
AddLast............................................................................................................ 118, 128
and............................................................................................................................. 14
Animation................................................................................................................... 45
array........................................................................................................................... 62
B
BaseBitmap...................................................................................................... 118, 128
BaseDocument........................................................................................................... 37
BaseDraw................................................................................................................... 75
BASEDRAW_DATA_CAMERA............................................................................. 70, 75
BaseList2D................................................................................................................. 35
BaseList4D................................................................................................................. 35
BaseObject..................................................................................................... 35, 37, 43
Basic Frameworks.................................................................................................... 128
BIT_AOBJ............................................................................................................ 56, 57
Books....................................................................................................................... 137
break...................................................................................................... 52, 64, 65, 125
bugs.......................................................................................................................... 133
C
C........................................................................................................................... 4, 137
C++....................................................................................................................... 4, 137
case.............................................................................................................. 56, 64, 125
Casts.......................................................................................................................... 46
class............................................................................................... 23, 35, 44, 108, 121
142
Class.......................................................................................................................... 47
Close................................................................................................................ 112, 125
C.O.F.F.E.E................................................................................................................... 3
CoffeeExpressionTag........................................................................................... 56, 59
Command..................................................................................... 4, 108, 111, 121, 125
Compile.................................................................................................................... 129
Conditional (expression)............................................................................................. 16
Conditions.................................................................................................................. 14
Console........................................................................................................................ 9
CreateLayout.................................................................................................... 108, 121
Cycle.......................................................................................................................... 49
D
Debugging................................................................................................................ 133
Declaration................................................................................................................. 47
default................................................................................................................... 64, 65
Dialog......................................................................................................................... 84
do............................................................................................................................... 51
doc (operand)..................................................................................................... 5, 7, 24
Documentation......................................................................................................... 137
DOCUMENT_CHANGED................................................................................. 112, 125
Document classes...................................................................................................... 44
Double........................................................................................................................ 13
E
Edit........................................................................................................................... 114
EditPlus...................................................................................................................... 96
else....................................................................................................................... 14, 16
enum........................................................................................................................ 104
Exception Handling.................................................................................................... 47
Execute............................................................................................................ 114, 126
Expression.................................................................................................................. 92
ExpressionPluginTag........................................................................................ 113, 126
F
Files............................................................................................................................ 46
FindObject...................................................................................................... 25, 26, 36
Float................................................................................................................... 13, 113
for............................................................................................................................... 48
Function........................................................................................................................ 4
Functions.............................................................................................................. 46, 47
G
GeDialog..........................................................................................................
GeEventAdd.....................................................................................................
GeGetRootFilename........................................................................................
GeResource.....................................................................................................
143
108, 121
112, 125
109, 128
109, 128
144
J
Java.............................................................................................................................. 4
JavaScript..................................................................................................................... 4
L
Language..................................................................................................................... 3
Literal (expression)....................................................................................................... 8
Load................................................................................................................. 114, 128
LoadControls.................................................................................................... 108, 110
LoadDialogResource........................................................................................ 109, 122
Loops.......................................................................................................................... 48
M
main.............................................................................................................................. 5
Materials..................................................................................................................... 45
Math........................................................................................................................... 46
Memory....................................................................................................................... 46
MenuPlugin.............................................................................................................. 126
mi................................................................................................................................ 96
MultipleAllowed........................................................................................................ 114
N
new............................................................................................................... 22, 36, 109
Nil............................................................................................................................... 25
NotePad..................................................................................................................... 96
O
object (oriented programming)............................................................................. 20, 31
OBJECT_POLYGON............................................................................................ 56, 58
Objects....................................................................................................................... 45
Open......................................................................................................................... 116
Operand....................................................................................................................... 4
Operator....................................................................................................................... 4
op (operand)....................................................................................................... 5, 7, 24
or................................................................................................................................ 14
P
plugincafe........................................................................................................... 40, 129
PLUGIN_ID.............................................................................................................. 105
Plugins........................................................................................................................ 45
PointObject........................................................................................................... 35, 43
PolygonObject............................................................................................................ 36
print.............................................................................................................................. 9
println........................................................................................................................ 8, 9
private............................................................................................................... 108, 121
Program Structure...................................................................................................... 47
145
protected.................................................................................................................. 108
public................................................................................................................ 108, 121
R
RDATA_XRES.................................................................................................. 123, 127
RDATA_YRES.................................................................................................. 123, 127
ReadContainer......................................................................................................... 115
Recursive................................................................................................................. 106
Register.................................................................................................................... 118
RemoveLast..................................................................................................... 109, 128
ResEdit....................................................................................................................... 80
Resource Editor.......................................................................................................... 80
Resources............................................................................................................ 45, 79
RestoreLayout.......................................................................................................... 126
Return........................................................................................................................... 6
S
Save......................................................................................................................... 114
SaveControls.................................................................................................... 108, 112
Script............................................................................................................................ 5
Scripting....................................................................................................................... 3
SDK.............................................................................................................. 40, 43, 128
SetContainer............................................................................................................ 114
SetData.................................................................................................... 113, 123, 124
SetFloat.................................................................................................................... 124
SetInt........................................................................................................................ 124
SetItem..................................................................................................................... 112
SetName.............................................................................................................. 36, 39
SetPoints.............................................................................................................. 56, 65
SetPosition......................................................................................................... 29, 117
SetRotation............................................................................................................... 118
SetScale................................................................................................................... 117
SetString................................................................................................................... 124
SetTitle............................................................................................................. 109, 122
Shaders...................................................................................................................... 45
Single......................................................................................................................... 13
SphereObject....................................................................................................... 36, 38
SplineObject............................................................................................................... 34
sqrt............................................................................................................................. 69
Stack........................................................................................................................ 106
String............................................................................................................ 13, 47, 113
SubEthaEdit............................................................................................................... 96
super................................................................................................................ 109, 122
switch..................................................................................................... 56, 63, 64, 125
Syntax.......................................................................................................................... 4
146
T
Tag.............................................................................................................................. 92
Tags............................................................................................................................ 45
TextEdit....................................................................................................................... 96
TextWrangler.............................................................................................................. 96
then............................................................................................................................ 14
tostring.................................................................................................................. 28, 74
Types.......................................................................................................................... 46
U
Undo........................................................................................................................... 67
UseMenu.................................................................................................................. 114
User Data....................................................................................................... 54, 60, 68
Utilities........................................................................................................................ 46
V
var.............................................................................................................................. 11
Variable................................................................................................................... 7, 10
Vector......................................................................................................................... 47
W
while........................................................................................................................... 51
Word........................................................................................................................... 96
WordPad..................................................................................................................... 96
WriteContainer.......................................................................................................... 115
X
XLent Framework..................................................................................................... 128
147
rui_mac 2007-2008
No contents - part or whole - of this document can be reproduced without expressed
permission of Rui Batista (rui_mac@ruimac.com)
148