You are on page 1of 146

Yup, left blank on purpose.

You can use it to draw whatever you want :-)

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.? 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?" 3

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.. 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. 4

To create your first C.O.F.F.E.E. script you need to add a new C.O.F.F.E.E. tag to an object. It can be any object you want but, for this purpose, lets create the simplest object there is: a Null. Now that you have a Null, add to it a C.O.F.F.E.E. tag. You do that exactly the same way as you add any other tag. Once you add the C.O.F.F.E.E. tag, you end up with a nice cup of C.O.F.F.E.E. in the Object Manager and, automatically, CINEMA 4D opens a C.O.F.F.E.E. Expression Editor. Even if you close this window, you can always get it back by double clicking the C.O.F.F.E.E. tag attached to your object(s). As you can see, the C.O.F.F.E.E. Expression Editor has already "typed" some code for you:

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?

Now that we know how in interpret the:

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 t 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

Invalid variable names are, for example: 123name is_this_a_variable? what about this one return (it starts with a number) (no, because it contains an invalid character: the question mark) (no can do... it contains spaces) (ops... its not possible to use command names either)

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;

You can type: var name1,name2,name3,my_number,another_number; Much more economical, isnt it? A good rule is to join together related variables. You can also define the variable like this: var name1,name2,name3; var my_number,another_number; Its just that it is easier to read and understand code that is neatly written, but you can type it, as you prefer within the limits I have already presented. 12

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 then 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:

IF there is a mad man with a knife coming towards me THEN its a good idea to start running
Conditions can be more complex, like this:

IF there is a mad man with a knife coming towards me AND I have my feet tied THEN then I may be in deep trouble
And even more complex...

IF there is a mad man with a knife OR an gun, coming towards me AND I have my feet tied OR there is no place to run to THEN I better start saying
my prayers Conditions can also offer alternatives:

IF there is a lion in my living room THEN I should call my psychiatrist or ELSE I should leave home, just to be safe
In all programming languages, you can test out conditions and take action accordingly. I mean, if a certain affirmation is true some action is taken. And how does this translate into C.O.F.F.E.E.? Try to figure out that this script does: main(doc,op) { var a=1; if(a==1) println("The variable a is equal to one"); } I believe this is obvious enough, except maybe for the two equal signs (==). Why two? That is a syntax imposition again. When you use just one equal sign you are assigning a value. If you look up in the script listing, that is exactly what is happening when I typed: var a=1; I was ASSIGNING a value to the variable. When two equal signs are used, we are comparing. So, a==1 returns a logical value. What is a logical value? Its either TRUE or FALSE, depending on the result of the comparison. In our case, the expression a==1 (a equals one?) is true, right? So, the println("The variable a is equal to one"); statement is executed. Try pressing the 14

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 logic 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

Yes, yes... the C.O.F.F.E.E. expression for NOT EQUAL is: != So, != actually means DIFFERENT whereas == mean EQUAL. The previous code is perfectly legitimate. But not very compact and not as elegant as: main(doc,op) { var a=1; if(a==1) println("The variable a is equal to one"); else println("The variable a is NOT equal to one"); } Logical, isnt it? As you may already guess, you can do the same with code blocks, as in: main(doc,op) { var a=1; if(a==1) { println("The variable a is equal to one"); println("Nice to know, isnt it?"); } else { println("The variable a is NOT equal to one"); println("Why, oh, why?"); } } Oh, by the way, you really must enclose the conditional expression you are testing inside parenthesis. Syntax requirements sorry, but I believe this also makes it easier to read the conditional expression. 16

Inside the conditional expression (the stuff between the parenthesis) 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 is EQUAL to 1 a is DIFFERENT than 3 b is EQUAL to 1.56 c is DIFFERENT than "word" a is LARGER than 10 b is LESS than minus 5 c is LARGER or EQUAL to 20 a is EQUAL to b c is DIFFERENT than b

a==1 a!=3 b==1.56 c!="word" a>10 b<-5 c>=20 a==b c!=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 a 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

You can even combine them. Just check this out: main(doc,op) { var a=10; var b=20; var c="10"; if(a==10 && c=="10") println("The variable a and c store the value 10, even if not in the same way"); if(a==5 || b==20) println("The variable a stores the number 5 or, maybe, the variable b stores the number 20"); } Executing this script will print both sentences. In the first condition both a is equal to 10 AND c is equal to "10" so, the whole expression is true. The operand for AND is, as you may have noticed, &&. Why two "&"? Not only to maintain consistency with the other conditional operands (== and !=) but there is another logical operation that uses the single character &. But there is no need to worry about that other operation right now. Using the && operand, only if ALL expressions evaluate to a true value, the result will also be true. So, if any of the expressions is false, everything else fails and the condition will become false. You can use as many && as you want, like this, for example: if(a==10 && c=="10" && b==20) println("Everything is true!!"); If, any of the comparisons failed lets say, for example that the variable b was set to 21 instead of 20 the whole expression would become false and the sentence would not be printed. Now, for the OR operand. Weird symbol, isnt it? Two vertical bars. Depending on your keyboard it can be in several different places, but it usually is on the same key as the backslash symbol. So, usually, pressing shift+\ will get you a vertical bar symbol. By the way, it is called a "pipe" symbol. Why? Beats me... but has a nice sound, you must agree. So, two pipe characters stand for OR. The same way as with the && operand, you can place as many || operands as you need inside an expression. The difference is that as long as, at least, one of the conditions is true, the whole expression will be true. This means that if all conditions are false, but at least 18

one of them is true, BINGO, the whole expression will be okeedokee! You can also combine && and || in the same expression, like this: if(a==10 && c=="10" || b==20) println("Getting complicated? Nah!!"); And, like you learned in math classes, you can also enclose expressions between parentheses to force some parts of the expression to evaluate first, like this: if(a==10 && (c=="10" || b==20)) println("Not complicated... just more powerful."); When you use parenthesis in conditional or mathematical expressions pay special attention to make sure you match all opening parenthesis with a closing parenthesis. Otherwise, you will get a syntax error. Ok, lets wrap this chapter. What have you learned? You learned lots of stuff about variables: how to create them, how to assign them values, how to manipulate them, somehow. You also learned how to make decisions, based on conditions. Until now, the only thing you can do with variables and conditions is to print stuff based on them. Be patient, young grasshopper. At least you are able to have some feedback on the Console about your actions. This may not seem very exciting now, but dont forget we are just laying down the foundations for more juicy stuff in the future. Once you master the general structure of programming, we will start manipulating real objects and documents. In the next chapter we will learn a couple more basic commands and we will start learning what object oriented programming is all about. And that, my friends, is all you need to know to start doing the really powerful mumbo-jumbo.

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 legate 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 de-valorization 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->devalorization() 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 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. Lets 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 parenthesis! 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 would they contain and what tools they would 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 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

