Tutorial 3.6: Making Macros

This tutorial makes a spiffy macro for a motion effect by blurring an image at any angle.

 

So, what is a macro? Macros are commonly-used nodes that are combined into new nodes (that you can use to thwart your enemies). You control what parameters are exposed, and hide the ones you do not need. Macros are extremely powerful ways of modifying Shake to suit your own nefarious needs.

The following images demonstrate the creation of a macro. The first image has four nodes, the Text node and the three nodes that combine to create the blur effect. Following a long, arduous, painful, tedious, frustrating (like this sentence) process, the second image shows the completed macro attached to an icon (RotateBlur icon). The third illustration shows the completed macro, RotateBlur, applied in the tree—it looks like any other node. The last image demonstrates that only the angle and the blur parameters are exposed from the two Rotate nodes and the Blur node that comprise the macro.

There is only one path to ultimate Enlightenment. Don't let anybody say it's some silly tripe like being nice to puppies, giving all your money to the poor, and calling your mother once a week (although, do not let this discourage you from trying that, too). Rather, it is achieved by dipping into the Shake scripting language with a text editor. This is detailed in the first three sections of the tutorial. What fun! Before your hand jumps over to the mouse to find another tutorial, the last part shows you how to interactively do the same thing inside Shake. There is a catch, however. To modify and enhance a macro, you must rely on the information found in the first parts of the tutorial. No rest for the wicked.

How to Make A Macro By Hand

The first step in a macro is to build the effect using normal nodes.


The algorithm works by rotating the image, blurring the image on the X axis, and rotating back to the start position. Therefore, if Rotate1 has an angle of 45 degrees, Rotate2 must have an angle of -45 degrees. The best way to do this is by entering an expression to link Rotate1 to Rotate2.

To link the Rotate1 and Rotate2 angle parameters:

Case sensitivity is important. In Shake, each word in a node name has initial capitalized letters. For example, QuickPaint has a capital Q and P. The parameters follow the same rule, except the first letter is lowercase. For example, in the parameter framesPerSecond only the P and the S are capitalized. As you develop macros, it is good form to follow these guides, as you never have to wonder which letters are capitalized. It's not like you'll get bludgeoned at the User Group Meeting you if you don't, though.

Set the Blur parameters:

Test the effect:

If it is not working for some reason, don't sweat it—delete your Rotate and Blur nodes, copy the following text, and paste it into the Node View with Command+V / Ctrl+V:

Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0);
Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba");
Rotate2 = Rotate(Blur1, -36.9218, 1, width/2, height/2, 0, 0.5, 0);

Because Shake is a compiler, you can copy nodes from text and paste the text into the interface. Ain't it swell?

Save the script for later use:

You are now ready to start creating the macro. First, create a User Directory that Shake looks in when launched. This directory is in your $HOME directory. You place macros, preference settings, and gui modifications in the User Directory. The User Directory has a series of subdirectories, which you can create in the Terminal.

Note: You can add files to other directories as well using an environment variable. For further information, jump to Customize Shake.

Create the macro file:


Copying Text into the Node View. If you copy the first two lines in the text editor, that is,

Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2,0, .5, 0);
Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba");

you can paste it back into the Node View with Command+V / Ctrl+V.

This demonstrates that you can copy between script and GUI, which is, in our humble view, mighty swell.

If you already know C programming, you may want to jump down to the finished macro, otherwise keep reading.

The text in your editor is raw script, meaning it is not formatted as a macro. If you paste it back in, you get three more nodes. You need to format the text as a single function—a macro.

Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, .5, 0);
Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba");
Rotate2 = Rotate(Blur1, 42.61728, 1, width/2, height/2, 0, 0.5, 0);

The left side of the argument supplies a variable name. In the above example, there are three variables: Rotate1, Blur1, and Rotate2. These are assigned to Shake functions, the Rotate and the Blur functions. (Inside Shake, these are referred to as "nodes." Inside a script, they are referred to as "functions," as you can have other types of functions.) Following that are the values for the functions. The first argument is generally the incoming image, so you can see the incoming image for Blur1 is the result of the first Rotate function, Rotate1. Rotate1 has a value of 0 as the incoming image—there is no expected input. If you look these functions up in the documentation, you find the scripting format for each function. For an example, jump to the Blur manual page. If you change the variable names Rotate1, Blur1, and Rotate2, you must modify every reference to them.

