Sei sulla pagina 1di 135

MAXScript

MAXScript Listener
The MAXScript Listener window is an interactive interpreter for the MAXScript language and works similar to a DOS command prompt window.

Macro Recorder
The Macro Recorder generates script code in a separate pane of the MAXScript listener based on the actions of the user. Several filtering options are available that control what types of user actions are recorded, whether the generated MAXScript commands contain explicit object references or are selection-relative, and whether the generated MAXScript commands contain explicit or relative transforms and coordinates.
These options are set using the MacroRecorder menu in the Listener window.

Absolute Transform Assignments/Relative Transform Operations


if Absolute Transform Assignments is enabled,
$.position = [55.6739,23.5,0]

If Relative Transform Operations is enabled,


move $ [0,-47.8044,0]

Explicit Sub-object Sets/Selection-Relative Sub-object Sets


if Explicit Sub-object Sets is enabled
move $Sphere02.verts[#{20..32, 51..65}] [40.0986,10.3648,0]

If Selection-Relative Sub-object Sets is enabled


move $Sphere02.selectedVerts [40.0986,10.3648,0]

Learning MAXScript with the Macro Recorder

Customizing MacroScript Buttons


Position your mouse over the text in the macro pane and press the left mouse button. Drag up to the main toolbar. As the cursor reaches the toolbar, you will see a small plus sign at the bottom of the cursor, indicating that you can drop the MacroScript there.

To edit the script, right-click the icon in the toolbar, and choose Edit Macro Script from the right-click menu.

Source Code Layout


a+b*c/d-e+f*g/h This line can be broken after any of the operators. MAXScript continues to read the next line, because an operator needs another argument. a+b*c/d-e+ f*g/h You cannot break the example line as shown next a+b*c/d-e +f*g/h The previous example of an invalid break can be made valid using the line continuation character (backslash \) a+b*c/d-e\ +f*g/h

MAXScript also lets you combine multiple statements and expressions in a single line. The statements or expressions are separated with a ;
1+2; 2^3; sin 0

The Mini-Listener

Using the '?' Symbol


You can use the ? anywhere you normally use a variable name and MAXScript evaluates the ? as the last Listener result. x=?

include
include <filename_string> :Inserts the specified files content into the Listener output pane. The inserted text is not evaluated. Examples:
include "my_script.ms"

scriptfile="c:\\my_scripts\\my_script.ms" include scriptfile

You can open a MAXScript Editor window from within the Listener by calling the edit() method. Examples:
edit "my_script.ms

You can create an empty MAXScript Editor window from within the Listener or from other running scripts by calling the newScript() method.

Running Scripts
To run an existing script file, press "Run Script" . You can also run a script from Listener or from within other scripts using the fileIn() method Examples:
fileIn "my_script.ms"

Variables and Data


Number Data Types There are two kinds of number data types: integers and floats. x = 5.0 y = 6.0 z=x*y

String Data Types msg = "File not found." messagebox msg

Boolean Data Types ready = true ready = off

Untyped Variables 3dsMax automatically allows you to manipulate the variable according to the rules associated with that data type. For example, you can legally write the following: msg = "File not found." messagebox msg msg = 5.6 z = msg + 7.0 When assigned a string, 3dsMax lets you manipulate msg as a string. Later, when msg is assigned a number, you can manipulate msg as a number.

Entering Arrays in MAXScript


#() This is the most basic type of array: an empty one. #( <expr> , <expr> ) Array1= #(5, pi, "String", 6+2^3)
output #(5, 3.14159, "String",14)

Array1[3]
Append Array1 30

finditem Array1 14 4 deleteitem Array1 4 #(5, 3.14159, "String", 30)


Array2 = #{2,4,10,20,30} array3 = for i in Array2 where i> 10 collect i #(20, 30)

Transformation matrix
t = teapot() t.transform // output (matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0]) // the transformation matrix is like in the XNA ( the transpose of the transformation matrix in OpenGL)

// translate by 10 units along the x-axis t.transform = (matrix3 [1,0,0] [0,1,0] [0,0,1] [10,0,0])

// read only properties t.transform.translation // output [10,0,0]

t.transform.rotation // output (quat 0 0 0 1)


t.transform.scale // output [1,1,1]

$.transform = rotateZMatrix 45 $.transform = $.transform * rotateYMatrix 45

Scatter objects to vertex normals


delete $*_Clone* theTarget = $Teapot01 theSource = $Teapot02 theMesh = snapshotAsMesh theTarget for v = 1 to theMesh.numverts by 1 do ( theNewObject = instance theSource theNewObject.name = uniquename (theSource.name + "_Clone") theNewObject.transform = matrixFromNormal (getNormal theMesh v) theNewObject.pos = getVert theMesh v theNewObject.parent = theTarget )

Aligning nodes using the transformation matrix


theSource = $Teapot01 theTarget = $Teapot02 theTarget.transform = theSource.transform // copy the orientation and scale but do not copy the position theSource = $Teapot01 theTarget = $Teapot02 theTm = theSource.transform theTm.row4= theTarget.transform.row4 theTarget.transform = theTm

$.bend.gizmo.transform = rotateXMatrix 90 //change the transformation matrix of the gizmo $.uvw_mapping.gizmo.transform = rotateYMatrix 45

Align Slice Plane To Scene Plane


( thePlane = $Plane01

for theObj in selection where superclassof theObj == GeometryClass and classof theObj != TargetObject and theObj != thePlane do ( theMod = theObj.modifiers[#slice] if theMod == undefined do ( theMod = SliceModifier slice_type:2 addModifier theObj theMod addModifier theObj (Cap_Holes()) ) theMod.slice_plane.transform = thePlane.transform * inverse theObj.transform )
)

AnimatingSlicePlaneWithScenePlane
( thePlane = $Plane01 for theObj in selection where superclassof theObj == GeometryClass and classof theObj != TargetObject and theObj != thePlane do ( theMod = theObj.modifiers[#slice] if theMod == undefined do ( theMod = SliceModifier slice_type:2 addModifier theObj theMod ) theMod.slice_plane.controller = transform_script() txt = "dependsOn $'" + thePlane.name + "'\n" txt += "$'" + thePlane.name+"'.transform * inverse $'"+ theObj.name +"'.transform\n" theMod.slice_plane.controller.script = txt )

( theObj = $Plane01 thePolyIndex = 6 theVerts = polyop.getVertsUsingFace theObj thePolyIndex theTM = matrixFromNormal (polyop.getFaceNormal theObj thePolyIndex) theRotTM = (rotateZMatrix 1.0) * theTM for t=1 to 360 do ( for v in theVerts do ( in coordsys theTM theVert = polyop.getVert theObj v theVert *= theRotTM polyop.setVert theObj v theVert ) redrawViews() ) )

Rotate one polygon in a plane (editable Poly) around the normal

Camera
// select all the geometry objects in front of the camera theTM = inverse $camera01.transform select ( for o in geometry where (o.pos * theTM).z < 0 collect o)

// select all the geometry objects in front of the camera theTM = $camera01.transform select ( for o in geometry where in coordsys theTM o.pos.z < 0 collect o)

// select all the geometry objects in front of the camera select ( for o in geometry where in coordsys $camera01 o.pos.z < 0 collect o)

Viewport transformation matrix


First you have to create a camera inverse (viewport.getCamera()).transform You will get the same result if you get the transform matrix of the viewport viewport.getTM() //set the transformation matrix of the selected viewport viewport.setTM(inverse $Camera01.transform) //create a teapot in a position [0,0,-100] from the current viewport teapot pos:([0,0,-100] * inverse (viewport.getTM()))

Objects
for o in objects do print o
$Box:Box01 @ [-15.102085,1.018532,0.000000] $Sphere:Sphere01 @ [-13.588623,-53.728287,0.000000] $Cylinder:Cylinder01 @ [62.862854,-5.142868,0.000000] $Circle:Circle01 @ [-60.000847,-29.512146,0.000000] $Target_Light:TPhotometricLight01 @ [20.313614,-60.486160,0.000000] $Target:TPhotometricLight01.Target @ [30.839767,-52.193558,0.000000] $Free_Camera:Camera01 @ [54.398849,41.417953,0.000000]

for o in geometry do print o


$Box:Box01 @ [-15.102085,1.018532,0.000000] $Sphere:Sphere01 @ [-13.588623,-53.728287,0.000000] $Cylinder:Cylinder01 @ [62.862854,-5.142868,0.000000]

for o in selection do print o


$Free_Camera:Camera01 @ [54.398849,41.417953,0.000000] $Cylinder:Cylinder01 @ [62.862854,-5.142868,0.000000]

for o in selection do print o.name


"Camera01" "Cylinder01"

Assigning Variables
mystring = "This is my string." MAXScript doesnt distinguish between lowercase and uppercase variable names
because MAXScript names are not casesensitive.

If you need to locate the source of a scripted function, you can use the showSource() function.
It displays a script Editor containing the source file in which the function was defined and positioned at the function's definition.

Mathematical Operations
random 1 100
78

random 1.0 100


17.6577

Incrementing
x=x+1 x += 1

Examples
-- numbers test bed i=10 -- assign integers to variables j=20 i/j -- integer divide, result is integer i=i as float -- convert i to a float i/j -- float divide, result is float i += 1 -- increment i by 1 if i < j do i=2.^3 -- if i is less than j, set i to 2 to the 3rd power mod j i -- return remainder of j divided by i cos 30.0 -- return cosine of 30 degrees sqrt j -- return square root of j seed 12345 -- seed the random number generator for k=1 to 5 do print (random i j) -- print out 5 random numbers

10 -- result of line 2 20 -- result of line 3 0 -- result of line 4 (10/20) 10.0 -- result of line 5 0.5 -- result of line 6 (10./20) 11.0 -- result of line 7 8.0 -- result of line 8 (2.^3) 4.0 -- result of line 9 0.866025 -- result of line 10 4.47214 -- result of line 11 (sqrt 20) OK -- result of line 12 10.7775 -- output from line 13 (random 8. 20) 15.0183 17.4467 16.1027 10.1344 OK -- result of line 13

Color
Examples
magenta=color 255 255 0 255 -- create colors using constructors aqua = [0, 255, 255] as color aqua.v /= 2. -- reduce "strength" of aqua color aqua aqua.alpha=128 -- set aqua to 50% opacity aqua lightGray=white/4 -- create light gray color by dividing -- each component of white by 4 composite aqua lightGray -- composite light gray over aqua random black white -- generate a random color

(color 255 255 0) -- result of line 2. Note that if -- alpha=255 it is not displayed (color 0 255 255) -- result of line 3 127.5 -- result of line 4 - value property of aqua (color 0 127.5 127.5) -- result of line 5 - color of aqua 128 -- result of line 6 - alpha property of aqua (color 0 127.5 127.5,128) -- result of line 7 - color of aqua (color 63.75 63.75 63.75) -- result of line 8 - each component divided by 4 (color 31.75 159.25 159.25 159.75) -- result of line 10 (color 170.944 109.543 74.1957) -- result of line 11

RGB color box


( step = 25 for r = 0 to 255 by step do for g = 0 to 255 by step do for b = 0 to 255 by step do theBox = box width:step height:step length:step pos:[r,g,b] wirecolor:(color r g b) )

select $box* selectmore $sphere* //add to selection

Or select (join ($Box* as array) ($sphere* as array) ) Hide $box* Unhide $box* unhide $* // unhide all deselect $* select helpers

Select by type instead of name


thePoints = for o in helpers where classof o == Point collect o Select thePoints

select ( for o in geometry where o.radius < 20 and classof o == Teapot collect o)
It will give an error. Why? It evaluates o.radius < 20 first which will give an error if the object does not have a radius properties

We need to select objects that has radius <20 select ( for o in geometry where (classof o == Teapot or classof o == Sphere or classof o == Geosphere or classof o == Cylinder) and o.radius < 20 collect o)
Too long and we did not include all types

select ( for o in geometry where hasProperty o "radius" and o.radius < 20 collect o) Or select ( for o in geometry where try (o.radius < 20) catch (false) collect o)

select (for o in geometry where try (o.bend.angle > 90) catch(false) collect o)

select (for o in geometry where ((for b in o.Modifiers where classof b == Bend collect b).count > 0) collect o)

select (for o in objects where o.material == undefined collect o) select (for o in objects where o.material != undefined collect o)
$.material = standard()

select ( for o in objects where o.material == medit.GetCurMtl() collect o) medit.GetCurMtl() This method returns the material currently selected in one of the sample slots of the Material Editor.

if selection.count > 0 do select (for o in objects where o.material == selection[1].material collect o)

select (for o in objects where length o.pos < 100 collect o)


Length: calculate the distance between the original of the world and the pivot

select (for o in objects where length o.center < 100 collect o)


Center: bounding box center

Or
select (for o in objects where distance o.center [0,0,0] < 100 collect o)

select (for o in geometry where o.mesh.numfaces > 1024 collect o)


o.mesh.numfaces o.mesh.numverts

( a="hello world" -- create a string value, put reference in a b=a -- put strings reference in b format "a=%; b=%\n" a b -- and print their values b="thanks for the fish" -- create a string value, put reference in b format "a=%; b=%\n" a b -- print values - they are different b=a -- put strings reference from a in b a[1]="J" -- change a component value format "a=%; b=%\n" a b -- print values - they are the same a=b=[10,20,30] -- assign a point3 value reference to a and b format "a=%; b=%\n" a b -- and print their values a.x=-100 -- change a component value format "a=%; b=%\n" a b -- print values - they are the same )
Output a=hello world; b=hello world a=hello world; b=thanks for the fish a=Jello world; b=Jello world a=[10,20,30]; b=[10,20,30] a=[-100,20,30]; b=[-100,20,30] OK

Drawing a Box with MAXScript


box()
This creates a box with the default parameters.

mybox = box()
It is generally better practice to assign an object to a variable, so you can later refer to it by name and manipulate it using that name

mybox = box length:19 width:20 height:20 mybox = box width:20 length:19 height:20 The order does not matter
$Box:Box01 @ [0.000000, 0.000000, 0.000000]
The first part of the statement is called a pathname. The second part of the statement is the object name: Box01 The values inside the brackets represent the x, y, and z coordinates of the center of the box.

Change the name property of the box


mybox.name = "BlueBox"

Change color
mybox.wireColor = blue The color blue is a predefined color constant in MAXScript and its RGB value is defined as (0,0,255). The other predefined color variables are red, green, white, black, orange, yellow, and brown. Instead of using a predefined color, you can assign a color with different RGB values, such as:
mybox.wireColor = (color 255 0 255) mybox.wireColor.r = 255 newcolor = color 40 120 200 mybox.wireColor = newcolor newcolor = color 40 120 200 128 alphaNum = newcolor.a

Change Size
mybox.scale = [1.5,1.5,1.5] mybox.height = 40 mybox.width = 60

showclass "box.*"
prints information about a specified 3ds Max class or classes. Box : GeometryClass {10,0} .height : float .length : float .lengthsegs : integer .width : float .widthsegs : integer .mapcoords : boolean .heightsegs : integer OK

mybox.lengthsegs = 10 mybox.widthsegs = 10 mybox.heightsegs = 10

showClass "box*" -- all 3ds max classes starting box showClass "box.*" -- all the accessible properties of the -- box class showClass "*:mod*" -- all the modifier classes showClass "*.*rad*" -- all the classes with a property name -- containing rad

The second inspector function is showProperties().


The showProperties() function is used to display the properties for a specific object that is an instance of one of the 3ds Max classes. It can be used in place of showClass() in situations where you have an actual object in hand. showProperties myBox

torus radius1:10 pos:[0, 0, 20] wirecolor:[225, 230, 100] s = sphere radius:100.0 s = sphere() s.radius = 10.0 for i = 1 to 3 do (
sphere pos:[random -80 80, random -80 80, random -80 80] cone pos:[random -80 80, random -80 80, random -80 80] cylinder pos:[random -80 80, random -80 80, random -80 80]

rot_box = eulerangles 0 30 0 rotate mybox rot_box


Creates an object called rot_box, which will rotate an object around the Y-axis by 30 degrees.

Moving, scaling, or rotating objects, or setting transform-related properties operates in the World Coordinate System, by default.
-- randomly jiggle each object in the selection around +/- 20 units in coordsys local selection.pos = random [-20,20,20] [20,20,20]

Change properties for a group of objects


for i = 1 to 200 do sphere pos:(random [-300, -300, -300] [300,300,300]) radius:(random 2 40)

for o in $Sphere* do o.radius = 10 Or $sphere*.radius = 20 // faster

Assign instance of an animation controller to multiple objects


Change controller will affect multiple objects Example:
Select some boxes $.height.controller = bezier_float() Change the height parameter of any of the boxes will change the rest. Change any other parameter such as width will not affect the rest of the boxes

$.heightsegs.controller = bezier_float()

Select some boxes

$.baseobject = $Cylinder01.baseobject You will note that the boxes change to cylinder

Creating and Assigning Modifiers


addModifier mybox (twist angle:30) Changing a Modifier
mybox.twist.angle = 60 for o in selection do addModifier o (bend()) for o in selection do for x in o.modifiers where classof x == Bend do x.angle = 45

Add a modifier as an instance to some objects


Change the modifier will affect these objects theMod = bend angle:90 for o in $ do addModifier o theMod

for o in objects do for m in o.modifiers do m.enabled = false for o in objects do for m in o.modifiers do m.enabledInViews = false
Affect Views only (not render)

for o in objects do for m in o.modifiers where classof a == Bend do m.enabled = not m.enabled for o in objects where classof o.baseobject == Box do for m in o.modifiers where classof a == Bend do m.enabled = not m.enabled

deleteModifier $ 1
1 = the top of the modifier stack

Animating the Box


animate on ( at time 0 (mybox.pos = [-100, 0, 0]; mybox.scale = [1, 1, 0.25]) at time 100 (mybox.pos = [100, 0, 0]; mybox.scale = [1, 1, 3]) )

In this example, the time was given as a simple number. If no unit is specified, MAXScript interprets this as a frame number. You can also use one of the various time literals in the following manner:
2.5s -- 2.5 seconds 20f -- 20 frames 4800t -- 4800 ticks = 1 sec 1m3s5f -- 1 min, 3 seconds, 5 frames 1:15.5 -- SMPTE: 1 min, 15 seconds, 5 frames

Note:
All times are automatically converted to frames.

theBox = box width:10 length:10 height:10 animate on ( at time 20 theBox.height = 100; at time 30 theBox.height = 10 ) for y=0 to 9 do for x=0 to 9 do (copy theBox).pos = [20*x, 20*y,0] delete theBox for o in $Box* do moveKeys o.height.controller (random 0 30) //moveKeys moves all keys

theBox = box width:10 length:10 height:10 animate on ( at time 20 theBox.height = 100; at time 30 theBox.height = 10 ) for y=0 to 9 do for x=0 to 9 do (copy theBox).pos = [20*x, 20*y,0] delete theBox for o in $Box* do moveKey o.height.controller 2 (random 0 10)

theBox = box width:10 length:10 height:10 animate on ( at time 20 theBox.height = 100; at time 30 theBox.height = 10 ) for y=0 to 9 do for x=0 to 9 do (copy theBox).pos = [20*x, 20*y,0] delete theBox theOffset = 0 for o in $Box* do moveKeys o.height.controller (theOffset +=1)

Materials
$.material = standard() $.material = standard diffuse:yellow $.material = standard diffuse:(random black white) $.material = undefined

$.material = standard shift = true for z=0 to 9 do (shift = not shift; for x=0 to 9 do (copy $Box01).pos = if shift then [20*x,0,10*z] else [10+20*x,0,10*z]) for m = 1 to 4 do meditmaterials[m] = standard diffuse:(#(red, blue, white, yellow)[m]) name:("L"+m as string) $.material = undefined

Save
saveNodes $ "temp/test1" // save the selected objects into test1.max select geometry saveNodes $ "temp/test1 select shapes saveNodes $ "temp/test1 select helpers saveNodes $ "temp/test1

select lights

saveNodes $Box* (GetDir #scene + "/ALLBoxes") // GetDir allows you to access the 3ds Max system directories loadMaxFile (GetDir #scene + "/ALLBoxes.max") // you have to put the extension maxFileName // output = ALLBoxes.max maxFilePath //output = "C:\Users\Dr. Khaled Fatehy\Documents\3dsmax\scenes\" thePath = maxFilePath + "Objects" makeDir thePath for o in objects do saveNodes o (thePath + "/"+o.name+".max") //save every single object in a separate max file shellLaunch "Explorer.exe" thePath //run programs

makeDir (thePath = maxFilePath+"SingleObjectsExport") for o in geometry do (select o; exportFile (thePath +"/"+o.name+".3DS") #noPrompt selectedOnly:true )

theFiles = getFiles (thePath + "/*.3ds") for f in theFiles do importFile f #noPrompt

Rays
theRay = Ray [0,0,100] [0,0,-1] theIntersect = IntersectRay $Sphere01 theRay // theIntersect ray: // The start point = the point of intersect //the direction of that ray is the normal at the point of intersect

if theIntersect != undefined do p = point pos:theIntersect.pos //create helper point at the point of intersect

IntersectRay
for o in $Box* do ( o.height.controller = float_script() txt = "dependsOn $Sphere01.pos.controller\n" txt +="theInt = IntersectRay $Sphere01 (Ray $" + o.name +".pos [0,0,1])\n" txt +="if theInt != undefined then \n" txt +="if (theDist = length (theInt.pos - $" + o.name +".pos)) > 100 then 100 else theDist\n" txt +="else 100\n" o.height.controller.script = txt ) // dependsOn was used with scripted controllers in versions prior to 3ds Max 8 to enable interactive update of scripted controllers when the code in them depends on other objects in the scene.

IntersectRayEx (Explore Mesh)


( local theMesh fn filterMesh obj = classof obj == Editable_Mesh rollout IntersectWithMesh "Intersect With Mesh" ( timer timerControl interval:50 active:false pickbutton pickMesh ">>Pick Mesh To Explore" width:380 filter:filterMesh edittext faceHit "Face:" edittext normalHit "Normal:" edittext worldHit "World:" edittext baryCoords "Barycentric:"

on pickMesh picked obj do ( if obj != undefined do ( theMesh = obj pickMesh.caption = obj.name timerControl.active = true ) )

on timerControl tick do ( theRay = mapScreenToWorldRay mouse.pos theInt = IntersectRayEx theMesh theRay if theInt != undefined then ( faceHit.text = theInt[2] as string normalHit.text = theInt[1].dir as string worldHit.text = theInt[1].pos as string baryCoords.text = theInt[3] as string ) else baryCoords.text = faceHit.text = normalHit.text = worldHit.text = "???" ) ) createDialog IntersectWithMesh width:400 )

IntersectRayEx (Explore Texel)


( local theMesh, theBitmap, theBitmapWidth, theBitmapHeight fn filterMesh obj = classof obj == Editable_Mesh rollout IntersectWithMesh "Intersect With Mesh" ( timer timerControl interval:50 active:false pickbutton pickMesh ">>Pick Mesh To Explore" width:380 filter:filterMesh edittext faceHit "Face:" edittext normalHit "Normal:" edittext worldHit "World:" edittext baryCoords "Barycentric:" colorpicker pixelColor "Pixel Color"

on pickMesh picked obj do ( if obj != undefined do ( theMesh = obj pickMesh.caption = obj.name timerControl.active = true if theMesh.material != undefined and classof theMesh.material.diffusemap == BitmapTexture do ( theBitmap = openBitmap theMesh.material.diffusemap.filename theBitmapWidth = theBitmap.width-1 theBitmapHeight = theBitmap.height-1 ) ) )

on timerControl tick do ( theRay = mapScreenToWorldRay mouse.pos theInt = IntersectRayEx theMesh theRay if theInt != undefined then ( faceHit.text = theInt[2] as string normalHit.text = theInt[1].dir as string worldHit.text = theInt[1].pos as string baryCoords.text = theInt[3] as string if theBitmap != undefined do ( theMapFace = meshop.getMapFace theMesh 1 theInt[2] theMapVert1 = meshop.getMapVert theMesh 1 theMapFace.x theMapVert2 = meshop.getMapVert theMesh 1 theMapFace.y theMapVert3 = meshop.getMapVert theMesh 1 theMapFace.z

theUVCoords = theMapVert1* theInt[3].x + theMapVert2* theInt[3].y + theMapVert3* theInt[3].z thePixels = getPixels theBitmap [theUVCoords.x * theBitmapWidth , theBitmapHeight - theUVCoords.y * theBitmapHeight ] 1 if thePixels[1] != undefined then pixelColor.color = thePixels[1] else pixelColor.color = red ) ) else baryCoords.text = faceHit.text = normalHit.text = worldHit.text = "???" ) ) createDialog IntersectWithMesh width:400 )

Controlling Program Flow


Conditional Statements
If-Then Statements Logical Operators: Not, And, Or If-Then-Else Statements

Loop Structures
For Loop
Loops with Multiple Statements

While Loops

if mybox.height == 10 then mybox.width = 20 if mybox.height == 10 then mybox.width = 20 else mybox.width = 10 if b.height == 10 then b.width = 20 else b.width = 10 for i = 1 to 5 do
( box_copy = copy mybox box_copy.pos = [i * 50, 0, 0] )

To make MAXScript increment the index by a value other than 1, insert by x for i = 1 to 5 by 2 do
( box_copy = copy mybox box_copy.pos = [i * 50, 0, 0] )

array1 = for i = 1 to 5 collect i output #(1, 2, 3, 4, 5) The collect statement in the for loop above tells MAXScript to collect the results of each iteration in an array, rather than just calculate them. In this case, you only asked MAXScript to collect the value of the index; however, you can collect iterations of any expression.

For loops can also perform iterations with the contents of an array for i in array1 do print i
Output
1 2 3 4 5

In this example you used in instead of =; they are both acceptable for i = 1 to array1.count do print array1[i]

x=10 while x>0 do print (x-=1) Returns:


9 8 7 6 5 4 3 2 1 0 0

MAXScript automatically returns the final value of the loop to the Listener. Therefore, the final value is returned twice.

x=10 do print (x-=1) while x<10

Function Example
fn uppercase instring = -- beginning of function definition ( local upper, lower, outstring -- declare variables as local upper="ABCDEFGHIJKLMNOPQRSTUVWXYZ" -- set variables to literals lower="abcdefghijklmnopqrstuvwxyz" -- create an unique copy of the string referenced by instring, and store -- reference to unique copy in outstring outstring=copy instring --- increment from 1 to number of character in string for i=1 to outstring.count do ( j=findString lower outstring[i] if (j != undefined) do outstring[i]=upper[j] ) outstring -- value of outstring will be returned as function result ) -- end of fn uppercase

s1="AbCdEfGh" -- set variable to literal s2=uppercase s1 -- call function uppercase, passing s1 as parameter if s1 == s2 do print "strings s2 and s3 are the same" -- compare strings if s1 != s2 do print "strings s1 and s2 are different" -theObject="sphere" -- set variable to literal theRadius= (random 10. 100.) as string -- convert number to string -- concatenate strings and execute string myObject=execute (theObject + " radius:" + theRadius)

Output: uppercase() -- result to function definition "AbCdEfGh" -- result of line 24 "ABCDEFGH" -- result of line 25 undefined -- result of line 26 - strings not equal, -- and no else expression in if expression "strings s1 and s2 are different" -- output from line 27 "strings s1 and s2 are different" -- result of line 27 "sphere" -- result of line 29 "75.4091" -- result of line 30 $Sphere:Sphere01 @ [0.0,0.0,0.0] -- result of line 32. Execute function -- created a sphere object

Functions
Function Parameters Positional Parameters (Function Parameters in Specific Order) Keyword Parameters (Function Parameters in Any Order) Function Parameters in Specific Order with Optional Keyword Parameters Passing Arguments
Passing Arguments by Value Passing Arguments by Reference Exceptions to Pass by Value

Returning Values from Functions

Positional Parameters
The order of arguments is important function SubNums x y = ( z = (x - y) ) a = SubNums 1 3

Keyword Parameters
Function Parameters in Any Order b1 = box length:20.5 width:15.0 height:5.6 b2 = box height:5.6 length:20.5 width:15.0 function putUpMessage text1:"File Not found." = ( messageBox text1 title: Warning beep: true ) s = sphere() msg1 = "The sphere's radius is " msg2 = s.radius as string putUpMessage text1:(msg1 + msg2)

Function Parameters in Specific Order with Optional Keyword Parameters


You can also mix positional parameters with keyword parameters. The function definition in this case must use the positional parameters first, followed by the keyword parameters.
function mysphere rad position:[0, 0, 0] = ( sphere radius:rad pos:position )

Passing Arguments by Value


m = 30.4 n = 23.0 function addnums2 x y = ( x = 10.0 x+y ) addnums2 m n

The Listener returns a value of 33.0. Type m in the Listener and evaluate
The result for the value of m is 30.4.

Passing Arguments by Reference


m = 30.4 n = 23.0 function addnums2 &x y = ( x = 10.0 x+y ) addnums2 &m n

The Listener returns a value of 33.0. Type m in the Listener and evaluate.
The result is 10.0.

Exceptions to Pass by Value


When a variable that has sub-properties is passed to a function,
the pointer or variable of the object is passed by value. However, the sub-properties of the object are passed by reference.

Exceptions to Pass by Value (Cont.)


myPoint = point3 10 20 30 function modifyPoints pnt = ( pnt.x = 3 pnt = point3 7 14 21 ) modifyPoints myPoint

The Listener returns a point3 literal of [7, 14, 21], Type myPoint in the Listener and evaluate.
The result is [3,20,30].

Returning Values from Functions


function foo = ( g=4 h=5 g*h } Then if you type i = foo(), the Listener will return 20.

function foo = ( g=4 h=5 return g*h ) Using the return keyword is a little slower to use. If the function is called many times, omit the keyword return, thereby reducing execution time.

function add a b = a + b fn factorial n = if n <= 0 then 1 else n * factorial(n - 1) function rand_color x = x.wireColor = random (color 0 0 0) (color 255 255 255)

fn starfield count extent:[200,200,200] pos:[0,0,0] = ( local f = pos - extent / 2, t = pos + extent / 2 for i = 1 to count do sphere name:"star" \ radius:(random 0.1 2.0) \ position:(random f t) \ segs:4 smooth:false )

Local and Global Variables


global rad = 10
"rad" is a global variable, it can be used throughout the rest of the script.

for i = 1 to 3 do
( local rad = 10 )

Local variables can only exist within a block. For this reason, they must be created in a block. If you try to define a local variable from the top level of MAXScript, you will receive an error message.

It is not necessary to declare every variable with a "global" or "local" statement. You learned previously that you can implicitly create a variable by simply assigning a value to it. If you do not make the "global" or "local" distinction upon your variable, MAXScript will create the appropriate association, depending on where the variable is defined in the script. For example if you create a variable inside a block, MAXScript automatically treats it as a local variable. On the other hand, any variable created at the top level of MAXScript (outside of all blocks) is always a global variable.

for i = 1 to 5 do ( global rad = 10 cyl_height = 25 c = cylinder() c.radius = rad c.height = cyl_height c.pos.x = i * 25 ) s = sphere radius:rad c and cyl_height are both local variables. The other two variables are both global;
s, because it was created outside of a block, and rad, because it was declared as a global variable.

function mycube side position:[0, 0, 0] = ( box length:side width:side height:side pos:position ) This function requires you to enter the size of the cube, but the position is optional.

Structure Definitions
Create a new structure, called person:
Struct person (name, height, age)

Now create an instance of this structure using the person constructor:


Bill = person name:"Bill" height:72 age:34
Joe = person name:"Joseph"
this person does not have initialized values for the height and age members. Since you did not provide these members with an optional default value in our structure definition, they will default to a value of undefined.

max Commands in MAXScript


MAXScript scripts can invoke 3ds Max menu and toolbar commands.
max file open max unhide all max quick render

The available commands can be displayed by using the "?" character in a partial max command.
max time ? -- shows all the time-related commands max sel ? -- shows all the commands with sel in them as a substring max ? -- shows all the commands (there are a lot)

Diffuse Shading
( st = timestamp() theObjects = for o in geometry where not o.isHidden and classof o != TargetObject collect o meshSel = mesh_select() for o in theObjects do addModifier o meshSel theLights = for o in lights where classof o != TargetObject and o.on collect o theView = getViewSize() theBitmap = bitmap theView.x theView.y color:backgroundColor theBitmap.filename = "RayScript - " + theView as string + " - "

fn fireRay theViewRay = ( lastSurfaceColor = backgroundColor lastSurfaceColor.a = 0 for o = 1 to theObjects.count do ( theObj = theObjects[o] theDiffuseLighting = black theInt = intersectRayEx theObj theViewRay if theInt != undefined then ( for aLight in theLights do ( theLightRay = normalize (aLight.pos - theInt[1].pos ) theLightAngle = dot theInt[1].dir theLightRay if theLightAngle > 0 do theDiffuseLighting += aLight.rgb * aLight.multiplier * theLightAngle ) lastSurfaceColor = theObj.wirecolor * theDiffuseLighting ) ) lastSurfaceColor )

for y = 0 to theView.y-1 do ( for x = 0 to theView.x-1 do ( theSurfaceColor = fireRay (mapScreenToWorldRay [x,y]) if theSurfaceColor.r > 255 do theSurfaceColor.r = 255 if theSurfaceColor.g > 255 do theSurfaceColor.g = 255 if theSurfaceColor.b > 255 do theSurfaceColor.b = 255 if theSurfaceColor.a > 0 do theSurfaceColor.a = 255 setPixels theBitmap [x,y] #(theSurfaceColor) )--end x loop display theBitmap )--end y loop for o in theObjects do deleteModifier o meshSel pushprompt ("RayScript: " + ((timestamp() - st)/1000) as string + " sec.") )--end script

Rollin
rollout rollin "Rollin'" ( group "Main settings" ( pickbutton selobj "Select object" label lab_rotaxis "Rotation axis: " across:2 radiobuttons rot_axis labels:#("X","Y","Z") default:3 align:#left across:1 spinner obj_radius "Radius: " type:#float range:[1,1000,48] fieldwidth:45 align:#left checkbox detect "Detect radius" width:140 enabled:false button center "Pivot to center of mass" width:140 enabled:false checkbox reverse "Reverse direction" align:#left

spinner calc_start "Calculation start: " type:#integer range:[1,1000,1] fieldwidth:47 align:#right spinner calc_end "Calculation end: " type:#integer range:[2,1000,200] fieldwidth:47 align:#right spinner step "Every Nth frame: " type:#integer range:[1,10,2] fieldwidth:47 align:#right button go "GO!" enabled:false )

on rollin open do ( global xvector, yvector,zvector global xradius, yradius,zradius xvector = [0,1,1] yvector = [1,0,1] zvector = [1,1,0] )

on selobj picked obj do ( if (superClassOf obj == geometryClass) then ( global animated_object animated_object = obj selobj.text = obj.name detect.enabled = true detect.state = false center.enabled = true go.enabled = true ) )

on go pressed do ( animate on ( cont = animated_object.rotation.controller for frame in animationRange.start to animationRange.end do ( key_index = getkeyindex cont frame if (key_index) != 0 and (key_index) != 1 then (deleteKey cont key_index) )

for frame in calc_start.value to calc_end.value by step.value do ( if frame < step.value then (at time 0 (prev_pos = animated_object.pos)) else (at time (frame-step.value) (prev_pos = animated_object.pos)) at time frame (current_pos = animated_object.pos) covered_distance = distance prev_pos current_pos rotation = (180 * covered_distance)/(pi*obj_radius.value) in coordsys local at time frame ( case rot_axis.state of ( 1: if reverse.checked == false then (rotate animated_object (eulerangles -rotation 0 0)) else (rotate animated_object (eulerangles rotation 0 0)) 2: if reverse.checked == false then (rotate animated_object (eulerangles 0 -rotation 0)) else (rotate animated_object (eulerangles 0 rotation 0)) 3: if reverse.checked == false then (rotate animated_object (eulerangles 0 0 -rotation)) else (rotate animated_object (eulerangles 0 0 rotation)) ) ) ) )
)

on center pressed do ( addModifier animated_object (edit_mesh()) obj_center = [0,0,0] for i in 1 to animated_object.numVerts do ( obj_center += getVert animated_object i ) obj_center /= animated_object.numVerts animated_object.pivot = obj_center deleteModifier animated_object 1 )

on calc_start changed val do ( if val > calc_end.value then calc_end.value = val ) on calc_end changed val do ( if val < calc_start.value then calc_start.value = val )

on rot_axis changed state do ( if (state == 1) and (detect.state == true) then obj_radius.value = xradius if (state == 2) and (detect.state == true) then obj_radius.value = yradius if (state == 3) and (detect.state == true) then obj_radius.value = zradius ) )

Rollfloater = newRolloutFloater "Rollin'" 200 443 addrollout rollin Rollfloater

Align objects to the PFlow particles


theTarget = $PF_Source_01 theSource = Teapot radius:2 theCount = 50 theCopies = for o = 1 to theCount collect (instance theSource) for t = animationrange.start to animationrange.end by 5 do ( sliderTime = t for i = 1 to theCount do ( theTarget.particleIndex = i with animate on theCopies[i].transform = theTarget.particleTM )--end i loop )--end t loop delete theSource

Slice into Chunks


fn splitInTwo theObj theAngle = ( local topObj = editable_mesh() local bottomObj = editable_mesh() centerPivot theObj resetXForm theObj convertToMesh theObj topObj.transform = theObj.transform bottomObj.transform = theObj.transform topObj.mesh = theObj.mesh bottomObj.mesh = theObj.mesh

topSlice = SliceModifier Slice_Type:2 bottomSlice = SliceModifier Slice_Type:3 topSlice.slice_plane.rotation = EulerAngles (random -theAngle theAngle) (random -theAngle theAngle) (random -theAngle theAngle) bottomSlice.slice_plane.transform = topSlice.slice_plane.transform addModifier topObj topSlice addModifier bottomObj bottomSlice

capMod = cap_holes() addModifier topObj capMod addModifier bottomObj capMod centerPivot topObj centerPivot bottomObj resetXform topObj resetXform bottomObj convertToMesh topObj convertToMesh bottomObj delete theObj #(topObj, bottomObj) )

sel = selection as array for i = 1 to 20 do ( sel = for o in sel where isValidNode o collect o nextObj = sel[random 1 sel.count] maxVolume = -1 for o in sel do ( bbox = o.max - o.min vol = bbox.x * bbox.y * bbox.z if vol > maxVolume do ( maxVolume = vol nextObj = o ) ) join sel (splitInTwo nextObj 45.0) )

Collision Flow
collision_objects = $Box* as array collision_pairs = #() collision_events = #() for i = 1 to collision_objects.count - 1 do for j = i+1 to collision_objects.count do append collision_pairs #(collision_objects[i],collision_objects[j],false,-10000)

for t = animationrange.start to animationrange.end do ( undo off ( at time t ( for i = 1 to collision_pairs.count do ( obj1 = collision_pairs[i][1] obj2 = collision_pairs[i][2] if intersects obj1 obj2 then (

if t-collision_pairs[i][4] >= 1 then ( motionVector1= obj1.pos - (at time (t-1) obj1.pos) motionVector2= obj2.pos - (at time (t-1) obj2.pos) if length motionVector1 > 0 or length motionVector2 > 0 then ( testmesh = (copy obj1) * obj2 if testmesh.numfaces > 0 then ( if not collision_pairs[i][3] then ( collision_pairs[i][3] = true

collision_pairs[i][4] = t centerPivot testmesh resetXform testmesh collision_mesh = Editable_Mesh() collision_mesh.transform = testmesh.transform collision_mesh.mesh = testmesh.mesh append collision_events #(t, obj1, obj2, collision_mesh, length(motionVector1-motionVector2)) hide collision_mesh ) ) else collision_pairs[i][3] = false delete testmesh ) ) ) else collision_pairs[i][3] = false )

particleFlow.beginEdit() new_source = PF_Source() new_source.quantity_viewport = 100.0 new_source.setPViewLocation 10 10

new_action = DisplayParticles() new_source.appendAction new_action new_action.type = 4 new_action.color = white

new_action = Force() new_source.appendAction new_action try(new_action.Force_Space_Warps = #($Gravity01))catch() new_action = renderParticles() new_source.appendAction new_action particleFlow.endEdit()

cnt = 0 particleFlow.beginEdit()

for i in collision_events do ( new_event = event() new_event.setPViewLocation (10 + cnt*200) 400 cnt += 1 new_source.appendInitialActionList new_event new_action = Birth() new_event.appendAction new_action new_action.amount = (i[5]) * 10.0 new_action.Emit_Start = i[1] * TicksPerFrame new_action.Emit_Stop = (i[1]+2) * TicksPerFrame

new_action = Position_Object() new_event.appendAction new_action new_action.variation = 50.0 new_action.Emitter_Objects = #(i[4]) new_action.location = 4 new_action = speed() new_event.appendAction new_action new_action.speed = i[5] * 2.0 new_action.variation = new_action.speed *10.0 new_action.direction = 3 new_action.divergence = 10 )--end i loop particleFlow.endEdit() holdmaxfile() fetchMaxFile quiet:true

Potrebbero piacerti anche