Real, valid, C.O.F.F.E.E. scripts are enclosed between main(doc,op) {... }, remember? So, now we have a new variable of type twingo. We can assign values to this variable this way: my_car.country_of_origin="France"; my_car.number_of_seats=4; my_car.year_of_manufacture=1995; we can also perform calculations using its tools, like: println(my_car->maximum_weight()); ... well, if only we had given it enough information by filling in all the data it requires. Lets summarize the characteristics of objects: - they can have data attached. - that data is easy to understand because it is properly named. - they can have tools (functions) attached. - those functions can work with all the data that is attached to the object. - its possible to derive objects from other objects and they inherit all the data and functions from the "parent" object. I know I didnt even show you code to create new objects. It was all very abstract so, why did I bother to talk so much about objects? Because almost everything in C.O.F.F.E.E. is based on objects and, if you understand the philosophy behind objects, EVERYTHING I will teach from now on will be much simpler. I didnt bother telling you how to create your own objects (a new object type, in C.O.F.F.E.E., is called a class, by the way) because most of you will never need to create new objects anyway. CINEMA 4D already has lots and lots (and I really mean LOTS!!) of object types defined for you. You just need to understand what they are and how to deal with them. Now, I have a little surprise for you. Did you know that you have already seen a couple of objects since the very first lesson? No way!! Really?!?! Yup... remember those inconspicuous variables between the parenthesis, after the name of the main function? The doc and op variables? You got it!! They 23

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), 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... its 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 object 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

4D objects (Cubes, Spheres, Lights, Nulls, HyperNURBS, etc). Sorry, no pun intended, really. It may sound a bit confusing using the word "object" to refer to programming objects and 3D objects but you should figure out which one Im referring to, out of context. Anyway, to make it simpler, I will from now on, call primitives to all 3D objects inside CINEMA 4D. I can show you a few more functions associated with primitives and documents now that I assume you understand the philosophy behind objectoriented programming. For example, if I wanted to search for a primitive with a specific name, inside the current document, I could do this: main(doc,op) { var obj; obj=doc->FindObject("PalmTree"); } This would search for an object named "PalmTree" (without the quotes, of course). This object could be anything: a Cube (not a very pretty palm tree, lets face it), a Sphere (not a great improvement, from a Cube), a polygonal object (now, that could very well be a palm tree), a Null (that could contain a set of objects that would, in fact, assemble into a palm tree), or anything else. If it finds any object named "PalmTree", starting from the very first object in the document, it will assign that object to the variable obj. If it doesnt find any matching object, the variable obj will get a value of Nil. Nil is the equivalent of a Null, but for programming. The name is almost the same, isnt it? So, in programming, Nil stands for Nothing, an empty value, something with no defined value. So, we could improve the last script a bit by doing: main(doc,op) { var obj; obj=doc->FindObject("Torus"); if(!obj) println("Are you crazy? There is no such object in the document!"); else println("Mmmmmm, I did found a nice looking Torus somewhere in here."); } 25