The following is used as an example only (it is not a step in the tutorial):


Marshall
= Rotate(0, -Holly.angle,1, width/2, height/2, 0, .5, 0); Will = Blur(Marshall, 200, 0, 1, "gauss", xFilter, "rgba"); Holly = Rotate(Will, 42.61728, 1, width/2, height/2, 0, 0.5, 0);

Notice how the strings "gauss" and "rgba" are in quotation marks. Compare these to what you see in the interface parameters tab to get an idea of how Shake saves its parameters in a script.

Now, add some formatting. You must declare the type of the macro—if the macro spits out an image, an integer (int), a number with decimal place (float), or a word (string). This macro needs to return an image, as it modifies an image. After that, add the macro name. In this case, name the macro RotateBlur, capitalizing each word for consistency. Also, include two sets of parentheses. Incoming variables are placed in the first set, and the second set (the curly brackets) surrounds the body of the macro. Add these first as they are easy to forget:

image RotateBlur( )
{
  Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0);
  Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba");
  Rotate2 = Rotate(Blur1, 42.61728, 1, width/2, height/2, 0, 0.5, 0);
}

Next, you need to declare the information that is passed into the macro. You can potentially change all parameters—the rotate angle, center of rotation, motion blur parameters, blur filter, and so on. Practically speaking, only the rotate angle and the blur amount need to be set, giving you two sliders. You must also declare that an image is passed into the macro to be modified. If you don't have an image, it is assumed that you are somehow creating a new image, like a Ramp or ColorWheel. Each time you add an image input variable, a knot is created on top of the node in the Node View (so you can plug in an image). Each time you add a float, string, or int, a new slider or text field is created in the Parameter View. When you add these variables, declare the type of variable (again, either image, int, float, or string), and then the name. The names of the parameters should follow that pesky rule mentioned earlier—the first letter in the parameter name is lowercase:

image RotateBlur(
  image input, 
  float angle, 
  float blur
)
{
  Rotate1 = Rotate(0, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0);
  Blur1 = Blur(Rotate1, 200, 0, 1, "gauss", xFilter, "rgba");
  Rotate2 = Rotate(Blur1, 42.61728, 1, width/2, height/2, 0, 0.5, 0);
}

Now, plug the variables into the right spot inside the macro body. Instead of the number or word copied from Shake, use the variable name instead. These values are later modified with sliders:

image RotateBlur(
  image input, 
  float angle, 
  float blur
)
{
  Rotate1 = Rotate(input, -Rotate2.angle, 1, width/2, height/2, 0, .5, 0);
  Blur1 = Blur(Rotate1, blur, 0, 1, "gauss", xFilter, "rgba");
  Rotate2 = Rotate(Blur1, angle, 1, width/2, height/2, 0, 0.5, 0);
} 

Next, indicate what you want to spit out of the macro with the return line. This is the final image (or float, int, string) that you want to extract out of the macro function. Remember the semicolon at the end of the line. In this case, return Rotate2, as it has the result you need:

image RotateBlur(
  image input, 
  float angle, 
  float blur
)
{
  Rotate1 = Rotate(input, -Rotate2.angle, 1, width/2, height/2, 0, 0.5, 0);
  Blur1 = Blur(Rotate1, blur, 0, 1, "gauss", xFilter, "rgba");
  Rotate2 = Rotate(Blur1, angle, 1, width/2, height/2, 0, 0.5, 0);
  return Rotate2;
} 

The following is the final macro. As a final touch, you should add default values for any parameter, so it launches without having to add values in the command line.

image RotateBlur(
  image input, 
  float angle = 45,
  float blur = 150
)
{
  Rotate1 = Rotate(input, -Rotate2.angle, 1, width/2, height/2, 0, .5, 0);
  Blur1 = Blur(Rotate1, blur, 0, 1, "gauss", xFilter, "rgba");
  Rotate2 = Rotate(Blur1, angle, 1, width/2, height/2, 0, 0.5, 0);
  return Rotate2;
} 

