home *** CD-ROM | disk | FTP | other *** search
/ Maximum 3D 3 / m3d-p3.iso / 3DS_MAX / 3DSMAX.2_0 / SCRIPTS / DEMO.MS < prev    next >
Text File  |  1997-10-19  |  39KB  |  1,015 lines

  1. --
  2. --      demo.ms - MAXScript demo
  3. --
  4. --                          John Wainwright
  5. --
  6. --  This script is intended to give an interactive walk-through of
  7. --  some of MAXScript's main features.  The best way to view this
  8. --  file is to step through it in the MAXScript listener window.
  9. --  Here are the instructions:
  10. --
  11. --  1. when MAX is running, choose the Utility command panel and
  12. --     select MAXScript from the utility list
  13. --  2. click [Open listener] in the MAXScript rollout panel and the
  14. --      interactive listener window opens
  15. --  3. in the listener window, type:
  16. --          include "demo.ms"
  17. --     and and hit <enter> and this file should start filling up the
  18. --     listener window.  The include command just inserts the named
  19. --     text file into the listener as though you'd typed it all in.  
  20. --  4. you can scroll around the text in the listener (unlike the
  21. --     standard DOS prompt window) and re-execute any of the code
  22. --     that's there, so, scroll the window back to the top
  23. --  5. position the cursor on the "2 + 2 * 3" line below by clicking
  24. --     on the line and then hit the number-pad <enter> key.  The
  25. --     expression is evaluated and the result printed out.  You can
  26. --     put the cursor anywhere on a line, so long as it is not selecting
  27. --     any run of characters, and the line will be executed when you
  28. --     hit number-pad <enter>. If you use the main <enter> key, it
  29. --     will just insert a newline at the cursor, just like a text
  30. --     editor.  This difference is so you can both edit & re-evaluate
  31. --     existing code.  (Note that this is not the normal way of
  32. --     editing permanent scripts, you use separate script editor
  33. --     windows for this, which are demonstrated later in the walk
  34. --     through).  You can also select a run of characters in a line
  35. --     and hit enter and that sub-expression will be evaluated &
  36. --     the result printed out on the next line.  If you don't have
  37. --     a number-pad on your machine, you can use shift <enter> as an
  38. --     alternative to number-pad <enter>.
  39. --  6. step through each example placing the cursor somewhere on each
  40. --     line and hitting number-pad <enter> to see what it does.
  41. --     You can select several lines at once and hit enter and it will
  42. --     evaluate each complete statement in the selection and print
  43. --     it's value.  Some code fragments extend over several lines, so
  44. --     you select them all and hit number-pad <enter>.  For the
  45. --     first few instances of this, the comments (introduced by
  46. --     a '--') tell you what to select.
  47. --  7. if you want to experiment, you can type new code into the
  48. --     listener at any point and hit number-pad <enter> to evaluate
  49. --     it.
  50. --  8. have fun.
  51. --
  52.  
  53. -- first, some language basics...
  54.  
  55. 2 + 2 * 3       -- infix algebraic expressions, standard precedence
  56. x = 1           -- assignment to automatically create variables
  57. x = x + 1
  58. x = "foo"       -- variables can contain any kind of object
  59. x * 2           -- but you can't make type mistakes; this prints an error
  60.  
  61. -- MAXScript has all the standard linear algebra types & operators  
  62.  
  63. x = [5, 10, 15]        -- 3D vector
  64. x + [3, 2, 1]          -- vector addition
  65. x * 1.2                -- scalar multiplication
  66. q = Quat 45 x_axis     -- quaternions, matrices, euler angles, etc.
  67. m = q as Matrix3       -- conversion between appropriate types
  68. x * m                  -- vector transformed by a matrix 
  69.  
  70. -- many values in MAXScript have subcomponents or
  71. -- properties which you get at with the '.' operator
  72.  
  73. x.y                    -- the y component of the vector
  74. q.angle                -- the quaternion's (derived) angle
  75. x.y = 23               -- set properties through assignment
  76. x
  77. m.row2.y               -- and cascade for nested access
  78.  
  79. -- MAXScript also has a general purpose dynamic array value that
  80. -- can hold values of any kind
  81.  
  82. a = #(1, 2, [5,3,2], "foo")   -- #(...) defines an array literal
  83. a[2]                          -- an array element, indexes start at 1
  84. a[4] = "baz"                  -- assignment to an element
  85. a[5] = 1.5                    -- arrays extend automatically
  86. append a "more"               -- or build them by appending
  87.  
  88. -- memory management is automatic in MAXScript.  It detects
  89. -- when some value is no longer referenced and reclaims its
  90. -- memory, so, for example, if you evaluated:
  91.  
  92. a = 23
  93.  
  94. -- memory taken up by the array that was in the variable 'a' will
  95. -- be automatically recycled since nothing else refers to it.
  96. -- This makes life easy.
  97.  
  98. -- there are several distinguished values in MAXScript
  99.  
  100. true                    -- booleans...
  101. false
  102. 4 > 5                   -- which are the results of comparisons
  103. on                      -- synonyms for true &
  104. off                     --                 false
  105. undefined               -- the undefined value which you get...
  106. foo                     -- if you access an as yet unused variable
  107. b = #(1, 2, 3)
  108. b[23]                   -- or an array element that isn't there
  109.  
  110. -- MAXScript has all the standard control structures:
  111.  
  112. if a > 5 then print "yes" else print "no"
  113. for i = 1 to 10 do print i
  114. while a > 20 do print (a -= 1)
  115. -- you'll notice that some of the command output duplicates
  116. -- the last line.  This is because MAXScript is an
  117. -- "expression-based" language and all of the constructs,
  118. -- including control structures, yield a value.  Every
  119. -- control structure doubles as an expression, for example
  120. -- the if-statement works as an if-expression, so you can say 
  121. -- things like:
  122.  
  123. x = if a > 5 then "yes" else "no"
  124.  
  125. -- or even:
  126.  
  127. x = (if a > 5 then sin else cos) a
  128.  
  129. -- in which an if-expression chooses the function to 
  130. -- call.  MAXScript is also known as a
  131. -- "fully first class" language, and entities like
  132. -- functions are values that can be computed and
  133. -- passed around as in this example.
  134.  
  135. -- the for-loop also automatically iterates over any of
  136. -- the collections in MAXScript:
  137.  
  138. a = #(1, "two", 3.3, [4,4,4])
  139. for i in a do print i
  140.  
  141. -- MAXScript is block-structured.  Blocks of code are
  142. -- surrounded by parentheses, as in the next example.  This is
  143. -- a multi-line expression, so you have to drag-select
  144. -- the whole thing and hit number-pad <enter>.
  145.  
  146. for i = 1 to 5 do
  147. (
  148.     local x = sqrt i        -- you can declare locals inside blocks
  149.     print x                 -- they are lexically scoped
  150. )
  151.  
  152. -- you can also put several expressions on one line
  153. -- with ';' separators:
  154.  
  155. for i = 1 to 5 do ( local x = sqrt i; print x )
  156.  
  157. -- the for-loop has a variant that collects things
  158. -- into arrays.  So,
  159.  
  160. sqrts = for i in 1 to 10 collect sqrt i
  161.  
  162. -- generates an array of square roots.  This form
  163. -- is handy for procedurally gathering sets of
  164. -- scene objects to work on.
  165.  
  166. -- Function definition is simple.  The following defines
  167. -- a function called 'add' that takes two parameters 'a' & 'b'.
  168. -- The body (after the '=') can be a block expression, 
  169. -- and can contain locals, etc.
  170.  
  171. function add a b = a + b
  172.  
  173. add 2 3.5       -- call it
  174.  
  175. -- the syntax function calls is command-language-like, 
  176. -- there are no parentheses or commas, you just line up the
  177. -- arguments one after the other.  This simplifies the syntax a
  178. -- lot, and makes it easy to have optional keyword arguments
  179.  
  180. -- functions can also be recursive, the 'fn' here, is short just
  181. -- for 'function'
  182.  
  183. fn fac n = if n <= 0 then 1 else n * fac (n - 1)
  184. fac 6
  185.  
  186. -- now some MAX stuff...
  187. -- first, simple object creation and animation.  These early examples
  188. -- are things you could do more quickly in the UI, but show the building
  189. -- blocks that will be used to do the hard stuff later
  190.  
  191. -- create a box and a sphere:
  192.  
  193. b = box length:20 width:20 height:20
  194. s = sphere radius:12 segs:20 position:[75,20,0]
  195.  
  196. -- they turn up in the scene, and you've got hold of them in variables b & s
  197. -- the keyword arguments you can specify are 
  198. --   1. any of the creation params for the object type
  199. --   2. any transforms as .pos, .rotation, .scale
  200. --   3. general node properties like .name, .wireColor, etc.
  201. -- they are all optional and have useful defaults
  202.  
  203. -- you can get at MAX object properties as you do other properties
  204. -- in MAXScript:
  205.  
  206. b.pos                      -- get position
  207. b.pos = [0,-120,10]        -- or set it and the box jumps forward
  208.  
  209. -- any time you change a property of a MAX object like this,
  210. -- MAXScript immediately reflects the change in the scene, as though
  211. -- you had adjusted the object in the UI
  212.  
  213. -- you can also create modifiers and add them to an object.
  214. -- The following creates a twist modifier set at 30 degrees and
  215. -- applies it to the box which is in variable b
  216.  
  217. addModifier b (twist angle:30)
  218.  
  219. -- bump up the segs
  220. b.heightsegs = 10; b.lengthsegs = b.widthsegs = 5
  221.  
  222. -- and change the modifier
  223. b.twist.angle = 60
  224.  
  225. -- modifiers turn up as named properties on the object
  226.  
  227. b.scale *= 0.5  -- scale the box down a bit
  228.  
  229. -- in the last example, one of the "math-assignment" operators
  230. -- has been used to modify a property in place.  The above
  231. -- line is equivalent to:  b.scale = b.scale * 0.5
  232. -- there are math-assignment operators for +,*,-,/
  233.  
  234. -- Again, all the parameters you can get at in the MAX rollouts
  235. -- are accessible as properties in MAXScript
  236.  
  237. -- Now some animation.
  238. --
  239. -- MAXScript has several syntax forms that mirror the main MAX UI tools,
  240. -- like the animate button and the time slider and the working coordinate
  241. -- system.  The next example 'turns on' the animate button
  242. -- for a block of code and 'sets the slider' twice and moves
  243. -- the objects around, mirroring what you'd do in the UI.
  244. -- (remember to drag-select all the lines & hit numberpad-<enter>)
  245.  
  246. animate on
  247.     at time 0   (b.pos = [-100, 0, 0]; s.scale = [1, 1, 0.25])
  248.     at time 100 (b.pos = [100, 0, 0];  s.scale = [1, 1, 3])
  249. )
  250.  
  251. -- now drag the slider around and see that it's made some animation
  252. -- and put keyframes at 0 & 100
  253.  
  254. -- when you use animate & time prefixes like this, the actual UI
  255. -- animate button doesn't come on and the slider
  256. -- doesn't move, it's virtual within MAXScript.
  257.  
  258. -- in these examples, the time was given as a simple number. This
  259. -- is interpreted as a frame number by MASXScript.  You can also
  260. -- use one of the various time literals that look like this:
  261.  
  262. 2.5s        -- 2.5 seconds (all times print out as frames)
  263. 20f         -- 20 frames
  264. 4800t       -- 4800 ticks = 1 sec = 30f
  265. 1m3s5f      -- 1 min, 3 secs, 5 frames
  266. 1:15.5      -- SMPTE 1 min, 15 secs, 5 frames
  267.  
  268. -- you can put the 'context prefixes' on simple expressions to affect
  269. -- their evaluation:
  270.  
  271. p1 = at time 10 b.pos          -- grab pos at time 10 into a variable
  272. p2 = at time 50 b.pos          -- and pos at time 50
  273. distance p2 p1                 -- and do some across-time computation
  274.  
  275. -- the following adds a little 4 seg sphere and makes it do a wobbly chase
  276. -- of the box  (again, select all the lines at once)
  277.  
  278. s2 = sphere radius:3 segs:4
  279. animate on for t in 0 to 100 by 5 do
  280.     at time t s2.pos = b.pos + random [-11,-11,-11] [11,11,11]
  281.  
  282. -- this turns on animate, loops over time in t every
  283. -- 5 frames, sets the current time each time through the loop
  284. -- and sets the follower's pos to the box's pos + a random
  285. -- vector.  Note that the animate command is split into
  286. -- two lines; MAXScript has a kind of free-form layout and
  287. -- there are some simple rules in the online docs that
  288. -- tell where you can split commands into multiple lines.
  289.  
  290. -- drag the time slider about to see the small sphere
  291. -- chasing the box.  Leave the slider at about frame 50.
  292.  
  293. -- the "animate" and "at time" contexts are one of several 
  294. -- that are in MAXScript. Others can be use to set 
  295. -- working coordinate systems, rotation centers, 
  296. -- working levels in the scene hierarchy, etc.  An important
  297. -- context is "undo" for controlling whether or not
  298. -- scripted operations are undoable in MAX.  By default, 
  299. -- scripted operations in the Listener are undoable, but those in
  300. -- utility & controller scripts are not since it is very
  301. -- easy to flood the undo buffers if you script a large number
  302. -- of changes.  You turn on or off undo for a sequence of
  303. -- expressions like this:
  304.  
  305. undo on
  306. (
  307.      delete s
  308.      b.height += 30
  309.      s2.radius = 25
  310. )
  311.  
  312. -- you can then undo this set of changes, *as a whole*, either
  313. -- using the undo button in MAX or with the command:
  314.  
  315. max undo
  316.  
  317. -- All the contexts, including undo, can be set permanently while
  318. -- working in the listener, using the 'set' construct, for example:
  319.  
  320. set undo on
  321.  
  322. -- now each individual evaluation in the listener will
  323. -- add an entry to the undo stack
  324.  
  325. -- Up until now you've worked with freshly created objects that
  326. -- were kept in MAXScript variables.  You can also name
  327. -- existing objects in the MAX scene.
  328.  
  329. -- first, open a scene that's got some objects:
  330.     
  331. loadMaxFile "msdemo1.max"
  332.  
  333. -- this is the tutorial ikwalk.max file and has a
  334. -- hierarchy of objects in the robot.
  335. -- <you can load & merge & save using built-in MAXScript 
  336. -- functions>
  337.  
  338. -- To name a scene object & work with it in MAXScript, you
  339. -- use a 'pathname'.  It starts with a '$', like this:
  340.  
  341. select $torso
  342.  
  343. -- this calls the MAXScript function 'select' to select the object
  344. -- named 'torso' in the scene
  345. --
  346. -- since MAX objects are arranged in a hierarchy and they
  347. -- can have non-unique names, you can use a 'path' through
  348. -- the hierarchy to help name an exact object:
  349.  
  350. select $torso/leftupperleg/leftknee/leftlowerleg
  351.  
  352. -- the slashes navigate through the heirarchy to a particular spot,
  353. -- much like file pathnames in Windows
  354. --
  355. -- you can also use wild cards patterns in pathnames to
  356. -- name sets of related objects:
  357.  
  358. select $torso/*         -- all immediate children
  359. select $torso...*       -- the whole robot
  360. select $*lower*...*     -- the lower extremities
  361. select $left*           -- the left side
  362. select $*leg*           -- both legs
  363.  
  364. -- once you've named a set you can work on it as a whole
  365. -- like you can in MAX:
  366.  
  367. selection.radius = 1   -- make all the legs skinny
  368.  
  369. -- this used one of MAXScript's built in 'object sets', in 
  370. -- this case the current selection. They act like
  371. -- pathname sets.  You could also have used the pathname
  372. -- in the assignment directly:
  373.  
  374. $*leg*.radius += 1              -- make him healthy again
  375. scale $*foot* [2,1,1]           -- give him clown feet
  376. rotate $*leftupper* -35 y_axis  -- and exercise
  377.  
  378. select geometry         -- here are some more object sets
  379. select lights           -- all the categories in MAX are available
  380. hide objects            -- 'objects' gives you everything in the scene
  381.  
  382. -- you can also invoke most of the MAX menu and toolbar functions
  383. -- using the 'max' command, the list is in the manual.
  384.  
  385. -- answer NO to the file save question that pops up and yes to
  386. -- the reset question
  387.  
  388. max select none
  389. max reset file
  390.  
  391. -- now create some geometry that's a bit more tedious to do
  392. -- by hand:
  393.  
  394. for i in 1 to 10 do
  395.     sphere radius: (i*10) position: [0, (10*(1.0*i)^2) - 10, 0]
  396.  
  397. -- this makes 10 spheres of increasing radius that touch exactly,
  398. -- a bit more of a chore to do interactively in MAX
  399.  
  400. -- do it again, but this time grab the spheres into a MAXScript
  401. -- array:
  402.  
  403. hide geometry      -- clear the scene first
  404.  
  405. spheres = for i in 1 to 10 collect
  406.     sphere radius: (i*10) position: [(10*(1.0*i)^2) - 10, 0, 0]
  407.  
  408. -- the 'collect' form of the for-loop gathers all the 
  409. -- results into an array.  You can then index them or
  410. -- loop over them:
  411.     
  412. spheres[3]
  413. for i in spheres do print i.radius
  414.  
  415. -- now make a variant of the 'array layout' tool in MAX.  Here
  416. -- you want the line of touching spheres to be instance-cloned
  417. -- and rotated in an increasing angle as it clones.
  418. -- <give it a second or so to run - MAX takes a little time
  419. --  creating lots of objects>
  420.  
  421. inc = 10
  422. for angle in 20 to 360 by inc do
  423. (
  424.     instance spheres rotation:(Quat angle z_axis)
  425.     inc += 10
  426. )
  427.  
  428. -- <you can zoom extents and move the listener to see the results
  429. --  better>
  430. -- this loops over an increasing angle and instances the whole
  431. -- array's worth each time, setting each sphere's rotation
  432. -- You can use an array of objects anywhere you use a pathname
  433. -- and it does the 'automatic' mapping of the function over all the
  434. -- array elements for you
  435.  
  436. -- Making a selection based on some geometric property is handy:
  437.  
  438. for s in $sph* where s.radius > 30 and s.radius < 70 do selectmore s
  439.  
  440. -- this looped over all the spheres and added the ones in a certain
  441. -- radius range to the current selection, now operate on them:
  442.  
  443. selection.radius = 30
  444.  
  445. -- you can see it's possible to use MAXScript casually to help your
  446. -- direct manipulation work in MAX.
  447.  
  448. max select none
  449.  
  450. -- some more neat tricks...  you don't want the spheres too neatly
  451. -- lined up; the following shuggles each one about a bit:
  452.  
  453. coordsys local for s in $sph* do s.pos = random [-40,-40,0] [40,40,0]
  454.  
  455. -- this shows the use of a coordinate system context. Such context
  456. -- prefixes effect transforms in MAXScript exactly the way they do
  457. -- in MAX. This example loops over the spheres and in each's local
  458. -- coordinate system, sets its position to a small random vector, so
  459. -- jiggling them all about.  You can click back on the command
  460. -- above & eval it (by hitting numpad-<enter> several times and
  461. -- each time the positions are randomized a bit more.
  462.  
  463. -- How about a radial scatter.  Here's a function to do this:
  464. --
  465. -- (drag-select the whole thing & numpad-<enter> to define the 
  466. --  function)
  467.  
  468. mapped function radial_scatter obj centre amount =
  469. (
  470.     local delta = obj.pos - centre,
  471.           stretch = length delta * amount/100.0,
  472.           stretchlow = stretch - stretch/4,
  473.           stretchhigh = stretch + stretch/4
  474.  
  475.     obj.pos = centre + delta * random stretchlow stretchhigh
  476. )
  477.  
  478. -- this takes an object, a center and a 'scatter' amount.  Notice
  479. -- that the computations are all on vectors - vector math is
  480. -- symbolic in MAXScript.  It computes a random radial move 
  481. -- proportional to the distance from the center and shifts
  482. -- the object.  Now apply it to all spheres:
  483.  
  484. radial_scatter $sph* [0,0,0] 0.2
  485.  
  486. -- you can make scripted functions that are automatically mapped
  487. -- over a collection by preceding their definition with the word
  488. -- 'mapped', so here the scatter function gets called many times,
  489. -- each time being given the next sphere in $sph*
  490.  
  491. max reset file          -- choose no and yes.
  492. max utility mode
  493.  
  494. -- Everyone wants a starfield. Here's a function to generate one
  495. -- out of geometry. It takes a star count and optional keyword
  496. -- arguments for position & extent.
  497.  
  498. fn starfield count extent:[200,200,200] pos:[0,0,0] =
  499. (
  500.     local f = pos - extent / 2,
  501.           t = pos + extent / 2
  502.     
  503.     for i = 1 to count do
  504.         sphere name:"star" radius:(random 0.1 2.0) \     
  505.                       position:(random f t) \
  506.                       segs:4 smooth:false
  507. )
  508.  
  509. -- this makes a bunch of 4-sided, non-smooth spheres scattered
  510. -- throughout the extent.
  511. --
  512. -- The optional keyword arguments defined above have values
  513. -- after them which are used as defaults if the arguments are
  514. -- not supplied by the caller.
  515.  
  516. starfield 250       -- 250 stars, default bounds, please wait.
  517.  
  518. -- again, most of the time here is in the actual object creation
  519. -- inside MAX.
  520.  
  521. -- if you wanted a spherical ball of stars you could select the 
  522. -- stars further than some distance from the center of the extent:
  523.  
  524. for s in $star* where distance s.position [0,0,0] > 100 do selectmore s
  525.  
  526. -- and trim them away
  527.  
  528. max delete
  529.  
  530. -- now some more complicated animation:
  531.  
  532. loadMaxFile "msdemo2.max"
  533.  
  534. -- this file has two objects, a traveling sphere and a tall
  535. -- box that has a bend modifier on it.  If you wave the time slider
  536. -- about, you can see the ball move.  The following animates the bend
  537. -- so the box sways around, following the sphere, like a look-at
  538. -- controller but deforming the top of the box and keeping
  539. -- its base still.
  540. --
  541. -- here's a function that takes a target and something to bend at it:
  542.  
  543. fn bendtracker bender target =
  544. (
  545.     animate on
  546.         for t = 0 to 100 do at time t
  547.         (
  548.             d = target.pos - bender.pos 
  549.             bender.bend.angle = atan2 d.z d.x
  550.             bender.bend.direction = -(atan2 d.y d.x)
  551.         )
  552. )
  553.  
  554. -- the function turns on animate and loops over t over animation time.
  555. -- it gets the delta vector by substracting the bender pos from the
  556. -- target pos.  Since this is in a changing time context,
  557. -- each time it is computed it will use the animated
  558. -- properties of the objects at that point in time.
  559. -- It does some trig to figure bend angle & direction and plants
  560. -- keyframes on the bend modifier.  Call it on the two scene objects,
  561. -- and then wave the time slider to see the result:
  562.  
  563. bendtracker $bender $target
  564.  
  565. -- Something even trickier: floating teapots.
  566.  
  567. loadMaxFile "msdemo3.max"
  568.  
  569. -- this scene has a teapot, an animated ripply surface and
  570. -- a tape helper.  The following makes the teapot float on the
  571. -- rippling surface at the point that the tape instersects 
  572. -- the surface, moving & rotating to follow the surface 
  573. -- as it ripples about.  There was a brilliant, 20 or 30 step
  574. -- tutorial by Randy Kreitzman about doing this in MAX r1 using
  575. -- several dummies & expression controllers.  And, of course,
  576. -- the Attach controller in MAX r2 does it all for you, but
  577. -- here it is in MAXScript:
  578.  
  579. fn sail surface item tape =
  580. (
  581.     local ir = intersectRay surface (tape as ray)
  582.     item.dir = ir.dir
  583.     item.pos = ir.pos
  584. )
  585.  
  586. animate on
  587.     for t = 0 to 100 by 5 do at time t sail $box01 $teapot01 $tape01
  588.  
  589. -- <play the animation to see the results>
  590.  
  591. -- the function takes a surface, a floater and a
  592. -- tape helper (it can be anything with a target)
  593. -- and uses a built-in function to compute the normal
  594. -- ray at the surface/ray intersection, then sets
  595. -- the floaters direction & position to those
  596. -- components of the normal ray.  The 'dir' property
  597. -- is one of the handy 'virtual' properties that MAXScript provides;
  598. -- it sets the direction of the object's local z-axis to point
  599. -- along the given direction vector.
  600. --
  601. -- the animate should be clear by now. Note that the context
  602. -- clauses, like time & animate are 'dynamic' in their effect.
  603. -- this means any functions you call and any they call, and so on,
  604. -- observe the current context.
  605.  
  606. -- the surface floater is sufficiently neat to warrant turning into
  607. -- a utility with a user interface (at least it was in MAX r1), so...
  608.  
  609. -- the following command opens a script editor window on the named
  610. -- file.  It contains a variant of the sail function and a utility
  611. -- panel rollout written in MAXScript.  When the window is open, 
  612. -- check the code out, then evaluate it by choosing "Evalute All" in 
  613. -- the File menu (or hitting ctrl-e) and come back here.
  614. -- <script editors let you write larger scripts
  615. -- that can be saved to & got from files and let you <enter> eval
  616. -- selections in them, the results printing in the listener>
  617.  
  618. edit "floater.ms"
  619.  
  620. -- the "Surface Floater" utility definition has been added to the
  621. -- utilities drop-down list in the MAXScript panel.  (There are some
  622. -- others there also that we're loaded from the startup script that
  623. -- we'll look at later.)  When you select it, the scripted panel opens
  624. -- and you can pick the surface, floater & intersector, do an
  625. -- immediate float or animate it over chosen frames and adjust
  626. -- the position of the floater relative to the surface.  The
  627. -- x,y,z & roll spinners are live, as soon as you pick a floater you
  628. -- can move it with them.
  629. -- Set the z offset to -4 and the roll to -90 and click animate
  630. -- again.  The teapot looks like it as some weight now and
  631. -- settles into the surface a bit.
  632. -- Close the utility
  633.  
  634. -- If you study the script file you'll see the floater function
  635. -- has been enhanced with a roll and a local offset parameter
  636. -- for the spinners to use.  The local coordsys code
  637. -- at the end of the function does the work.  Note that it
  638. -- backscales the offset by the object's scale in case it
  639. -- isn't unity, so the offset coord is always in world-space units.
  640. --
  641. -- A utility definition is made up of a set of locals that
  642. -- stay alive until the panel is closed, a set of panel UI
  643. -- controls (buttons, spinners, etc.) with names &
  644. -- captions & setup parameters, and a set of handler functions
  645. -- (the 'on' things) that get called as the user works with
  646. -- the panel.  Each kind of control has associated 'events'
  647. -- (like pressed, picked, changed) and call the associated
  648. -- handlers with appropriate arguments when those events happen.
  649. -- Because a panel rollout is narrow, MAXScript can compute a
  650. -- reasonable layout automatically, so you don't need a
  651. -- layout editor.
  652.  
  653. max reset file
  654.  
  655. -- another use of the intersecting ray technique is for scattering
  656. -- objects over a surface, such as growing hair.
  657. -- (Of course, this has now been provided for with the
  658. -- scatter compound object in r2!).  The following function
  659. -- takes a surface and a collection of prototype hairs and instances
  660. -- clones of randomly chosen proto-hairs randomly over the surface.
  661.  
  662. -- open a test file:
  663.  
  664. loadMaxFile "msdemo9.max"
  665.  
  666. -- define the function...
  667.  
  668. fn grow_hair surf hairs count =
  669. (
  670.     progressStart "Growing hair..."
  671.     local r = ray [0,0,0] x_axis,
  672.           len = length (surf.max - surf.min) * 2
  673.  
  674.     for i in 1 to count do
  675.     (
  676.         -- place the ray at a random position outside the
  677.         -- surface pointing into the center & intersect
  678.         r.dir = random [-1,-1,-1] [1,1,1]
  679.         r.pos = surf.center + -r.dir * len
  680.         local ir = intersectRay surf r
  681.  
  682.         -- instance a randomly chosen hair on the intersect ray
  683.         if ir != undefined do
  684.             instance hairs[random 1 hairs.count] name:"hairs" \
  685.                 pos:ir.pos dir:ir.dir
  686.  
  687.         -- update the prograss bar, exit loop if user canceled
  688.         if progressUpdate (i * 100 / count) == false then exit
  689.     )
  690.     
  691.     -- close the progress bar
  692.     progressEnd ()
  693. )
  694.  
  695. -- this function also demonstrates how to display a progress
  696. -- bar and handle the cancel button.
  697.  
  698. grow_hair $surf1 $hair* 500  -- 500 hairs, please
  699.  
  700. -- notice how you can pass a wild-card pathname around as a 
  701. -- function argument and it acts like a collection of objects.
  702.  
  703. -- this function is actually pretty crude, there are better ways
  704. -- to do this by iterating over the mesh itself and attaching
  705. -- geometry onto one MAX object, but it highlights some
  706. -- simple and powerful techniques
  707.  
  708. delete $hairs*      -- delete the generated geometry
  709. max reset file
  710.  
  711. -- you can construct and access bezier splines in MAXScript, 
  712. -- using them to help object layout and animation.
  713. -- The following places spheres evenly-spaced along one curve,
  714. -- using the second curve as a 'scaling' envelope
  715. -- to control the diameter of each sphere as it is placed.  
  716. -- For each sphere, it uses the nearestpath() function
  717. -- to compute the closest point on the envelope curve and
  718. -- sets the sphere radius using the distance from it to that
  719. -- point on the envelope
  720.  
  721. loadMaxFile "msdemo6.max"
  722. unhide $line01  
  723. unhide $line04 
  724.  
  725. len = curvelength $line01
  726. for u = 0.0 to 1.0 by (12.0/len) do
  727. (
  728.     local s = sphere radius:4 pos:(lengthinterp $line01 u)
  729.     local p = pathinterp $line04 (nearestpathparam $line04 s.pos)
  730.     s.radius = distance s.pos p
  731. )
  732.  
  733. -- you can also make a curve act like a 'spine' for a 
  734. -- set of objects (like metaballs) and as you animate the
  735. -- spine the balls move with it.
  736.  
  737. delete $sph*        -- get rid of the balls 
  738.  
  739. unhide $spine       -- and show a spine and
  740. unhide $ball*       -- some balls along it
  741. hide $line*        -- hide the other stuff
  742.  
  743. -- wave the time slider about to see the spine curve animating. The
  744. -- balls are placed along it at time zero.
  745.  
  746. -- the following computes 2 arrays with elements in it for each ball.
  747. -- one has the nearest point parameters at time 0 for each ball
  748. -- and the other has a distance from the nearest points to each ball
  749. -- at time 0:
  750.  
  751. nus = at time 0 
  752.     for b in $ball* collect nearestpathparam $spine b.pos
  753.     
  754. dists = at time 0
  755.     for i in 1 to nus.count collect
  756.     (
  757.         local b = $ball*[i],
  758.               d = distance b.pos (pathinterp $spine nus[i])
  759.         if b.pos.z > (pathinterp $spine nus[i]).z then d else -d
  760.     )
  761.  
  762. -- if you wave the time line around, you'll see that the spine is
  763. -- a morphing curve.  the following places a keyframe every 5 frames
  764. -- for each ball, placing them at a point that maintains their original
  765. -- orientation & distance from the curve's nearest point as the 
  766. -- curve moves
  767.  
  768. animate on
  769.     for t in 0 to 100 by 5 do at time t
  770.         for i in 1 to nus.count do
  771.             $ball*[i].pos = pathinterp $spine nus[i] +
  772.                 ((pathtangent $spine nus[i]) * dists[i]) * quat 90 y_axis
  773.  
  774. -- now wave the time slider about.  the balls move in concert with the
  775. -- spine.  (there is a limitation here with only one spline, that
  776. -- you can't control any twisting. a generalization would have 2
  777. -- splines you could use as 'rail' to control twist over time over
  778. -- the length of the spine.
  779.  
  780. -- the next thing to look at is mesh creation at the vertex level.
  781.  
  782. m = mesh vertices: #([0,0,65], [0,-25,0], [-10,-10,0], [-25,0,0], [-10,10,0], [0,25,0], [10,10,0], [25,0,0], [10,-10,0]) \
  783.           faces: #([1,3,2], [1,4,3], [1,5,4], [1,6,5], [1,7,6], [1,8,7], [1,9,8], [1,2,9])
  784.  
  785. -- the 'mesh' function takes several forms, in this case arrays of vertices
  786. -- & faces (you can also add arrays of materialIDs, texture vertices,
  787. -- etc.)
  788.  
  789. -- another variant generates a simple square mesh which you can then run
  790. -- over moving the vertices around and do other stuff to it.
  791. --
  792. -- the following example generates surface meshes based on 3D math
  793. -- surface functions.
  794. -- 
  795. -- first, some z functions of x & y:
  796.  
  797. fn saddle x y = sin(x * y)                                 -- saddle
  798. fn sombrero x y = (cos(x^2 + y^2)) / (1 + (x^2 + y^2)/45)  -- sombrero
  799.  
  800. -- now a surface builder that will use the 3D surface function
  801. -- you give it to build a mesh:
  802.  
  803. fn math_mesh func length:100 width:100
  804.                   lengthSegs:16 widthSegs:16
  805.                   zscale:10 xyrange:22.5 old_mesh: =
  806. (
  807.     local m, vert_count = (lengthsegs + 1) * (widthsegs + 1),   
  808.           xscale = xyrange as float / width, 
  809.           yscale = xyrange as float / length
  810.  
  811.     if old_mesh == unsupplied then
  812.         m = mesh length:length width:width lengthsegs:lengthsegs widthsegs:widthsegs
  813.     else
  814.         m = old_mesh
  815.  
  816.     m.position = [-width/2, -length/2, 0]
  817.     for i = 1 to vert_count do                                                                     
  818.     (
  819.         vertex = getvert m i 
  820.         vertex.z = zscale * func (vertex.x * xscale) (vertex.y * yscale) 
  821.         setvert m i vertex
  822.     )
  823.  
  824.     update m 
  825.     m  
  826. )
  827.  
  828. -- this takes some optional geometry & topology arguments and a 3D surface
  829. -- function (MAXScript lets you pass functions around as parameters),
  830. -- builds a mesh (or modifies one you optionally supply) and runs 
  831. -- across its vertices computing a new z for each using the
  832. -- surface function.  For those of you who have done SDK-level
  833. -- mesh programming, vertex coordinates in MAXScript are 
  834. -- always given in the working coordinate system, in this case 'world'.
  835. --
  836. -- Select the definition and hit <enter> it to define the
  837. -- function if you haven't already.
  838. -- 
  839. -- First move the existing mesh in the scene off to the side then
  840. -- make some surfaces...
  841. -- <move each aside as you make them, they all get built at [0,0,0]>
  842.  
  843. math_mesh saddle                                           
  844. math_mesh saddle lengthsegs:32 widthsegs:32 xyrange:45   
  845. math_mesh sombrero xyrange:45 zscale:40 lengthsegs:32 widthsegs:32 
  846.  
  847. max reset file
  848.     
  849. -- The next example illustrates how a tech department in 
  850. -- a large house might set up a job-specific tool for the
  851. -- artists and wrap it in a utility panel UI...
  852. --
  853. -- Here's the scenario:
  854. -- You have generated some text files from an Excel spreadsheet;
  855. -- one contains a list of bitmap file names in pairs, a diffuse
  856. -- map and an opacity, for some background trees in a scene
  857.  
  858. edit "mapdef.dat"
  859.  
  860. -- the other file has some generated names & positions & rotations
  861. -- for the backdrops
  862.  
  863. edit "place.dat"
  864.  
  865. -- you want a tool that let's the artist choose the map & position
  866. -- files through a standard file open dialog and that will then
  867. -- read the files and build materials from the map file info.
  868. -- It then constructs a cross mesh and instances it at the locations
  869. -- given in the placement file and randomly applies one of the
  870. -- backdrop materials.
  871.  
  872. -- The following file defines the 'backdrop loading' function
  873. -- and a utility panel to drive it.  Open the file and evaluate
  874. -- it by choosing "Evalute All" in the File menu (or hitting ctrl-e)
  875. -- and come back here.
  876.  
  877. edit "backdrop.ms"
  878.  
  879. -- here's a simple scene file to load them into:
  880.  
  881. loadMaxFile "msdemo4.max"
  882.  
  883. -- Shrink or close the data file windows to clean up some space.
  884. -- Select the "Load Backdrop" utility in the MAXScript panel
  885. -- utility drop-down.  Click the file chooser buttons in order and
  886. -- locate the mapdef.dat & place.dat files in the scripts directory.
  887. -- click "Load backdrops" and in come the backdrops.  Render the
  888. -- scene for a check.  The adjustment spinners are active and
  889. -- apply to all the backdrops so move them, -100 in y,
  890. -- and scale them up, 1.5 for x,y,z and re-render.
  891. -- The "Shuffle materials" button randomly reassigns the materials.
  892. -- Click it a try another render.
  893. -- The utility script file has some comments in it about how it works.
  894.  
  895. -- While still in this scene lets make some cameras and 
  896. -- access the renderer.  First some cameras:
  897.  
  898. targetcamera pos:[3.5, -320, 50] target:(targetobject pos:[0, -100, 40])
  899. targetcamera pos:[85, 30, 20]    target:(targetobject pos:[-50, 1, 20])
  900. targetcamera pos:[-2, -8.6, 450] target:(targetobject pos:[0, 0.5, 17])
  901.  
  902. -- note you have to make a target object explicitly for target cameras
  903. -- (and target spots and tape helpers).  The cameras provide
  904. -- different angles on the scene.  You can get a snapshot through each like
  905. -- this:
  906.  
  907. for c in cameras do render camera:c outputwidth:320 outputheight:240
  908.  
  909. -- it loops over each camera in the scene and renders the views into 
  910. -- a separate bitmap object shown in its own virtual frame buffer.
  911. -- Other optional keyword args let you specify outputfile and all 
  912. -- the common renderer parameters. See the docs.
  913.  
  914. -- There's another utility in the MAXScript panel called "Trail Maker".
  915. -- Open it up and load this file to play with:
  916.  
  917. loadMaxFile "msdemo5.max"
  918.  
  919. -- The trail maker lets you select a master animated object 
  920. -- and a trailer object (or selection) and places copies
  921. -- of the trailers along the path of the master at intervals
  922. -- controlled by the appropriate spinners.  Have a play. Hit
  923. -- the delete button each time to trash the last trail.
  924. -- Here's one interesting use:
  925. --  1. select all the trees that are over to the side
  926. --  2. pick the big yellow box as master (the one that
  927. --     moves but doesn't bank)
  928. --  3. choose "use selection" for the trailer - for
  929. --     each trailer copy, this will randomly choose
  930. --     an object in the current selection
  931. --  4. set offset y to 25
  932. --  5. select "offset is exclusion zone"
  933. --     (this means leave a space that wide along the
  934. --      path and put trail copies outside it)
  935. --  6. set randomize y to 15, frame step to 5
  936. --     copies to 4 & hit "Make trail" - instant
  937. --     road-side grove.
  938. --
  939.  
  940. -- MAXScript also lets you log listener activity to a file:
  941.  
  942. openlog "foo.log"       -- log on
  943.  
  944. $box01.pos.z += 20      -- do stuff
  945. print objects
  946.  
  947. closelog ()             -- log closed
  948. edit "foo.log"          -- look at it
  949.  
  950. -- note the '()' above after the closelog. This function
  951. -- takes no arguments, but you can't just write the function
  952. -- name to call it; this would just reference the variable holding
  953. -- the function so you'd just get the function value and not
  954. -- call it.  The () is a 'nullary' call - it calls the
  955. -- function with no arguments.
  956.  
  957. -- You can also do formatted ouput to a text file, useful for
  958. -- exporting scene info for artists or to drive motion-control
  959. -- cameras, etc.
  960. -- For this example, first create some keyframed objects....
  961.  
  962. b = box (); s = sphere ()
  963. animate on
  964.     at time 0   (move b [-100, 0, 0]; scale s [1, 1, 0.25])
  965.     at time 35   move b [0, 100, 0]
  966.     at time 100 (move b [200, 0, 0];  scale s [1, 1, 3])
  967. )
  968.  
  969. -- The following code creates an ouptut file and loops over the
  970. -- objects in a scene dumping out name & bounds.  It checks to
  971. -- see which have keyframes and dumps out the keyframe info...
  972. --
  973. -- <select this whole piece of code and eval it>
  974.  
  975. f = createfile "foo.dmp"
  976.     for o in objects do
  977.     (
  978.         format "object %, % - %\n" o.name o.min o.max to:f
  979.         local keys
  980.         if o.pos.isAnimated and (keys = o.pos.keys) != undefined do
  981.         (
  982.             format "  % position keys\n" keys.count to:f
  983.             for k in keys do
  984.                 format "    %: %\n" k.time k.value to:f
  985.         )
  986.         if o.scale.isAnimated and (keys = o.scale.keys) != undefined do
  987.         (
  988.             format "  % scale keys\n" keys.count to:f
  989.             for k in keys do
  990.                 format "    %: %\n" k.time k.value to:f
  991.         )
  992.     )
  993. close f
  994.  
  995. edit "foo.dmp"   -- look at the results.
  996.  
  997. -- and MAXScript has all the linear algebra you could want...
  998.  
  999. foo = box height:100
  1000. foo.rotation = quat 45 x_axis * quat 90 y_axis     -- quaternions
  1001. m = $box01.transform                               -- object's node TM
  1002. mi = inverse m                                     -- matrix math...
  1003. [1,2,3] * mi                                        
  1004. for i in 0 to 1 by 0.1 do                           -- quat. interpolation
  1005.     print (slerp (quat 0 x_axis) (quat 45 y_axis) i)
  1006.  
  1007. -- if you have access to the SDK help files, the notes on the linear
  1008. -- algebra types are relevant if you can decode them - MAXScript
  1009. -- exposes all these types and their operations.
  1010.  
  1011. -- the end
  1012.  
  1013.