I believe the only thing that is a bit weird - although, somehow familiar, if you remember the last lesson - is the (!obj) Remember what is 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... its not a very 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 nonexistence 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. Lets see if you can spot what 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!!) lets 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 parenthesis, 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 to: println("The object Torus is placed at position ("+tostr ing(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, that 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 to 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. Lets now write a script that prevents the object it is attached to, to move in the Y axis. This is useful to 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 axis: 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 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 can see, the GetPosition and SetPosition functions work with the local 29

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 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 edition, 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 view port 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 parenthesis? They are necessary and its very important to assure that they are balanced. With this, I mean that there must be as many opening parenthesis and there are closing parenthesis. Otherwise, you will get a syntax error. The most inner parenthesis are the ones surrounding the "My_Sphere" parameter. They are required because all functions require that their parameters be enclosed in parenthesis, 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 parenthesis 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 parenthesis 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 parenthesis 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 its 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

Since it is a task we are performing on the document (placing something in it), we use the doc variable again. This time we use its InsertObject() function. It requires three parameters: the object to be placed, a parent object inside which we want to place the object and an object, after which we want to place the object. Weird!! Must we provide all that? Not really. The only really required parameter is the object we want to insert in the document. The other two are optional and, in this case, I simply replaced both parameters with the constant NULL (a constant is like a variable that you cant change. CINEMA 4D already has lots of constants defined and NULL is one of them. It simply stands for... nothing). So, what happens if I dont tell the InsertObject() function what primitive I want to use as a parent of my newly inserted object or after what primitive I want to place my newly inserted object? Nothing special. It simple places the object at the top of the primitives list, in the Object Manager, just like if I had created a new primitive by clicking its icon. Now, we do have a new sphere inserted in the document. But it has the default name of "Sphere". We must change it to "My_Sphere" to make sure the conditional expression at the beginning will, in fact, find such a primitive the next time it runs and doesnt create yet another, and another and another sphere (ad infinitum). The new_sphere variable still stores the newly created sphere. In fact, everything we do to this variable, we are doing to the sphere primitive that is now in the document. So, to change the name of the primitive, we use the function SetName(), like this: new_sphere->SetName("My_Sphere"); I believe this is clear enough and requires no explanation. Even so, I will tell you something that may not be too obvious. The new_sphere variable holds an object of type SphereObject, right? Right! And if I told you that the SphereObject object HAS NO SetName() FUNCTION DEFINED! But we just used such a function... you may say. Well, the SphereObject object itself has no such function defined but the BaseObject object does and the SphereObject is a child of BaseObject, inheriting everything from it (and from his "grand-daddy", BaseList4D and its "great-grand-daddy", BaseList2D). Do you understand how powerful objects are now? I hope you do. Try deleting the new sphere. A new one is automatically created. Try 39

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 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 its 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

be presented with a window similar to this one:

(I assume the Windows HTMLHelp version is similar). As you can see, there are a few links to interesting stuff like "Introduction" or "The C.O.F.F.E.E. language" or even "Tutorials". You should also read the "About this documentation" page, as it contains some references about additional sources of information. I will not talk any more about this document in this chapter. Simply keep it accessible and wait for the next chapter. Oh, by the way, the www.plugincafe.com site has lots more useful stuff, besides the C.O.F.F.E.E. documentation. One of the greatest sources of information is the forum. I advise you to register there (dont worry, its free) and to use it often. If you have a doubt or even a problem you cant solve, chances are that someone else went through the same situation. Do a search in the forum before starting to pull out your hair.

42

Chapter 5

Hello again guys (and girls). 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 were 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 can also access to some more specific characteristics of some special kinds of objects, namely polygonal (as in, non-parametric) 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. Other advantage is that the texts are 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, what platform is the C.O.F.F.E.E. code running in, loading of documents, execution of external application, opening of external files, etc.

Files
If you need to access to 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 on 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 function, 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 session 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 session 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 vector 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 is their type 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 fix 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 automize 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 interactions 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 interactions 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 you 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 did 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 variables 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

This would print in the console: The variable a holds the value: 0 ----The variable a holds the value: 1 ----The variable a holds the value: 2 ----The variable a holds the value: 3 ----The variable a holds the value: 4 ----The variable a holds the value: 5 ----You can skip the initialization, if the variable is already initialized, like this: main(doc,op) { var a; a=0; for(;a<6;a=a+1) println(a); } Notice that the semicolon still has to be there. In fact, for loops ALWAYS require all the semicolons (just two), even if nothing is placed inside them. So, if you want to create an infinite loop, you could write this: main(doc,op) { var a=0; for(;a<1;)println("This will last forever. You better force quit the application!"); } I dont advise you to try this script. If you try to execute it, you will have to press Command+Alt+Esc (if you are on a Mac) or Ctrl+Alt+Del (if you are on a PC) to force quit CINEMA 4D. In future chapters we will see how flexible the for loop can be but, for now, 50

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 that your code never gets executed, if the test fails, 51

use the while {...} method. What if you need to get out of a loop due to an unexpected situation? If, for some reason, you need to get out of a loop, even if the verification test doesnt fail, you can always use the break command. Check out this example:
main(doc, op) { var obj; obj=doc->GetFirstObject(); while(obj) { if(obj->GetName()=="Light") { println("Found a light."); break; } println(obj->GetName()+" is not a light."); obj=obj->GetNext(); } println("End of search."); }

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

We have finally reached a level of knowledge (I hope) that allows us to start exploring fully working examples of C.O.F.F.E.E. scripts. Most of the things you will see in such scripts will not sound so "foreign" any more. You all know now what variables are, what conditional expressions are, what is an object, what is a loop/cycle, etc. You may have not noticed but, if you have been following these articles, you are ready to start, at least, understanding a few simple C.O.F.F.E.E. scripts. From here on I will be presenting fully working C.O.F.F.E.E. scripts and, dissecting them. We will now be learning much more that all the required " programming infrastructure" was learned. Enough with the small talk. Let us proceed to the juicy stuff. After long consideration (and painful one-person brainstorm sessions) I decided that a good script to present as the first one would be a modeling aid script. It is not extremely complex, but it is already a true C.O.F.F.E.E. programming example. What exactly will this script do? Consider the following scenario: One often-used modeling technique involves the creation of a half-model that is "completed" by using a parent Symmetry object. The Symmetry object is a very useful tool but has some limitations. One of those limitations is that the points near the mirroring plane should not move in the perpendicular of that same plane, otherwise the welding will not occur. Of course we have a Tolerance value that we can set but, if you are using the Brush tool or the Magnet tool (two tools that are often used for organic modeling), it is very easy 53

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 it 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 what 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 it self. 55

Wow!! This is a big one, isnt it? At least it is the biggest one, so far, 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 distributed 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 its 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 Cinema4D 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

So, op->GetBit(BIT_AOBJ) will return us to the bit number 2 of object op. You could also type op->GetBit(2) but using the variable is more readable. Since the value of a bit is either true or false, the result of op->GetBit(BIT_ AOBJ) is a logical value. That is why we can use the expression: if(! (op->GetBit(BIT_AOBJ))) return; Meaning that... if the bit is NOT set (meaning that the object is not selected) we can return immediately. After this (and now that we know the object is selected) we must check if it is a polygonal object. We can get the type of an object with the function GetType(), like this: op->GetType() Luckily, there are already lots of variables inside CINEMA 4D that store important values (like the BIT_AOBJ variable). And, in this case, we also have a variable that holds the value that means that an object is a polygonal object. It is the variable OBJECT_POLYGON. If you dont know the variable names you can easily check what values correspond to each type of object by simply creating the object you want to check and add to it a C.O.F.F.E.E. expression tag. If you type the code: main(doc,op) { println(op->GetType()); } ... and press Execute, in the Console you will get a number that corresponds to that type of object. In our case, a polygonal object has a type value of 5100 but using the variable will make the whole script much more readable. We check if the object is polygonal object and, if not, we return immediately, with the following sentence: if(! (op->GetType()==OBJECT_POLYGON)) return; Now we will read its point list. If it werent a polygonal object, it would not have a points list and trying to read it would return an error. Because of the execution reached this far, we know for sure that it is a true polygonal object. There is a function associated with polygonal objects that returns the number of points of an object. As you will start to realize, most have very logical names and this one is no exception. Its named GetPointCount(). So, now we store that value inside the variable npoints, like this: 58

npoints=op->GetPointCount(); We must now check if the polygonal object is not empty. This could be the case 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 logic, 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 are objects? 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

You will end up with this: if(! (coffee_tag#ID_USERDATA:1 Now just type the rest: if(! (coffee_tag#ID_USERDATA:1)) return; Since the first user data field is a Boolean operator, it simply returns a true or false value. If it holds a value of false (checked with the ! operator), meaning that the expression is turned off, we simply return. Now we get the tolerance value and store it in the tolerance variable, with this line: tolerance=coffee_tag#ID_USERDATA:2; Obvious enough, isnt it? Now we check if the value of tolerance is 0 (zero). If it is, there is nothing to do and we return also. if(tolerance==0) return; Ok!! We have made all the necessary checks. We can now, safely, run through all the points and perform the real action. We just need to get the value of the last user data field: axis=coffee_tag#ID_USERDATA:3; 61

We now get the list of all points, with: p_list=op->GetPoints(); This will store a list of all points inside the variable p_list. How can a variable store ALL the points of an object, you may ask? Like I said on one of the first chapters, variables in C.O.F.F.E.E. are very versatile. And they take the "shape" of whatever type of data they store. In this case, the p_list variable turns into an array of vectors. Darn!! "Complicated stuff", your brain warns you!! Not really. Let us dissect what an array of vectors is. First, let me explain what an array is. When I explained you what variables were, I said that they were like containers or boxes that could store values inside, right? Well, an array its like a warehouse where you store several containers. It still has a single name, just like any regular variable. But you can access any of its elements by means of an index. Something like this: variable_name[10] This would return to us the eleventh element (indexes start at zero). Remember when I told you that a variable could contain many types of values? Ok, so imagine that an array is just like several variables in a row, all containing the same type of content (a number, a character, a Boolean value, etc). They are all accessible by the name of the array followed by an index value enclosed inside [ and ]. So, what is a vector? A vector is an object. It has a set of commands and functions associated with it, just like any other object, of course. But the most important characteristic of a vector, at least for us, is that it can store three independent values, namely x, y and z. So, for the sake of example, if a variable named rotat is of type vector, you can access (read, write, manipulate...) three independent values, in the form: rotat.x rotat.y rotat.z This type of object is very often used because, as you may imagine, there are lots and lots of stuff that needs to deal with x, y and z. So, the list of points is and array of vectors. If the variable that hold the list is named p_list (like in our example), the array of vectors it contains has this type of structure:

62

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 interaction 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 did we do that? Well, because since 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 is located each specific element (multiply the index by the size of the elements of the array and retrieving 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 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 than if you had used lots of if commands. 63

It works like this: switch(variable) { case value1: stuff to do break; case value2: stuff to do break; case value3: stuff to do break; default: this

if the variable has a value equal to value1

if the variable has a value equal to value2

if the variable has a value equal to value3

is optional but its the stuff to do if none of the previous cases applies

In our case, we must perform different actions, depending on the axis we choose in the user data parameters. So, our switch structure looks like this: switch(axis) { case 0: if(pt.x<=tolerance) pt.x=0.0; break; case 1: if(pt.z<=tolerance) pt.z=0.0; break; case 2: if(pt.y<=tolerance) pt.y=0.0; break; }

What this does should be pretty obvious, but I will explain it anyway. The variable axis is evaluated by the switch command. If it happens to 64

be equal to zero, the following conditional expression is performed: if(pt.x<=tolerance) pt.x=0.0; What this does is to check if the x coordinate of the point is less than or equal to the tolerance value, stored inside the tolerance variable. If that is true, the x coordinate should become 0.0. And why 0.0 and not simply 0? Because coordinates are not integer values and, just to make sure, we should keep it as a float value, even if they are zero. After that, a break command must be present. This break command will make the execution of the script go on after the switch structure. If there were no break command, all the code after the first successful case would be executed, even if it didnt matched the other case statements. The same conditional expressions are evaluated for the y and z coordinates, depending on the value of the axis variable. In our case there is no need for a default case because our axis variable will always be 0, 1 or 2. When we leave the switch structure the pt variable already has one of its components updated (x, y or z, depending on the axis variable value). So, we just need to store the updated point, stored inside the pt variable (as type vector), back into the points array, p_list. Finally we end the for loop with the closing }. This will assure that all the points inside the p_list array are updated. The last thing we need to do is to load the p_list array into the object, updating its point list. That is done with: op->SetPoints(p_list); This last sentence should be very logical, by now. We just need to close the script with the last closing }. I hope you have been saving your file. At least now, that you finished typing the script, you should save your file before testing it. This is because some C.O.F.F.E.E. scripts may be potentially dangerous since you could be messing with the inner structures of a file. This specific script is not particularly dangerous but saving, at least at this stage, is a good practice to get used to. Before running the script we should check for syntax errors. Press the Compile... button. If there are any errors, check out the relevant lines until everything is fine. You can now close the Expression Editor window and test out your script. To 65

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 on 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 score 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.

dist2=op#ID_USERDATA:5; // the user data named Dist. 2 (%) dist3=op#ID_USERDATA:9; // the user data named Dist. 3 (%) if(dist2>dist3) dist3=dist2; op#ID_USERDATA:5=dist2; // the user data named Dist. 2 (%) op#ID_USERDATA:9=dist3; // the user data named Dist. 3 (%) val2=max_dist*dist2; val3=max_dist*dist3; op#ID_USERDATA:6=tostring(val2,".2f"); // the user data named Dist. 2 op#ID_USERDATA:10=tostring(val3,".2f"); // the user data named Dist. 3 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 curr_cam=doc->GetRenderBaseDraw()#BASEDRAW_DATA_CAMERA; matrix=op->GetMg(); mat1=matrix->GetV0(); matrix=curr_cam->GetMg(); mat2=matrix->GetV0(); curr_dist=distance(mat1,mat2); if(curr_dist>max_dist) curr_dist=max_dist; perc=curr_dist/max_dist; op#ID_USERDATA:13=tostring(curr_dist,".2f")+" rc*100.0,".1f")+"%)"; // the user data named Distance curr_obj=obj1; ("+tostring(pe

70

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 its definition 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 that 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 defined 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

The next session of code reads: if(dist2>dist3) dist3=dist2; op#ID_USERDATA:5=dist2; //the user data named Dist. 2 (%) op#ID_USERDATA:9=dist3; //the user data named Dist. 3 (%) This checks if the value of the dist2 variable is larger than the value of the dist3 variable. This should not happen because the distance 2 should not be larger than the distance 3. If that happens, we make the variable dist3 equal to the value of the variable dist2. Then we reload the adjusted values into the correspondent User Data fields. Since dist2 and dist3 come from User Data fields that hold percentage values, they store values that vary between 0 and 1 where 1 stands for 100%, 0.5 stands for 50%, .33 stands for 33% and so on. Now we multiply their value by the value of the maximum distance, stored in the max_dist variable and stored those results in the val2 and val3 variables, respectively. This will give us the correct values of the distance, in units, instead of percentages. So, why did I define the distances as percentages of a maximum distance? Because, if you redefine the maximum distance all the other distances will adapt accordingly. If the distances were set as absolute values, you would have to adjust ALL the distances, not just the maximum distance. Now that we have the true values of the distance 2 and distance 3, we will load those values into the User Data fields that were created just for display purposes. But those User Data fields are of type String so they required strings (as in, characters), not numeric values as the ones we have in the variables val2 and val3. So, we change them into strings using the function tostring, like this: op#ID_USERDATA:6=tostring(val2,".2f"); op#ID_USERDATA:10=tostring(val3,".2f"); The function tostring can use one or two parameters. In previous chapters I showed you how to use the tostring function with just one parameter. Now I will explain how to use it with two parameters. The first is the value to change into a string (usually its a variable, like in our case, val2). The second parameter instructs the tostring function in how to convert that value. You can tell CINEMA 4D to format the string as a floating-point number with a specific number of decimal places or as an exponential value, for example. 74

By default, all floating-point numbers have 6 decimal places. That is a lot!! 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 we even notice 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 75

as we already have the position stored in another variable): 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 change, 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 an added information for the user, we now display the current distance between the camera and the Instance object and what percentage of 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")+" ing(perc*100.0,".1f")+"%)"; ("+tostr

As you can see, we have to transform all numerical values to strings using the tostring function. It is also easy to understand that the distance is displayed with only two decimal places and the percentage (multiplied by 100.0 because it currently holds a value between 0 and 1) is displayed with only one decimal place. That is more than enough but you can adjust that to your personal taste. 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 that Dist. 2. As soon as the current distance reaches Dist. 2, the Object 1 is switched by 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 by Object 3. What is the best way to organize your scene? I usually use something like this:

I create a Null that 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 (a not very usual situation) I strongly recommend using resources to define your plug-ins interface. So, how to 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" its easily fixable. Just close one of the windows that appeared.

And now choose "Execute Last Plugin" from you Plugins menu.

Now the toolbox window, (that is unmovable in MacOS), is out of the way and you can re-arrange all other windows to your taste.

81

ResEdit opens four windows: the Toolbox window where all the interface elements that you can use are available. the Properties window where you can set parameters for all your interface elements. the Structure window where a hierarchical representation of your interface is displayed. your interface window as in... your interface document. But this last window (your interface document window) is a bit weird, at first. Its waaaaaay too small. Actually, its not that weird, since you havent created anything in it, yet. To make it more pleasant to look at - and to allow us to see the interface elements we will create - let us enlarge it a bit. So, just grab the size handles, (depends on what OS you are using, of course) and make it a decent size. OK, we are ready to go! By the way, these steps Ive presented here must be executed every time you open CINEMA 4D and you choose Resource Editor from the ResEdit menu for the first time. But, like I said, its not a bug... its a "feature"... at least by Microsoft standards ;-) Even before we start creating our interface, we need to do an additional task: we must create a folder with a specific hierarchy where we will build our plug-in and its interface. So, start by creating a folder named InBetweener (that 82

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 chance 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 bellow 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 windows and you should end up with a Structure window and Dialog window looking like this:

84

Now, hit the Button item ( ) from the Toolbox window two times (its the second item from the top, at the left, bellow the item). Select the first Button item from the Structure windows and change the Element ID to B_CANCEL and the name to Nevermind. Select the second Button item from the Structure windows and change the Element ID to B_OK and the name to Oh yeah!. Now hit the Update Preview button and you should have something like this:

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 then. 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 into 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

Your Properties window should look like this now:

Now that 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 at 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 out 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

NAME IDS_STATIC2; ALIGN_TOP; ALIGN_LEFT; BORDERSTYLE BORDER_NONE; BORDERSIZE 4, 0, 4, 4; COLUMNS 1; GROUP IDC_STATIC { NAME IDS_STATIC1; ALIGN_TOP; SCALE_H; BORDERSTYLE BORDER_THIN_IN; BORDERSIZE 4, 4, 4, 4; COLUMNS 1;

} } }

CHECKBOX POS { NAME IDS_CHECK; ALIGN_TOP; ALIGN_LEFT; CHECKBOX SCL { NAME IDS_CHECK1; ALIGN_TOP; ALIGN_LEFT; CHECKBOX ROT { NAME IDS_CHECK2; ALIGN_TOP; ALIGN_LEFT; } 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; } BUTTON B_OK { NAME IDS_BUTTON1; CENTER_V; 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, I 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 plug-ins. The rest of the code (the parts that may scare you at first) are just for structural purposes, being absolutely fundamental for the definition of the plugin 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 Plug-ins 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. 91

The ones that are most useful (and easier to create) are Menu and Tag plugins. 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 type 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 in 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 contents 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; GROUP IDC_STATIC { NAME IDS_STATIC1; ALIGN_TOP; SCALE_H; BORDERSTYLE BORDER_THIN_IN; BORDERSIZE 4, 4, 4, COLUMNS 1; SPACE 4, 4; } } CHECKBOX POS { NAME IDS_CHECK1; ALIGN_TOP; ALIGN_ CHECKBOX SCL { NAME IDS_CHECK2; ALIGN_TOP; ALIGN_

4;

LEFT; LEFT; LEFT;

CHECKBOX ROT { NAME IDS_CHECK3; ALIGN_TOP; ALIGN_ } } GROUP IDC_STATIC { ALIGN_TOP; SCALE_H; BORDERSIZE 0, 0, 0, 0; COLUMNS 2; SPACE 4, 4;

BUTTON B_CANCEL { NAME IDS_BUTTON; ALIGN_TOP; SCALE_H; } BUTTON B_OK { NAME IDS_BUTTON1; CENTER_V; SCALE_H; } } } }

93

You can also create this file using any text editor, if you didnt had used 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 had used 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 had used 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 had 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 comes 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 finish typing the code and saved it as inbetween.cof, place the file inside your inbetween folder, right next the res and string_us folders. 96

Place the inbetween folder inside the plugins folder that resides inside the Cinema4D folder. Now run CINEMA4D. 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; }

class inbetweener_gui : GeDialog { public: inbetweener_gui(); CreateLayout(); Init(); Command(id,msg); LoadControls(); SaveControls(); } inbetweener_gui::inbetweener_gui() {super(PLUGIN_ID);} inbetweener_gui::CreateLayout() { var dialog_path = GeGetRootFilename(); if (!dialog_path) return FALSE; dialog_path->RemoveLast(); dialog_res = new(GeResource, dialog_path); var ret = LoadDialogResource(IDD_INBETWEENER,dialog_res,BFH_ SCALEFIT | BFV_SCALEFIT); SetTitle(" Inbetweener v"+inbetweener_version); return ret; } inbetweener_gui::Init() { inbetweenerTag = GetActiveTag(owner); cControls = inbetweenerTag->GetContainer(); LoadControls(); return TRUE; } inbetweener_gui::Command(id,msg) { switch (id) { case B_CANCEL: Close(); break; 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*st if(scl_var) first_obj>SetScale(Mix(scl1,scl2,(f*step)) if(rot_var) first_obj->SetRotation(Mix(rot1,rot2,(f*st first_obj=first_obj->GetNext(); }

ep))); ); ep))); }

