INSIDE 3DS MAX® 7 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

INSIDE 3DS MAX® 7 [Electronic resources] - نسخه متنی

Adobe Creative Team

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید











  • Making Life InterestingYour First Ten (or So) Words of MXS


    MAXScript can take a big bite out of routine tasks. With a few simple commands at your disposal, you can frequently automate your way out of tedious clicking and even approach some problems from an entirely new angle. The goal of this section is not to make you a scripting whiz but instead to provide a few tools to help you automate tasks without involving a technical director or searching ScriptSpot.

    Your tool for interacting with MAXScript is called the Listener (MAXScript > MAXScript Listener), a window into which you can type script commands that are immediately executed, and into which MAXScript writes its output (Figure 6.1). Your commands appear in black, while MAXScript's feedback appears in blue (for output) and red (for error messages).

    Figure 6.1. The MAXScript Listener window.

    [View full size image]

    Your starting vocabulary is the following:

    • $

    • show

    • move

    • rotate (eulerAngles X Y Z)

    • scale

    • in coordsys

    • for

    • where

    • classOf

    • random

    • #() and []

    • at time


    Let's dig in.

    The "$" Symbol


    $ means the current selection. For now, it is most useful when only one object is selected, as it behaves a bit differently with multiple objects selected. Why do you need this? Well, one of the simple things MAXScript is good for is looking at and changing properties of objects in your scene. You access properties of the current object with $.propertyfor example, $.radius or $.height. Like most things in 3ds max, properties are organized into hierarchies. This means that properties can have properties of their own, and so on, all separated by dots, as in $.material.diffusemap.coords.U_Tiling, the U Tiling parameter of the Diffuse Color map of the selected object's material.

    If you just type a property name into the Listener, it will tell you the value of that property. To assign a new value, use $.property = newvalue. For relative values, you can use the operators += , -= , *= , and /=, as in $.radius /= 2, which halves the radius of a selected object (as long as it has a radius property to change).

    Note

    Make sure to use the number pad Enter key, not the Return key, when entering MAXScript commands in the Listener. The Enter key by the number pad will always execute the code you just typed, while Return causes the Listener to wait for more input in some cases. Use Return when you want to enter multiple lines of code in the Listener before executing them all.

    The value you set doesn't have to be a simple number. You can use mathematical expressions, other properties, or even math that operates on other properties.

    $ has another, similar use as well: Follow it with an object name, like $Box01, and you are referring to that node, and all the access to properties described above is just as valid (for example, $Box01.height). By using the wildcards * and ?, as in $Box*, you can set properties for a whole list of objects. (We'll get to handling multiple objects in more depth later.)

    Note

    If your object names have spaces in them, you need to use single quotes around the name, as in $'my box'.

    The "show" Command


    How do you know if an object has a particular property? As you might guess from the examples above, there are a lot of property namesfar too many to memorize. That's what the show command is for. show generates a list of an object's properties in the MAXScript Listener. For example, if you select a box and type show $, you'll get the following:


    .height : float
    .length : float
    .lengthsegs : integer
    .width : float
    .widthsegs : integer
    .mapCoords : boolean
    .heightsegs : integer
    false

    The word to the left of the colon is the property name, and the word on the right describes the type of value it's expecting (The false at the end is a MAXScript hiccup, not a property name; you can safely ignore it.) If you try to set a property to the wrong kind of value, as in $Sphere01.radius = false, MAXScript will complain with an error message. The sidebar explains some of the common value types you're likely to encounter and how to format them.

    Bear in mind that not all the object's properties are shown. All objects, as well as certain classes of objects, share some properties, such as name, material, and renderable, which do not appear for clarity's sake. Also, properties of the object's modifiers, materials, and other "extras" are not shown. Full listings are available in the MAXScript Reference, under "General Node Properties," as well as in the "common properties, operators, and methods" entry for that object class (see, I wasn't kidding when I said you had to use the Reference!).

    Note

    Meanings of Some Common Variable Type Names

    • integer:
      Whole number

    • float:
      Decimal number

    • string:
      A text value, entered with double quotes: "hello world"

    • boolean:
      true/false value, entered as true, false, on, or off

    • point2:
      Two-axis coordinate, entered with square brackets and a comma: [1,2]

    • point3:
      Three-axis coordinate, also with square brackets and commas: [1,2,3]

    • color:
      RGB values, entered with parentheses, no commas, and the word color: (color 128 128 128)

    When an object's properties have properties of their own, you can use show to list those subproperties as well. For example, if you needed to find out how to view or adjust properties of the box's material, you would type show $Box01.material. If you then wanted to find properties of the material's Diffuse Color map, you'd find the MAXScript property name for the diffuse map in the output of the previous command and use it to write show $Box01.material.diffusemap, and so on.

    Putting a Two-Word Vocabulary to Work


    So what can you do now, with only two words of MAXScript, that you couldn't do before? You can do the following:

    • Find and change any parameter of any object in your scene, even if it's hidden, frozen, or buried in a group.

    • Set any parameter of any object quickly, including copying settings from one object to another and setting values using mathematical expressions.

    • By using wildcards (* and ?) in object names, set a property for many objects at once.


    Pretty good for just two words, huh? Armed with your amazing vocabulary, see if you can figure out what each one-line script below does:


    $Box01.height = $Box02.height
    $Box01.height = $Box01.length = $Box01.width = 10
    $Sphere01.radius = $Box01.height/2
    $Box*.height = 10
    $Box*.isHidden = false
    $Omni*.multiplier /= 2
    $Box*.material = $Sphere01.material
    $Spot*.on = false

    The answers in plain English are below:

    • Set Box01's Height spinner to the value of Box02's.

    • Make Box01 a cube 10 units on a side.

    • Set Sphere01's radius equal to half of Box01's height (in other words, make the objects the same height).

    • Set the Height spinner of all objects whose name starts with Box to 10.

    • Unhide all objects whose name starts with Box.

    • Halve the Multiplier parameter for all objects whose name starts with Omni.

    • Assign all objects whose name starts with Box the material assigned to Sphere01.

    • Set the on parameter of all objects whose name starts with Spot to off. (This particular example is likely not to work as written; we will discuss why later in great detail. It is included here to get you thinking about the sorts of tasks you can automate with MAXScript.)


    "move," "rotate," "scale," and "in coordsys"


    Now we're going to add four more words, to give you some more verbs to use in your MAXScript sentences. You know what move, rotate, and scale mean already; we just need to cover syntax for those, and I'll bet you have your suspicions about coordsys.

    For move commands, you provide XYZ coordinates in square brackets, separated by commas (MXS calls this kind of value a point3):


    move $Box01 [10,0,0]

    moves the box 10 units in the positive X direction. Note that this is a relative move. For an absolute move, you set an object's position property instead:


    $Box01.position = [10,0,0]

    moves the box to 10,0,0 in world coordinate space.

    Rotation values are a little more complicated, and they require us to sneak an extra word into your vocabulary. To avoid the extremely ugly math of true 3D rotations, MXS lets you input rotation as a eulerAngles value: an X rotation plus a Y rotation plus a Z rotation. Like the similarly named Euler XYZ rotation controller, this method has problems with arbitrary rotation axes and with gimbal lock (the condition in which, after a lot of Euler rotation, the XYZ axes are no longer mathematically perpendicular and not all rotations are possible), but the gain in usability is well worth it.


    rotate $Box01 (eulerAngles 90 90 0)

    rotates the object 90 degrees in X and 90 degrees in Y.

    scale transforms take a point3 number, just as move does, but considers 100% scaling to be equivalent to the number 1, so a 50% uniform scale would look like this:


    scale $Box01 [.5,.5,.5]

    Now, all these transforms happen in world coordinate space by default. In order to do a transform in local, parent, or any other coordinate system, you preface your transform command with in coordSys some coordinate system:


    in coordSys local move $Box01 [10,0,0]

    moves Box01 10 units in Local X rather than World X.

    Automating with "for" Loops


    Let's move on to what is arguably the most important automation tool in MAXScriptthe for loop. As pseudocode:


    for variable in a list of things do something

    In other words, perform a task (or sequence of taskssomething can mean a whole list of commands, including other loops) on each member of a list. This is where the "automating repetitive tasks" aspect we keep talking about really takes off.


    for i in selection do (i.wirecolor = color 0 128 0)

    In this example, the variable is named i (this is arbitrary, but using i as the loop counter is a programming convention), the list of things is the keyword selection, which as you may have guessed means "the currently selected objects," and the something is to set one of the object's properties to a specific value, in this case the wireframe color to bright green. In other words, the pseudocode reads as follows:


    for (each object) in (the selection) do (set the wireframe color to green)

    See how that works?

    Now, one thing to keep in mind is that each time through the loop above, i serves as a placeholder for the object itself. So anything you know how to do to a single object (transform it, query a property, change a property, or any combination) you can now do to a whole list of objects automatically, by putting the command after do, and substituting i for $ or $objectName.

    The example above uses selection as the set to work on, but there are many other possibilities. These include predefined keywords (objects, geometry, lights, cameras, helpers, shapes, systems, sceneMaterials, meditMaterials, and spacewarps), wildcarded names (for i in $Box* do i.height = 10), numbers, or explicitly defined lists of objects called arrays (more on those later). Now that you have for in your arsenal, you can really start automating tedious tasks:


    for i in $*Omni* do (i.multiplier = i.multiplier*0.5)

    means "Dim all lights with Omni in their name by half."


    for i in selection do i.material = $Box01.material

    means "Set the material of all selected objects to Box01's material."

    You can also enter multiple commands on a single line in the do statement; surround it with parentheses and use a semicolon to separate the commands:


    for i in geometry do (i.motionblur = #image;i.motionBlurOn = true)

    means "Turn motion blur on and set the type to image motion blur for all scene geometry."

    Note

    One of the most common errors the Macro Recorder introduces to MXS code is its use of $ to refer to the currently selected object. Since $ stands for an actual object if a single object is selected, but for the selection keyword when multiple objects are selected, it is good coding practice to purge code you are going to reuse of references to $. One way to do this that works in many cases is to put the commands from the Macro Recorder inside for i in selection loops, and substitute i for $ in the Macro Recorder code within the loop.

    "where" and "classOf"


    Now let's try a for loop that doesn't work. Try a one-liner to turn off all the lights in your scene:


    for i in lights do i.on = false

    If your scene contains any targeted lights, you'll get an error similar to the following:


    -- Error occurred in i loop
    -- Frame:
    -- i: $Target:Spot01.Target @ [-11.212518,-15.219044,0.000000]
    -- Unknown property: "on" in $Target:Spot01.Target @ [-11.212518, -15.219044,0.000000]

    What the error message means, line by line, is the following:

    The code in the for loop using the variable i had a problem.

    If this had been an animation problem, the frame number would go here.

    When the problem occurred, i represented the target object Spot01.Target at these coordinates.

    The problem was that there was no "on" property to set on the object.

    In other words, our problem is that by using the keyword lights, we included the light target objects as well, and MAXScript complained and gave up because targets don't have an on/off switch. To do what we want, we need to exclude the target objects from processing. That's where where and classOf come in.

    where lets you filter a for loop's actions by inserting a true/false statement that is checked each time through the loop. If the statement is true for that iteration of the loop, the code in the loop executes. If false, that iteration is skipped. In its purest form,


    for i in selection where (false) do something

    does nothing, while:


    for i in selection where (true) do something

    acts on everything in the selection.

    To make this useful, though, we want to put an expression after the where that evaluates to a true/false, or Boolean, value. And for that we need some operators that test truth. There are several ways to compare two values (Tables 6.1 and 6.2).

    Table 6.1. Numeric Comparison Operators

    Symbol

    Means

    Example

    Example Output

    ==

    Is equal to

    1 == 2

    false

    1 == 1

    true

    !=

    Is not equal to

    1 != 2

    true

    1 != 1

    false

    >

    Is greater than

    1 > 2

    false

    2 > 1

    true

    <

    Is less than

    1 < 2

    true

    2 < 1

    false

    >=

    Is greater than or equal to

    1 >= 2

    false

    1 >= 1

    true

    2 >= 1

    true

    <=

    Is less than or equal to

    1 <= 2

    true

    1 <= 1

    true

    2 <= 1

    false

    Table 6.2. Boolean (Truth) Operators

    Operator

    True If

    Example

    Example Output

    and

    Both are true

    false and false

    false

    true and false

    false

    true and true

    true

    or

    Either or both are true

    false or false

    false

    true or false

    TRue

    true or true

    true

    not

    Changes true to false and vice versa

    not true

    false

    not false

    TRue

    Let's try some examples:


    for i in $Box* where (i.height > 10) do i.height = 10

    means "Set all objects whose names start with Box that are taller than 10 units to a height of 10."


    for i in $Box* where((i.height > 9) and (i.height < 11)) do i.height = 10

    means "Set all objects whose names start with Box that are between 9 and 11 units tall to a height of 10."


    for i in geometry where (i.material == undefined) do i.isHidden = false

    means "Unhide all geometry that doesn't have a material assigned."


    for i in shapes where (i.baseobject.renderable == true) do i.name = "RS_" + i.name

    means "Add RS_ to the start of the name of all renderable splines (to make selection by name easier)."

    Note

    Spline objects' renderable property is accessed with object.baseobject.renderable to distinguish it from the renderable object property shared by all objects. The same goes for lights' duplicate-named castShadows property.

    But what about our broken code from the start of this section? We need a way to test what kind of object something is, so that we can plug it into our where clause: classOf.

    Every object in max belongs to a class, a category whose members share identical parameters and behave in the same way. Boxes are a class, as are spheres, and both classes are distinct from the editable mesh class. Take our beloved Box01, for example:


    classOf $Box01

    returns


    Box

    Like show, classOf can be used as a reference tool, except for class names rather than property names. Unlike show, the value it returns can also be used in a truth test expression:


    classOf $Box01 == Box

    returns true. To use it in a where clause, just add parentheses:


    for i in geometry where (classOf i == Box) do i.height *= 2

    which means, "Double the height parameter of all box primitives, regardless of name."

    One more thing about classOf before we debug our lighting script. If you add a modifier to the box, such as Bend:


    classOf $Box01

    returns Editable_mesh instead of Box. The class name reflects the current state of the object, not the one it was born with (if you deactivate the modifier, for example, classOf $Box01 will return Box once again). If you want to ignore all modifiers when checking class, use:


    classOf $Box01.baseobject

    which looks only at the bottom entry in the modifier stack and returns Box as before.

    Turning Off the Lights


    Let's debug our previous failed turn-off-all-lights script command using where and classOf. Previously we tried:


    for i in lights do i.on = false

    which bailed because it couldn't set the on property for target objects. To get around this error, we want to change the command so that it skips over targets. This technique turns out to be essential when doing anything that batch-processes lights or cameras.


    1.

    Reset 3ds max.

    2.

    Create a few lights in the scene, including at least one Target Spot or Target Direct light.

    3.

    Select one of the light targets in the viewport, and type classOf $ in the Listener. Max responds with a class name of Targetobject.

    4.

    Now that we know the class name, let's try a truth test just to be sure:


    classOf $ == Targetobject

    returns true. Now we know what to put inside our where statement to filter out targets.

    5.

    Type in the Listener:


    for i in lights where (classOf i != targetobject) do i.on = false


    All the lights turn off, and MAXScript doesn't complain a bit.

    Now the power of for is really unleashed for scene management. You can conditionally change properties for thousands of objects at a time, if needed. Here are a few highly useful sentences to get you started:


    for i in cameras where (classOf i != targetObject) do i.mpassenabled = true

    means "Turn Multi-Pass Effects on for all cameras."


    for i in geometry where (i.material == $Box01.material) do i.isHidden = false

    means "Unhide all objects sharing Box01's material." This gets around the limitations of the Material Editor's Select by Material button, which only considers visible objects.


    for i in geometry where i.material == undefined do print i.name

    means "Output the names of all objects with no material assigned."


    for i in lights where (classOf i != targetobject) do i.multiplier *= 1.05

    means "5% brightness increase on all lights in the scene."

    [View full width]

    for i in lights where (classOf i == targetSpot) do (i.showCone = i.showNearAtten = i
    .showFarAtten = off)

    means "Turn off Show Cone and Show Attenuation Ranges for all targeted spotlights."


    for i in shapes where (i.baseobject.renderable == true) do (i.isHidden = false;selectMore i)

    means "Unhide and select all renderable splines."

    Doubtless you have a number of least-favorite chores of your ownstart using that show command to add your own.

    "random" Numbers


    Producing high-quality 3D imagery often means overcoming the tendency of the computer to make things look too perfect and orderly. Scenes that are too regular, ordered, or idealized break the illusion, and many of our most effective techniques involve "dirtying up" our scenes in some way. Disorder added by hand has a way of not looking random enough, however. If you've ever done any particle work, you know that particle systems' controls for adding randomness are the key to making a believable effect. The MXS command random is a fantastic tool for adding chaos to other parts of your scene (in a carefully controlled manner, of course!). You use it with the expression:


    random value1 value2

    where the two values are of the same type, and they define the upper and lower limits of the range of possible values.

    We're now going to use your burgeoning MXS vocabulary to add some chaotic believability to a scene.

    Adding Chaos to a Scene with "for" and "random"



    1.

    On the DVD, open the file cafe_start.max. It's a scene of some instanced café tables and chairs, arranged with robotic precision.

    Note

    This scene has been set up so that the Listener is docked to a viewport. To do this in your own scenes, right-click the viewport label to open the Viewport Right-Click menu and choose Views > Extended > MAXScript Listener.

    2.

    Render the camera view as is (Figure 6.2), then load the render into channel A of the RAM Player using the button with a teapot icon. The changes we're making are going to be subtle, and we'll want to be able to do a close comparison of the before and after.

    Figure 6.2. The cafe looks nice, but it's too orderly. (Furniture models courtesy of Scott Onstott at www.scottonstott.com.)

    3.

    First, let's make sure the tables don't all have their feet aligned. Activate the Listener viewport and type the following:


    for i in $*Table* do rotate i (eulerAngles 0 0 (random -180 180))

    The pseudo-code for the line above is this:


    for every object with 'table' in its name, rotate the object a
    random amount in Z.

    Let's take a closer look at that part at the end of the line. MAXScript treats every line of code as an expression. This means that every line ends up evaluating to a value. It also means that any component part of a line can be its own expression, which, as long as it evaluates to the kind of value MXS is expecting, works the same as if a simple value were there.

    When MAXScript gets to the random statement, it simply fills in the resulting value in that spot in the code. The parentheses help guide the interpreter as to what gets evaluated first, just like in a code-free mathematical expression: 2 + 3 * 2 = 8, but (2 + 3) * 2 = 10.

    Now let's shift each group of tables around on the floor a little. In order for the chairs to stay properly positioned relative to the tables, they'll have to travel together. It's possible, with a larger vocabulary and more code, to do this with MAXScript directly, but we're sticking to quick and dirty in this exercise, and trying to spend less time coding than we would have spent clicking.

    4.

    Select each group of four chairs, and link them to the nearest table with max's Select and Link tool.

    5.

    Once the chairs are linked to the tables, type into the Listener:


    for i in $*Table* do move i [(random 5 5),(random 5 5),0]

    The tables are now a little less excruciatingly organized. As in the previous line of code, we're using expressions for part of the coordinatesthe X and Y components in this case (Z stays 0 because we want everything to remain on the floor).

    It's also worth examining why we're using a for loop instead of simply typing the following:


    move $*Table* [(random 5 5),(random 5 5),0]

    If you run the above piece of code, the random expressions are only evaluated once, and the same random X and Y values are applied to all the objects in unison. By generating the random number within the for loop, on the other hand, a different random number is used each time through the loop, and the objects move different amounts relative to each other. There are times when you might want to have a random effect work either way, however, so keep in mind that you have the option.

    Now let's scramble the chairs a bit. We need to think a little bit harder about how we want them to move. On average, they'd probably be pulled out or pushed in more than they'd be displaced horizontally, but with four chairs surrounding each table, we need to use a move in local space for that to happen.

    6.

    As we saw previously, putting in coordsys local before a transform makes it occur in local space rather than world space. And so we type the following:


    for i in $*chair* do in coordSys local move i [(random -6 3), (random -1 1),0]

    and get exactly what we want. Local X is the push-pull direction of the chair, so we're adding a larger push-pull jitter that favors pulling the chairs out in local X, and a small, symmetrical side-to-side jitter in local Y. Z, as before, stays at 0.

    7.

    One last twiddle of the chairs, just like we did with the tables at the outset, and we're set:


    for i in $*chair* do rotate i (eulerAngles 0 0 (random -5 5))

    8.

    Render the camera view and load it into channel B of the RAM Player (Figure 6.3).

    Figure 6.3. The subtle chaos added to the scene makes it look more real.


    See the difference? That little bit of chaos has given the scene a lot more life, without altering the narrative truth of "four tables, each with four chairs, pushed in." It's just that now employees, rather than robots, did the organizing. Viewers don't see the difference, but they do feel it. And all it cost you was the time to type four sentences of MAXScript!

    The scene in its final state is on the DVD as cafe_01.max. If you were developing this scene further, you could take a similar approach to positioning the place settings on the tables, rotations and scale of flowers on the tables, and so on.

    Just a couple more words for your MAXScript vocabulary, and you'll find yourself using it like a Swiss army knife.

    #( ) and [ ]Working with Arrays


    An array is a list of data. Arrays are indicated by parentheses preceded by a number sign, and commas separate each element:


    myArray = #($Box01,$Box02,$Box03)

    The above code defines a variable named myArray as a placeholder for the list of three boxes. To access a member of the array, you type the array's name followed by an index number in square brackets. The number indicates the position in the array of the object you that want to access:


    myArray[2]

    returns something like:


    $Box:Box02 @ [6.800152,13.702519,0.000000]

    and:


    myArray[2].height

    accesses Box02's height parameter.

    A lot of built-in data structures are stored as arrays, and being able to access them by index number in this way makes them accessible to for loop processing. Instead of processing the array itself, though, it is often preferable to have the for loop iterate over a series of numbers like so:


    for i in 1 to 3 do myArray[i].height = i*10

    Each time through the loop, the value of i increases by 1, and so Box01's height is set to 10, Box02's to 20, and Box03's to 30. In other words, we just built a simple staircase generator in two lines of code.

    Using random and the built-in meditMaterials array, you can use this technique to randomly assign materials to a group of objects. Say you have a crowd shot to finish that uses textured planes for the individual people. You load your 12 people materials into the first 12 slots of the Material Editor, select the planes, and type the following:


    for i in selection do i.material = meditMaterials[(random 1 12)]

    Since (random 1 12) evaluates to a number, a random index is being generated each time through the loop, and thus a random material from the first 12 Material Editor slots is assigned to each plane in the selection.

    An object's modifier stack is an array as well, with modifiers[1] being the top modifier in the stack. When you don't know how long your array is going to be, you can use the array's count property to find out.


    for i in 1 to $.modifiers.count do $.modifiers[i].Enabled = true

    turns on all of a selected object's modifiers.

    When processing modifier stacks for multiple objects, it becomes necessary to go beyond a single line of code (actually it is possible in a single line, but it's harder to read and debug). To create multiline scripts, you need to open the MAXScript Editor rather than the Listener (MAXScript menu > New Script). To execute the Editor code, use the window's File menu > Evaluate All.


    for i in geometry where (i.modifiers.count > 0) do
    (
    for j in i.modifiers where ((classOf j == meshsmooth) or (classOf j == TurboSmooth)) do
    (
    j.useRenderIterations = true
    j.renderIterations = 3
    j.iterations = 1
    )
    )

    Can you see what's going on in the above code? The parentheses and line breaks are mostly for clarity's sake. They help you visually separate sections of your code. The first for loop is exactly what we've been using in our examples so farfor i in selection do something. The something in this case, though, is a second for loop, using j instead of i as the index variable. j is used because i already has a meaning within the loop, so we need to pick another variable name to avoid conflicts.

    In pseudo-code, our script reads like the following:


    for all geometry with at least 1 modifier do
    (
    for each of this object's modifiers that's a meshsmooth or turbosmooth do
    (
    turn on the modifier's render iterations
    set the modifier's render iterations to 3
    set the modifier's view iterations to 1
    )
    )

    The j loop looks at each object (represented by i) and puts each modifier into the value j. It then checks whether that modifier is a MeshSmooth or TurboSmooth, and if so, it performs a series of tasks: It checks the Render Iterations check box, sets the Render Iterations to 3, and sets the Viewport Iterations to 1.

    Note the two parentheses at the end as well. All parentheses have to balance out in order for code to execute properly. The practice of indenting your code at each parenthesis helps you keep track visually of whether your parentheses are properly closed.

    Note

    Pressing Ctrl-B in the MAXScript Editor will select all the text within the nearest set of parentheses or brackets, and pressing Ctrl-M in Text Pad will move the cursor to the nearest matching brackets. These can be valuable tools for fixing unclosed parenthesis bugs.

    "at time"Animating with MAXScript


    We've reached the last term you need for your basic vocabularycongratulations! With at time, you can use MAXScript to animate, as well as to change static parameters. The usage is the following:


    at time frame

    followed by any valid MAXScript. If (and only if) you have Auto Key turned on, this will cause your code's actions to set a keyframe.

    Note

    at time is equivalent to moving the time slider to a frame and then performing an action. If you don't have Auto Key turned on in the interface, your code will not set keyframes.

    In the following exercise, we'll use the power of at time and some MAXScript you already know to animate 20 objects in a hurry. When the specifics of the animation aren't critical (such as for background objects or for a large number of objects at once), animating with MAXScript can be a huge time-saver.

    Haunting the Cafe with "at time"



    1.

    Open the file cafe_haunted_start.max from the DVD. This is the same cafe you modified in the earlier exercise, but with a slight changeit's haunted.

    [View full width]

    for i in $*table* do
    for i in $*table* do
    (
    for j in 1 to 3 do
    (
    at time ((j*30) + random -10 10) i.position.z = (random 5 30)
    at time ((j*30) + random -10 10) rotate i (eulerangles (random -2 2) (random -2 2)
    (random -30 30))
    )
    )
    for i in $*chair* do
    (
    for j in 1 to 3 do
    (
    at time ((j*30) + random -10 10) in coordsys local move i [(random -2 2),(random -2
    2),(random -6 6)]
    at time ((j*30) + random -10 10) rotate i (eulerangles (random -2 2) (random -2 2)
    (random -5 5))
    )
    )

    3.

    Make sure that Auto Key is turned on.

    4.

    Execute the script using the MAXScript Editor's File > Evaluate All.

    5.

    Turn off Auto Key and play the animation. If you don't like the results, you can "Undo" twice and re-run the script to get different randomization.

    The script is really just an extended version of what we did to move the chairs and tables around in the previous exercise (nested for loops and random transforms), but extended into all three dimensions and across time. In pseudocode it reads as follows:

    [View full width]

    for all objects with "table" in the name
    (
    at about frames 30, 60, and 90 set a key for a Z position between 5 and 30
    at about frames 30, 60, and 90, set a rotation key up to 2 degrees in X and Y, and up
    to 30 degrees in Z
    )
    for all objects with "chair" in the name
    (
    at about frames 30, 60, and 90, set a key for a local move up to 2 units in x and Y,
    and up to 6 units in Z.
    at about frames 30, 60, and 90, set a key for a local rotation up to 2 degrees in X and
    Y, and up to 5 degrees in Z
    )


    Obviously, the choice of actual numbers to plug in is an aesthetic decision. The basic program structure above, however, is extremely flexible.

    Bear in mind, also, that unlike particle- or modifier-driven animation, this approach can serve as a starting point for manual keyframing. If you like the motion of three of the tables but not the last one, simply adjust the keys by hand. You'd be doing a lot more manual work if you hadn't used a scriptyou've saved work even if the output isn't perfect.

    With only a few words of MAXScript, we've managed to batch-process objects, randomize aspects of your scene, and even generate animation! Even if you don't go any deeper into scripting than this, it's still a versatile and powerful tool to have.


    • / 193