When this is done, save the file in your startup directory. Ensure the file name has a .h file extension. Some text editors automatically append an extension. This is bad. There is no correlation between the file name and the macro name.

/User/ImASpecialPerson/nreal/include/startup/my_macro.h

Now to test the macro. Go to the command line.

You might be saying to yourself right now, "Hey, why am I testing this in a Terminal?", which is a good question. All you have done so far is create the macro—you have not plugged it into the interface. You have not written anything that tells Shake to build this function into the interface. You do that in a moment.

shake -help rotate

If everything worked properly, you should get a listing of all functions that contain the word "rotate," including the new function, RotateBlur. The command line is the only place where capitalization doesn't matter, because it saves you the effort of having to press Shift all the time.

-irotate [density] [steps] [stepBlend]
[controlChannel] [invert]
-rotate [angle] [aspectRatio] [xCenter] [yCenter] [motionBlur]
[shutterTiming] [shutterOffset]
-rotateblur [angle] [blur]

If you didn't get this help message, it returns an error that tells you what went wrong.

Error Messages. When working in the terminal, error messages are printed in that window. When working in the interface, they are printed in the terminal that launched Shake, or, if you launched Shake with the Dock, in the Console.

The errors generally detail where you have a mistake or if you have a variable that is misnamed.

If you can't tell from the error message, check the following list of possible problems:

If you still can't figure it out, copy the finished macro from above into your startup directory and save it as my_macro.h.

Now try it on an image. If you don't have an image, go to <ShakeDirectory>/doc/pix. If you are using these images, try

shake traffic_bg.jpg -rotateblur
shake traffic_bg.jpg -rotateblur 100 300
shake traffic_bg.jpg -rotateblur "Linear(1,0@1,360@11)" 200 -t 1-10
shake traffic_bg.jpg -rotateblur "Linear(1,0@1,360@11)" 200 GUI

Terminal Shortcuts for Repeating Commands. If you press the Up Arrow key, it repeats the last command. Press the Up Arrow again to list the previous command on the list, and so on. Use the Left and Right Arrow keys to edit the command.

The "Linear..." used for the angle of the blur is an animation curve, using a Linear format. The first 1 indicates the curve is extended into infinity. The second set of numbers indicates you have a value of 0 at frame 1, and then a value of 360 at frame 11.

The last command launches the tree into the interface. You can see your new RotateBlur node in action. However, you still can't actually click on anything to create a second node.

How to Add an Icon to the Interface

This section shows you how to create a button in the interface and attach a node to the button. The icons for the Tabs have three qualities:

Make the icon with the RotateBlur function:

You now have the two elements that you need, the macro and the icon. You now need to write the code to place both in the interface.

nuiPushToolBox("Filter"); 
  nuiToolBoxItem("RotateBlur", RotateBlur(0,0,0));
nuiPopToolBox(); 
       
//These set the slider ranges for the blur parameter
//nuiDefSlider("RotateBlur.blur", 0, 400 );

"You've got to be kidding me." You rarely type code in—nobody remembers this stuff. Typically, it is copied from the nreal.h and nrui.h files, or from this documentation, then pasted in and modified. Now you know why there is online help.

The first line indicates the tab where the macro is placed, the Filter tab in this case. If the tab does not exist yet (for example, the MySwankFunctions tab), Shake creates it for you. The second line adds the function into the tab. The first occurrence of "RotateBlur" calls the icon file that you just created. It assumes you have matched the tab name with the file name, so if you actually did place this into the MySwankFunctions tab, you have to rename the icon file on disk to MySwankFunctions.RotateBlur.nri.

Creating a node without an icon. If you don't have an icon, create a plain button with text on it by placing an @ sign before the word:

nuiToolBoxItem("@OopsNoIcon",	RotateBlur(0,0,0));

As this example shows, you can have a different name between the button and the function it is attached to.

 

The next part of the code, RotateBlur(0,0,0), is the function call, followed by its default values. Because Shake does not know what image it is attached to (until you actually press it), place a 0 as the first (image) variable. The second and third zeroes set the angle and blur parameters to 0. The next line closes up the tab.