main() { icon_map = new(BaseBitmap,24,24); var file = GeGetRootFilename(); if (!file) return; file->RemoveLast(); file->AddLast("res"); file->AddLast("inbetweener.tif"); icon_map->Load(file);

Register(inbetweener_plug); println(" Inbetweener v"+inbetweener_version+" was successfully loaded."); }

Once again, dont feel scared by all this, potentially, complex code. I didnt comment on any line, on purpose. All the explanations will come in the next chapter. In the meanwhile, you already have a fully working plug-in tag. To check it out, once it loads correctly, create a Null object. Inside it, place as many objects you want. Something like the this picture on the right. 101

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 InBetween tag to the parent Null.

You will be presented with a dialog similar to this:

Select whatever options you want. For example, Position and Rotation. Press "Oh, Yeah!" and now, 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 also 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 but, 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 Plug-in 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. 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 means 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. But 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 code itself. And you can even change it after your code is compiled (more about that on 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 graphic user interface of the window of your plug-in. This way, you will be able to refer to them by name, instead of, by number. 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;

The first variable (PLUGIN_ID) is the ID number of the plug-in. All plug-ins need a unique ID number so that there is no conflict between them. And how do you know how to choose a number that will not conflict with any other plug-in? Easy, you go to www. plugincafe.com and request an ID from Maxon. This way, the ID you are provided with, will be stored inside Maxons database and no one else will receive that ID number again. But, if you are just testing out code for a possible plug-in, you can use any ID number between 1000001 and 1000010 (inclusive) because these are reserved for developing purposes. As soon as you have your plugin running, make sure you get a unique ID from www.plugincafe. com. Then a variable named inbetweener_ version is created with the current version of the plug-in (in this case "1.0"). This is not really necessary but it will make things easier later, if 105

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 is it 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

name means: a stack of something, as in a pile. And what is this "something"? Well, its the address that follows the call and all the variables from the calling procedure. So, when a recursive procedure calls itself, it stores inside a stack all the variable values that it is using and also the return address. Then, when a return command is reached, the return address is popped out of the stack and followed. This means that a recursive routine can rapidly fill a stack with lots of data. This can lead to out of memory situations. So, beware!! Recursive procedures are very compact and efficient, but they must be used wisely. Next, we define a procedure that will simply call our tag-finding-procedure, starting out with the first object of the document. GetTheActiveTag(doc) { return GetActiveTagOwner(doc->GetFirstObject()); } The next procedure does the opposite of the first procedure. It returns the first object that contains an active tag, if any. The explanation, as usual, will be given by the comments in the code: GetOwner() { doc = GetActiveDocument(); // get the current document if (!doc) return NULL; // there is no document! Return a NULL var active_tag=GetTheActiveTag(doc); // get the first active tag, if any if (active_tag) // if an active tag was found... { var ob=active_tag->GetObject(); // get the object that contains it... return ob; // ... and return that object } else // otherwise... { return NULL; // return a NULL because no object was found that contained an active tag. } } We now reach a completely new part of the code. Remember all the talk we 107

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 legate characteristics to objects that are generated from them, and so on, 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? So, this new class will deal with the dialog of our plug-in so, 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 that is not important, really. We only need public stuff. So, we declare the super class, named just like its "parent". The super is the procedure 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 will only open an empty window (that its default behavior). Since our plug-in has a dialog, with graphic 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. So, in a nutshell, we created a new class for our plug-in dialog that is based 108

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 bellow... 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 of the code). Also, we set the dialog to fit horizontally and vertically into the 109

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 bellow 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 bellow... 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 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 as this function can retrieve any type of data but, if I wanted to force a specific type of data, I could use other type of function. 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 its much more versatile. Of course, if you try to read a type of data using a function that relates with 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 did I know 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 when 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 into front of the ( symbol. CINEMA 4D typed for me, the name of the parameter, 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 bellow. 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) 111

structure

Close(); // we close the window break; // and jump out of the switch

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 }

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 elements 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 parental classes. Since we want to create a Tag plugin, we need to derive the class from the ExpressionPluginTag class. You can see all 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

class inbetweener_plug : ExpressionPluginTag { public: inbetweener_plug(); } GetID(); GetName(); GetHelpText(); GetIcon(); UseMenu(); MultipleAllowed(); Edit(); Save(hf); Load(hf); Execute(doc,op);

Nothing very new in the listing above, its just like 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 super itself (our inbetweener_plug class, derived from the ExpressionPluginTag class). Actually, that is exactly what we will do first... define the super 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 hoovers 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 (it double clicks the tag or 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 the one that 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 the one that 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 116

while(op) // as long as there is a valid object... { num_obj++; // the counter is increased last_obj=op; // store the last object in a variable op=op->GetNext(); // get the next object } if(num_obj<3) return; // if the number of children of the object that contains the tag is less than 3, return. num_obj--; // decrease the number of children of the object that contains the tag, by one. step=1.0/num_obj; // calculate the value of the incremental step, based on the number of children pos1=first_obj->GetPosition(); // get the position of the first child of the object the contains the tag scl1=first_obj->GetScale(); // get the scale of the first child of the object the contains the tag rot1=first_obj->GetRotation(); // get the rotation of the first child of the object the contains the tag pos2=last_obj->GetPosition(); // get the position of the last child of the object the contains the tag scl2=last_obj->GetScale(); // get the scale of the last child of the object the contains the tag rot2=last_obj->GetRotation(); // get the rotation of the last child of the object the contains the tag first_obj=first_obj->GetNext(); // point to the second child of the object the contains the tag bc=GetContainer(); // get pos_var=bc->GetData(POS); position check box scl_var=bc->GetData(SCL); scale check box rot_var=bc->GetData(ROT); rotation check box the container of the tag // get the value of the // get the value of the // get the value of the

for(f=1;f<num_obj;f++) // cycle through all the objects between the first and the last { if(pos_var) first_obj->SetPosition(Mix(pos1,pos 2,(f*step))); // if the position check box is set, adjust the position of the current object to a mix between the position of the first and last objects if(scl_var) first_obj>SetScale(Mix(scl1,scl2,(f*step))); // if the scale check 117

box is set, adjust the scale of the current object to a mix between the scale of the first and last objects if(rot_var) first_obj->SetRotation(Mix(rot1,rot 2,(f*step))); // if the rotation check box is set, adjust the rotation of the current object to a mix between the rotation of the first and last objects first_obj=first_obj->GetNext(); // advance to the next object } } The last segment of code, like I said, is the code that does the hard stuff. It is the equivalent to the main routine you know from the expressions you have been writing. But, like I said in chapter number one, all C.O.F.F.E.E. scripts need a main procedure. It is the first procedure that is executed and even plug-ins need one. So, here is the main procedure of our plug-in. main() { icon_map = new(BaseBitmap,24,24); // a new 24 by 24 pixels bitmap is allocated var file = GeGetRootFilename(); // we get the path to the plug-in that is running if (!file) return; // something went wrong... return file->RemoveLast(); // remove the last part of the path because the path includes the name of the plug-in itself file->AddLast("res"); // add the res folder to the end of the path file->AddLast("inbetweener.tif"); // add the name of the icon file to the end of the path icon_map->Load(file); // load the file Register(inbetweener_plug); // register the plug-in println(" Inbetweener v"+inbetweener_version+" was successfully loaded."); // print a message to the Console signaling that all is running fine } I hope all this explanation was not too scary. Like I said (more than once), most of the code you saw here is almost 100% re-usable, with just minor adjustments. That is good news, right? So, when you want to create a plug-in, you usually perform some tests with C.O.F.F.E.E. tag expressions. When you have your code working, you use this framework, paste your code into the Execute part and adjust the rest. Of course you have to deal with the dialog stuff but, that is fun to do, right? 118

Chapter 11

Here we are in chapter number eleven. As promised, I will present a fully working plug-in in this chapter, with fully commented code. Actually, I will not include much text in this lesson, except for the actual code because most of what you need to know was already explained in the previous chapter. You will see that there are many similarities with the code of the tag plug-in from chapter number ten. Anyway, I will comment the whole code, even some parts that were already explained previously. When additional explanations are required, I will interrupt the flow of the code to tell you about them. Oh, another thing... I will not explain or show the content of the resource files, namely, the files named c4d_symbols.h, IDD_DIALOG_PRS.res and IDD_DIALOG_PRS.str. They were already explained in previous lessons and you will get them inside the required folders of the plug-in that you can get from the Goodies folder. They were created with Resedit, like I explained in chapter number eight and, if you want, you can inspect them with a text editor or opening the IDD_DIALOG_ PRS.res file in Resedit. Before moving on to the code, I believe it would be a good idea to explain what the plug-in does, right? Well, I needed to make a simple plug-in that would allow me to include in a single chapter but, at the same time, I wanted to make something that would be useful to everyone. So, I remembered that one of the omissions of CINEMA 4D is that, in the Render Settings dialog, you cant rescale the dimensions of your final render, proportionally. Meaning that, if you have your render set to, lets say, 1200 x 900, if you want to make a smaller 119

render with, lets say, 700 pixels wide, you will have to calculate what will be the new vertical size. 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, bellow 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 as private two methods. 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 parent method. It is named after itself ;-) The first thing it does is to call its super; doing 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 the interface. When you have all 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