If there is a problem, check the following list:

If the function worked, walk with confidence and pride in the face of your enemies as you display your compositing prowess.

How to Set Slider Ranges

First, test the sliders.

Notice that the angle slider is from -360 to 360, but the blur slider is from 0 to 1, forcing you to use the Virtual Sliders (Ctrl+drag in the text field) to go beyond 1. Set your own slider range with a bit of code already included in the example from above. On the last line of your ui file, and only the last line, remove the comments, which are the //. When commented out, it is not read when Shake starts. By removing the comments, you reactivate the line. The result is the following.

To uncomment the slider definition:

nuiPushToolBox("Filter"); 
  nuiToolBoxItem("RotateBlur", RotateBlur(0,0,0));
nuiPopToolBox(); 
       
//These set the slider ranges for the blur parameter
nuiDefSlider("RotateBlur.blur", 0, 400 );

This sets the slider range from 0 to 400 for the blur parameter of the RotateBlur function. Spelling and capitalization are important, which is why that tiresome rule explained earlier starts to make sense.

You don't have to set a range for angle, because it is already set between -360 and 360 in one the Shake setup files. To change the angle slider range, duplicate the blur line and modify it to say angle. This example sets it from 0 to 180:

//These set the slider ranges for the blur parameter
nuiDefSlider("RotateBlur.blur", 0, 400 ); 
nuiDefSlider("RotateBlur.angle", 0, 180 ); 

For more information on customizing parameters, see the Customize section of the documentation.

Creating Macros with MacroMaker

Not the most fun way to do things, but it beats banging on rocks next to a road in the Georgia heat. This next part of the tutorial duplicates your labor by using the MacroMaker. It is a fast way of creating reusable macros, but it is important to understand the files it creates so you can modify the files. Because you (might have) already created the macro by hand, it helps demystify the process that the MacroMaker follows.

Text1 = Text(300, 200, 1, "Text", "Courier", 100, 
  xFontScale, 1, 
  width/2, height/2, 0, 2, 2, 
  1, 1, 1, 1, 0, 0, 0, 45
);
Rotate1 = Rotate(Text1, -Rotate2.angle, 1, 
  width/2, height/2, 0, 0.5, 0
);
Blur1 = Blur(Rotate1, 181.9876, 0, 1, "gauss", xFilter, "rgba");
Rotate2 = Rotate(Blur1, 42.64842, 1, 
  width/2, height/2, 0, 0.5, 0
);

You now have something like the following (again).

The top part of the Shake MacroMaker window contains text fields for the name of the macro, and the tab it is placed in. By default, it is called UserMacro, and creates a new tab called User. Feel free to change this information.

The "Store macro in" setting indicates where the macro is saved:

The output tells you what node is spit out of the new macro. Shake usually makes a good guess, but you may need to explicitly specify it if you have multiple branches selected. In this example, Rotate2 is the proper output. This is the equivalent to the line:

return Rotate2;

that you previously used in the first macro.

The next area contains the list of parameters you can expose to the user, with the name of the parameter, the default values, and the slider range. In this step, expose Blur1's xPixels parameter and Rotate2's angle parameter (but not Rotate1's).

Rotate1's In parameter is activated automatically. This is because an image is fed into it, but not accounted for by the nodes that you selected. This can cause problems (though not in this case), as all unattached image inputs are activated.

This process creates two files, which are similar to the files you previously created by hand:

You typically use the MacroMaker to create the initial files, and then modify the files with a text editor. You have created two macros, one by hand and an identical macro with the MacroMaker. Both of these processes create two nearly identical sets of files. The first files create the function, and appear in the $HOME/nreal/include/startup directory. The second set of files loads the macro into the interface, and is found in the startup/ui subdirectory. The interface files are separate as you may want the file to only appear in the command line. Why bother doing the function by hand? Not necessarily because you are being punished, but rather so you can expand your macros with custom behavior and buttons for the parameters, and to edit the functions. These processes are listed in the Customize and Scripting sections of the documentation.