RENDER_DATA = doc->GetFirstRenderData(); // we get the active render settings for this document RD=RENDER_DATA->GetContainer(); // we get the container of the render settings. Its inside the container that are all the values you define in the Render Settings dialog. WIDTH_VAR=RD->GetFloat(RDATA_XRES); HEIGHT_VAR=RD->GetFloat(RDATA_YRES); Ok, time to stop again. We are getting the size of the render now... but how did I know the name of the parameters (RDATA_XRES and RDATA_YRES)? Well, you can get this information from a few places. For example, there is a file named COFFEEsymbols.h that resides in your CINEMA 4D folder, inside the resource folder. In this file - that you can open with any text editor - you will find ALL the names of ALL the accessible data fields in CINEMA 4D, for using with C.O.F.F.E.E.. The problem is that none of the names is commented so, its a matter of finding out by "approximate name" and trial/error what each name refers to. Another way to do it is to make some detective work. Open up the SDK for release 9.6, referred to in chapter number four (even if you are using release 10, you should be using the SDK for 9.6) and go to the Reference link. In it, go to the Document link. In the Document link you should find a reference to RenderData. Click it and you will see all the names of the data fields in it. Or, if you want to be very lazy and you are not in a hurry, go to www. plugincafe.com and ask at the forum ;-) Oh, I used GetFloat to get a floating point value because of two reasons: the value inside the RDATA_XRES and RDATA_YRES fields is a long value, meaning a very big integer so, using a float Im sure it will fit entirely in the variable. Besides, I will be making calculations with those values that involve floating point numbers so it is better off starting out with a float. // now that we have the values we need, we load them into our containers fields bc->SetData(CURR_SIZE_STR,tostring(int(WIDTH_ VAR))+" x "+tostring(int(HEIGHT_VAR))); bc->SetData(NEW_X,WIDTH_VAR); bc->SetData(NEW_Y,HEIGHT_VAR); // and now we pass the content of our container into the dialog 123

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 king 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 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 entered new NEW_X field by pressing TAB TEMP_X=GetFloat(NEW_X); TEMP_Y=(TEMP_X/WIDTH_VAR)*HEIGHT_VAR; // 125

the NEW_Y value is calculated, proportionally... bc->SetData(NEW_Y,TEMP_Y); // ... and loaded into our container ContainerToDialog(); // our container is loaded back into the container of the dialog break; case NEW_Y: // the user pressed Enter while in the NEW_Y field or just entered new NEW_Y field by pressing TAB TEMP_Y=GetFloat(NEW_Y); TEMP_X=(TEMP_Y/HEIGHT_VAR)*WIDTH_VAR; // the NEW_X value is calculated, proportionally... bc->SetData(NEW_X,TEMP_X); // ... and loaded into our container ContainerToDialog(); // our container is loaded back into the container of the dialog break; default: // well, there are no other possible choices... but its good to make things nice and clean } return FALSE; // report that there are no errors } The following section of code is the definition of the plug-in itself. It is very similar to the section of code of the previous lesson. But, instead of defining an ExpressionPluginTag, we define a MenuPlugin. Most of the code bellow requires no comments because it is almost the same as the one presented in the previous chapter. class PRSPlugin : MenuPlugin { private: var bc; public: PRSPlugin(); GetID(); GetName(); GetHelp(); GetIcon(); Execute(doc); RestoreLayout(secret); // this is the only one that sounds weird... more about it bellow } PRSPlugin::PRSPlugin() {super();} PRSPlugin::GetID() {return PLUGIN_ID;} 126

PRSPlugin::GetName() {return "PRS";} PRSPlugin::GetHelp() {return "Adjust render size proportionally.";} PRSPlugin::GetIcon() {return icon_map;} PRSPlugin::Execute(doc) { if(!bc) bc=new(BaseContainer); // if there is still no container, create a new one dialog = new(prs_gui); // allocate a new dialog dialog->SetContainer(bc); // assign it the newly created container dialog->Open(TRUE,-1,-1); // open the dialog. The parameters were already explained in the previous chapter bc=dialog->GetContainer(); // get the container from the newly opened dialog } The following section of code is new because the menu plug-ins has dialogs that can be dockable anywhere in the CINEMA 4D layout. Their dialog must be refreshed right after the layout is loaded (when you start CINEMA 4D), even if not explicitly evoked from the Plug-ins menu. That is what this method does. PRSPlugin::RestoreLayout(secret) { var doc = GetActiveDocument(); // get the active document RENDER_DATA = doc->GetFirstRenderData(); // get the Render Settings from the current document RD=RENDER_DATA->GetContainer(); // get the container of the Render Settings WIDTH_VAR=RD->GetInt(RDATA_XRES); // get the render width HEIGHT_VAR=RD->GetInt(RDATA_YRES); // get the render height if(!dialog) dialog = new(prs_gui); // if there is no dialog yet, create e new one... dialog->RestoreLayout(secret); // ... and restore it. } Finally, we reach the main routine. This is where we prepare all the stuff that the plug-in will need, inside it. 127

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 line 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 Plug-ins 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 more 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 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 - in a format that (hopefully) you understand - into a compact 129

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 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

...

This is a snippet of my Aligner plug-in, after compiling. I believe I can be rest assured that no one will be able to understand how I coded it ;-) At least it looks a whole lot different than:
include "c4d_symbols.h" enum{ COUNT=1, STORE=2 } enum{ XMAX=1, XMED=2,

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 Plug-ins 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 keeping the folder with the .cob file inside your CINEMA 4D plug-ins folder. You 132

cannot keep both versions in the plug-ins folder because, at startup, CINEMA 4D will try to load both and the second one will conflict with the first one, since they both have the same ID. Nothing dangerous will happen, but you will get an error message in the Console and that is boring. Also, the .cob file will load and run slightly faster. Really, is only very, very, and very slightly. But even a few microseconds faster, is faster, right? Your newly created .cob file still requires all the other files and folders inside its folder. In fact, the .cob file is just a substitute for the .cof file. So, once again, remember to keep the .cof file in a safe place, in case you want to make changes or, heavens forbid, clean a bug you found. Ok, speaking about bugs, I bet there will be many of them, lurking around your code, when you start writing your C.O.F.F.E.E. scripts. Even when you become a seasoned coder, rest assured that bugs will always appear, uninvited, ready to make you feel lost and confused. Dont get me wrong. I only say this because it is the plain truth. I started programming when I was fourteen (with a little interregnum of a few years when I was at college), and I still meet face to face with a huge crowd of bugs in almost all of my projects. Oh, and Im almost 36 years old, now ;-) So, how do you prevent bugs? You dont, because they are there almost always. So, you hunt them down and exterminate them until they are all gone. But, as almost every other "hunting" situation, it helps to know the enemy. So, what types of bug can you find? There are several types of bugs. I will show you a few of them and tell you how to fix them. For example, check out this script: main(doc,op) { var child,num; num=0; child=op->GetDown(); if(!child) return; while(child) 133

{ println("Name of child: "+child->GetName()); num++; }

println("The object "+op->GetName()+" has "+num+" children."); } Ok, can you spot the bugs in here? When you hit Compile (after typing it inside a C.O.F.F.E.E. tag, of course), it returns no errors so, apparently, it has no bugs in it. Or does it? Well, it does. It has two bugs and these are the ones that are not spotted by the compiler.

DONT TRY TO EXECUTE THIS SCRIPT!!


Otherwise you will have to force quit CINEMA 4D because one of the bugs is fatal. This fatal bug will create an infinite loop that will bring CINEMA 4D into a crash situation, so we must hunt for it first. Oh, this also shows that it is a good idea to always save your file before executing any script. This is especially true with scripts that are written inside a C.O.F.F.E.E. tag because these scripts are constantly being executed as soon as you do anything to change your document. For this kind of bug you will have to trace your code, line by line and, maybe, create a hand-drawn chart for it. Dont worry its easy. You start with something like this:

As you can see we have a fictitious hierarchy just to serve as an example, and we also have a little table with our variables. Below, we will fill in the values of the variables as we follow the script code. 134

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 correspondent column and also add a little arrow to signal 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 it another true string: " has ". Then we add 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 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 instruction 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 from 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

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.

139

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

140

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..................................................................................................... 108, 121 112, 125 109, 128 109, 128

141

GetActiveDocument......................................................................... 107, 122, 127, 128 GetActiveTag............................................................................................................ 106 GetBit............................................................................................................. 56, 57, 58 GetClone.................................................................................................................. 122 GetContainer.................................................................................................... 110, 123 GetData.................................................................................................... 110, 111, 124 GetDown.................................................................................................................. 106 GetFirstObject.............................................................................................. 24, 52, 107 GetFirstRenderData................................................................................. 123, 127, 128 GetFirstTag........................................................................................................... 56, 59 GetFloat. ................................................................................................... 111, 123, 124 GetHelp.................................................................................................................... 126 GetHelpText. ............................................................................................................ 114 GetIcon............................................................................................................. 114, 126 GetID................................................................................................................ 114, 126 GetInt. ............................................................................................................... 112, 124 GetItem. .................................................................................................................... 113 GetLength. .................................................................................................................. 34 GetMg. .................................................................................................................. 70, 75 GetName.............................................................................................. 24, 52, 114, 126 GetNext........................................................................................................ 52, 60, 106 GetObject................................................................................................................. 107 GetPointCount................................................................................................ 56, 58, 59 GetPoints. ............................................................................................................. 56, 62 GetPosition................................................................................................... 27, 29, 117 GetRenderBaseDraw........................................................................................... 70, 75 GetRotation.............................................................................................................. 117 GetScale. .................................................................................................................. 117 GetString.......................................................................................................... 111, 124 GetType................................................................................................................ 56, 58 GetV0................................................................................................................... 70, 75 GUI............................................................................................................................. 45 I Icon. ............................................................................................................................ 95 ID.............................................................................................................................. 105 ID_USERDATA..................................................................................................... 60, 73 if. ................................................................................................................................. 14 include.............................................................................................................. 103, 120 Index. ........................................................................................................................ 139 Init. ............................................................................................................ 108, 121, 122 InsertObject.......................................................................................................... 36, 39 InstanceObject........................................................................................................... 70 instanceof....................................................................................................... 56, 59, 70 Instruction..................................................................................................................... 4 Int............................................................................................................................. 113 Integer........................................................................................................................ 13 iText............................................................................................................................ 96

142

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

143

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

144

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

145

rui_mac 2007
No contents - part or whole - of this document can be reproduced without expressed permission of Rui Batista (rui_mac@ruimac.com)

146

You might also like