home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD1.iso / GFX / Raytracing / Raytracer / LightWave.lha / Lightwave / Toaster / Arexx_Examples / lwm / randomenvelopes / Macro.Text < prev    next >
Encoding:
Text File  |  1994-05-26  |  28.9 KB  |  800 lines

  1. Line by Line through the Random Envelope Macro
  2.  
  3. by Grant Boucher
  4.  
  5.  
  6.  
  7.      One of the most powerful features of the Amiga operating system is the
  8. ARexx  programming  language.   Essentially,  ARexx lets you write your own
  9. programs that control other programs according to your needs.  For example,
  10. you  can  use  ARexx  to  write programs that will convert large numbers of
  11. image files from one file format to another.
  12.  
  13.      With the new Modeler Arexx and Macro functions, we can program our own
  14. custom  LightWave and Modeler effects without having to know how to program
  15. in  C  or how the Modeler actual does the things it does.  Just take a look
  16. at  the  vast  numbers  of  powerful  ARexx  Macros, both public domain and
  17. commercially available, and the great deal of functionality they have added
  18. to the entire LightWave/Modeler combo.
  19.  
  20.      Recently,  I  was involved in some special effects shots that required
  21. at  least  a hundred lights which needed to randomly undulate in intensity.
  22. We  also  wanted the option of having them fade out towards the end.  Since
  23. the  shots  were as long as 900 frames and we wanted to avoid repeating any
  24. motions  which  might  make the lights look mechanical or programmed, I was
  25. asked  to  whip  up a quick and dirty ARexx script to do the job.  Once the
  26. LightWave  envelopes  were  generated, I was then asked to give the macro a
  27. gui interface and add some features that might make it a little more useful
  28. for future effects shots.
  29.  
  30.      That's where Modeler came in and and I felt the finished Modeler Macro
  31. seemed   a  perfect  tutorial  for  those  not  yet  initiated  into  Arexx
  32. programming the Modeler.
  33.  
  34.  
  35.  
  36.  
  37.  
  38. ARexx - Basic principles
  39.  
  40.      ARexx  is  actually  just  a  typical  programming  language  with  an
  41. attitude.  You'll find all of the standard programming tools of a Basic, C,
  42. or  Pascal style language that you'd expect, but also the extra added bonus
  43. of  commands  that  come  from  virtually  all other Amiga programs.  Image
  44. processing  programs  like  ImageFX  and  ADPro  come with entire reference
  45. manuals  listing special commands that allow you to load, process, and save
  46. image data...all from YOUR ARexx script!
  47.  
  48.      So,  I  like  to  think  of  ARexx  as  just  a new version of all the
  49. programming  languages  I  have  used  in the past, with Amiga software and
  50. hardware control functions thrown in.
  51.  
  52.      Until  recently, I used ARexx primarily for batch processing of files,
  53. so  I  didn't  have  to  manually Load Object, Flip Polygons (wait), Reduce
  54. Faces  (wait),  and  Save  Object  for every Public Domain Imagine object I
  55. wanted to convert to LightWave using Pixel 3D Professional, for example.
  56.  
  57.  
  58.  
  59. Writing ARexx Scripts
  60.  
  61.      Since  ARexx  programs  are  just  text  files  that  the  RX  program
  62. interprets,  you  can  use  any  word processor like Final Writer or a text
  63. editor  like  CygnusEd  to  write and edit your ARexx script.  If you use a
  64. Word Processor, make sure you import and export your file as ASCII text, as
  65. the  word  processor's  own internal file format will certainly add strange
  66. control  characters  that  mean  nothing  to  ARexx  and  make  your script
  67. unusable.
  68.  
  69.  
  70.  
  71.  
  72.  
  73. The Program - line by line
  74.  
  75. Line 1 - The Author Line
  76.  
  77.      The first line of any ARexx script must be a comment line.  It is just
  78. ARexx's  way  of signifying that this is an ARexx program, and not a letter
  79. you  are sending to your mother.  Comments begin with the /* characters and
  80. end  with  the  */  characters.  Comments are usually used to document your
  81. script  for  others  or  yourself if you come back to it three years later.
  82. Since  the  first  line  of  any  ARexx  script  must  be  a  comment, most
  83. programmer's  find this a convenient place to put their name for posterity.
  84. Note  that  comment  lines  don't  affect the operation or function of your
  85. program, and are simply ignored when it is run.
  86.  
  87.  
  88.  
  89. Lines 3 - 12
  90.  
  91.      For  most Modeler macros, you'll want these lines at the start of your
  92. script.   Basically,  they initialize special ARexx math functions, turn on
  93. some  special  debugging tools, and generally check to make sure everything
  94. is  set  up right to start.  Many initialization functions like this can be
  95. just  grabbed  from  the  start  of another ARexx script and left alone.  I
  96. don't worry about them, so probably neither should you.
  97.  
  98.  
  99.  
  100. Line 15
  101.  
  102.      This  line  calls  (or  performs)  Modeler's  requester  and interface
  103. definition,  named  REQ_BEGIN().   The  text  that  follows this command in
  104. quotations  becomes  the  name of the requester.  The Modeler automatically
  105. formats this and all requester commands for you!
  106.  
  107.  
  108.  
  109. Line 17
  110.  
  111.      This  is  the  first  of  the  requesters, where we actually want some
  112. user-input  to  come  back.  Again, we use the REQ_ADDCONTROL() function to
  113. set  up  our  new  feature, but this time we pass only two arguments to the
  114. function.   The  first  is  a text message describing to the user what this
  115. function means.  The second is the letter 'B' which tells the function that
  116. this  is  a BOOLEAN, or ON/OFF, style requester.  The modeler automatically
  117. creates the Check-Mark gadget for you!  I gave this feature the name RampID
  118. so  I  would know that this is my Ramp to Minimum toggle.  This feature, if
  119. on,  tells  the  macro  to  decrease  the  overall  intensity of all of the
  120. envelope  key  frames until they equal the user's lowest intensity value by
  121. the  end of the envelope.  In other words, the intensities fade towards the
  122. end of the envelope.
  123.  
  124.  
  125.  
  126. Lines 18-25
  127.  
  128.      All  of  these  lines  are  essentially  the  same.   This  time,  the
  129. REQ_ADDCONTROL()  function  is  passed a letter 'N' (as the second variable
  130. again)  to signify that this is going to be a numeric requester, asking for
  131. a  value  from the user.  The first argument is again a description of what
  132. number  the user needs to enter, and the third argument tells the requester
  133. what  units  the  number  will  be in.  Since we are working with arbitrary
  134. values  like  frames  and  intensities,  not meters, we send a 0 for all of
  135. these, which means 'unitless'.
  136.  
  137.      Note  the  strange  %1  and %2 characters attached to these functions.
  138. The  %  character  stands  for  INTEGER DIVIDE, meaning it is the same as a
  139. standard  DIVIDE  command  (i.e.   '/')  but  that  the result is always an
  140. integer.  When I take a number and INTEGER DIVIDE by 1 (i.e.  %1) I am just
  141. forcing  whatever  value the user entered to become an integer.  We have no
  142. use for Frame 10.333 for example!
  143.  
  144.      The MINFRAME and MAXFRAME variables are INTEGER DIVIDE by 2 (i.e.  %2)
  145. because  if  our envelope peaks are going to be, say 30 to 50 frames apart,
  146. each  key  frame is actually going be half that amount from every other key
  147. frame.   Two  key  frames,  or twice that value, gets you back to where you
  148. started  from, either a peak or valley.  I kill two birds with one stone as
  149. I  make  sure these values are integers and they are reduced by half so our
  150. program works properly.
  151.  
  152.  
  153.  
  154.  
  155.  
  156. Definitions of our main variables...
  157.  
  158.  
  159.  
  160. MINFRAME  =  The minimum distance between peaks of our undulating envelope.
  161. For  example,  a value of 20 here means that no two high points (or two low
  162. point for that matter) will be closer than 30 frames apart.
  163.  
  164.  
  165.  
  166. MAXFRAME  =  The maximum distance between peaks of our undulating envelope.
  167. For  example,  a value of 50 here means that no two high points (or two low
  168. point for that matter) will be farther than 50 frames apart.
  169.  
  170.  
  171.  
  172. NOTE:   If  you  want  the  envelope key frames to step forward evenly, say
  173. every  40  frames between peaks, then MINFRAME and MAXFRAME should be equal
  174. (i.e.   40 in our example above).  Having these two values random means the
  175. intensities  vary  from high to low over a random time frame, giving them a
  176. more organic and natural feel.
  177.  
  178.  
  179.  
  180. ENDFRAME = The length in frames of our envelope.  Because we are generating
  181. our  steps between key frames, this value is a minimum guideline only.  For
  182. example,  a  value  of  1000  means the last key frame of the envelope will
  183. certainly not be less than 1000, but it might be slightly higher.
  184.  
  185.  
  186.  
  187. LOWMIN = This value determines the lowest intensity of our envelope.
  188.  
  189.  
  190.  
  191. LOWMAX = This value determines the maximum range of the lowest intensities.
  192.  
  193.  
  194.  
  195. NOTE:   If  LOWMIN  is  0 and LOWMAX is 10, the valleys (or low key frames)
  196. will  never be lower than 0 and never higher than 10.  If LOWMIN and LOWMAX
  197. are equal (for example, both are 0), then there will be no variation of the
  198. low  values  (i.e.   the  low  key frames will always be 0).  Again, having
  199. these  values  different  makes  our  lights  dim  in  a more unpredictable
  200. fashion.
  201.  
  202.  
  203.  
  204. UPPERMIN  =  This  value  determines  the  minimum  range  of  our  highest
  205. intensities.
  206.  
  207.  
  208.  
  209. UPPERMAX = This value determines the highest intensity of our envelope.
  210.  
  211.  
  212.  
  213. NOTE:   Similar to LOWMIN and LOWMAX, UPPERMIN and UPPERMAX give us a range
  214. for  the  peaks  (or  maximum  values) of our intensity envelope.  The peak
  215. values  will  never be higher than UPPERMAX (i.e.  40) and never lower than
  216. UPPERMIN (i.e.  20).
  217.  
  218.  
  219.  
  220. ENVELOPES  = The total number of Envelopes you want to generate using these
  221. settings.
  222.  
  223.  
  224.  
  225.  
  226.  
  227. Lines 27-35
  228.  
  229.      Once  we have set up the requesters, we usually need to send them some
  230. default  values.   Note  that  we  CALL  the REQ_SETVAL() function for each
  231. requester  to  do this.  The first argument is the ID name of the requester
  232. (for example, RampID).  The second is the actual value (for example, a 1 or
  233. 0  for the Boolean ON or OFF).  The rest of the functions and there default
  234. values should be self-evident.
  235.  
  236.  
  237.  
  238. Line 37
  239.  
  240.      The  function  REQ_POST() sets up the requester with the Okay, Cancel,
  241. and  Reset  options  already  enabled.   It  waits  for  the  user to input
  242. everything  and  select  either Okay or Cancel.  Reset automatically clears
  243. all  variables.   If  okay is pressed, then X is set to 1 by this function.
  244. If Cancel is pressed, then X is set to 0.
  245.  
  246.  
  247.  
  248. Line 39
  249.  
  250.      A standard IF - THEN conditional statement.  If X=1 (therefore we know
  251. Okay  was  pressed)  then  DO  everything  that follows.  If this check was
  252. failed  (therefore, Cancel must have been pressed and X must be 0) then the
  253. program  drops  to  the  END statement that matches this IF statement (i.e.
  254. line 130).  The next command after that is EXIT and so the program ends.
  255.  
  256.  
  257.  
  258.      With  just  those  few  lines,  we set up an entire program front-end,
  259. prompted the user for lots of input, with default values given as examples,
  260. and  waited  for the user to either abort the program, reset the values, or
  261. tell  us  to proceed!  The Modeler and ARexx do all the work for us and our
  262. interface  is  guaranteed  to  behave and look professional like all of the
  263. other Macros written for the Modeler.
  264.  
  265.  
  266.  
  267. Lines 40-48
  268.  
  269.      Now  that  the  user has told us to begin, we need to read back in the
  270. values  he  or  she  entered  using  the  REQ_GETVAL()  function.  The only
  271. argument  we  pass  to  the  function  is  the name of our ID (for example,
  272. EndFrameID).   We  then  assign  the  value  returned  by the function to a
  273. variable  (for  example,  ENDFRAME).  We do the exact same thing for all of
  274. our requesters.
  275.  
  276.  
  277.  
  278. Lines 51-55
  279.  
  280.      We  cannot  always be sure our users give us exactly what we want.  To
  281. avoid  generating  an  error later on in the program, we check to make sure
  282. that  the  LOWMIN  value  is  indeed  less than the LOWMAX value.  From the
  283. user's  stand  point it doesn't matter, as long as the range is between two
  284. values,  but  internally  our  own functions require these two values to be
  285. ordered correctly.
  286.  
  287.      First  we  check  to  see IF LOWMIN is greater than LOWMAX.  If it is,
  288. then  we  DO  the next series of commands.  If it isn't then we skip to the
  289. END of the IF statement (i.e.  line 55) and proceed on with the program.
  290.  
  291.      Assuming  our user entered in a LOWMIN value that was greater than the
  292. LOWMAX  value,  we need to switch these around before proceeding.  First we
  293. assign  the  LOWMIN value to a temporary variable called TEMP.  Now that is
  294. is  saved  for later, we set LOWMIN equal to LOWMAX, and then assign LOWMAX
  295. the  old  LOWMIN  value  stored  in TEMP.  The old variable switcheroo is a
  296. classic programming example.  Now we can proceed on with our program
  297.  
  298.  
  299.  
  300. Lines 57-61
  301.  
  302.      These  lines  perform  the  exact  same function as Lines 51-55 above,
  303. except they assure than HIGHMIN is always less than HIGHMAX.
  304.  
  305.  
  306.  
  307. Line 63-67
  308.  
  309.      These  lines  again  perform  the  same  function  as the previous two
  310. examples,  but  instead  insure than our MINFRAME offset value is less than
  311. our MAXFRAME offset value.
  312.  
  313.  
  314.  
  315. Lines 69-70
  316.  
  317.      Now  that  we  are  sure all our data is useful, we need to prompt the
  318. user for some additional input.  We use the GETFILENAME() function to get a
  319. path  and  root  file name for the envelope(s) we are about to create.  The
  320. first  argument  passed to the function is a title for the requester, while
  321. the  second  is  a  default path, which I chose to be LightWave's Envelopes
  322. drawer.   As  before in our program, the Modeler does all the work with the
  323. requester  in  one  line!   The entire path and filename is returned to the
  324. program  and  we assign that to a variable called ROOT_NAME.  We then check
  325. to  see  IF  ROOT_NAME equals "(none)", which is the function's message for
  326. "no response." If it is then we EXIT the program, otherwise we proceed with
  327. our program.
  328.  
  329.  
  330. Lines 72-73
  331.  
  332.      Using  the GETFILENAME() function, we can ask our user to input a path
  333. and  a  root  name  for  our envelope(s).  The first argument passed to the
  334. funtion lets us name our requester for the benefit of our user.  The second
  335. argument  lets  us automatically log in to a default path (you will need to
  336. change this to suit your needs).
  337.      The  second line checks to see if the user failed to enter anything or
  338. selected abort, in which case the requester returns the string "(none)" and
  339. therefore we exit the program.
  340.  
  341.  
  342. Line 76
  343.  
  344.      Here  begins  the  main body of our program.  This first line begins a
  345. loop  (called  a  DO  loop) where we will step the variable N from 1 to the
  346. variable  ENVELOPES  which our user entered earlier (Default is 1).  All of
  347. the  program  between  this line and the matching END statement for this DO
  348. loop  (i.e.   line 129) will be run again and again until N = ENVELOPES, at
  349. which  point  the  program  drops  to  line  130,  which  ENDS  the main IF
  350. conditional, and then EXITs the program.
  351.  
  352.  
  353. Lines 77-80
  354.  
  355.      Users  who  have  previously  programmed  will recognize the following
  356. lines  as  one of those annoying little file-management necessities.  Since
  357. we  might  be  creating  hundreds of envelopes, we can give them all unique
  358. names by appending a number to the end of the root name.  However, when you
  359. append  the  number  2  directly to a filename (i.e.  Random.2) and a 10 to
  360. that  same  root  name  (i.e.  Random.10) programs and requesters that sort
  361. those  values  return  them in the wrong order (i.e.  Random.10, Random.2).
  362. What  we  really  need  is to 'pad' out the number with leading zeros (i.e.
  363. Random.002, Random.010, Random.100).
  364.  
  365.      The first line sets our PAD variable to a null string "", with nothing
  366. in  it.   Then we check to see if N, our envelope number, is less than 100.
  367. If it is, add a "0" to our PAD string.  Next, if N is less than 10, we need
  368. to  add  another  "0"  to our PAD string.  Finally, our FILE_NAME is set to
  369. equal  our ROOT_NAME combined with our PAD string and finally combined with
  370. N, our envelope number.  NOTE that strings are combined, or concatenated in
  371. programmer's  parlance,  with  the  ||  characters  (i.e.  strike shift '\'
  372. twice).
  373.  
  374.  
  375.  
  376. Lines 82-85
  377.  
  378.      We  are  now  ready  to open our new envelope file!  We call the ARexx
  379. OPEN()  function  to do this.  The first argument (i.e.  'out') is the name
  380. our  program  uses  to  address this file.  It can be anything we want, but
  381. make  sure that each file you have open at the same time has a unique name,
  382. to  keep from getting the open files confused).  The second argument is the
  383. actual  AmigaDos  path  and file name of the file we are opening (i.e.  the
  384. variable  FILE_NAME  we  just created above).  The third argument 'W' tells
  385. the  OPEN()  function  that  we want to Write to this file.  To read from a
  386. file use the 'F' argument.
  387.  
  388.      Now  that  are  file is created and open, we send it some initializing
  389. lines  that  LightWave requires to recognize this text file as a legitimate
  390. LightWave  envelope file).  The WRITELN() function sends a complete line of
  391. text  to  our  file.   We call the function with the ARexx file name of our
  392. file  (i.e.   'out')  as the first argument, and the actual text we want to
  393. write out (i.e.  'LWEN') and the second argument.  The next two lines write
  394. additional  data  required  by LightWave.  Do not change any of these three
  395. lines or LightWave will not recognize your file!
  396.  
  397.  
  398.  
  399. Line 87
  400.  
  401.      This  line  initializes our LASTFRAME variable to 0 each time we begin
  402. writing  a  new  envelope.  The LASTFRAME variable is used to keep track of
  403. the last frame number we have processed during our program.
  404.  
  405.  
  406.  
  407. Line 88
  408.  
  409.      This line initializes our K variable to 1 each time we begin writing a
  410. new  envelope.   K is used to keep track of the actual number of key frames
  411. we have created in a given envelope.
  412.  
  413.  
  414.  
  415. Line 89
  416.  
  417.      We  need  to  decide  whether our envelope begins within a valley or a
  418. peak  (i.e.  using LowMin/LowMax or UpperMin/UpperMax as values).  We could
  419. have  just picked a value, either 0 for valley or 1 for peak for all of our
  420. envelopes, but again I chose to make the results as random as possible.
  421.  
  422.      My  variable  MINMAX  is going to alternate between 0 and 1 throughout
  423. the  program, so I want it to start with a random value between 0 and 1.  I
  424. assign  MINMAX  the  result  of  the  ARexx  RANDOM()  function,  using the
  425. arguments  0 and 1 as the minimum and maximum values for the function.  The
  426. result of the RANDOM() function is always an integer, so it can only return
  427. a 0 or a 1.
  428.  
  429.  
  430.  
  431. Line 91
  432.  
  433.      Before  we  begin  our  loop  to generate random values and key frames
  434. (i.e.   line  92), we are going to get a little fancy and take advantage of
  435. one of Modeler's built-in interface features, the progress meter!
  436.  
  437.      We  call the METER_BEGIN() function with two arguments.  The first one
  438. is  the number of steps we expect our meter to be updated by.  For example,
  439. if  we  knew  we were going to be processing 10 key frames, we might send a
  440. value  of  10  here,  so that we'd see the meter update 10 times during our
  441. upcoming  loop.   The second argument is the name or title that we pick for
  442. the meter requester.
  443.  
  444.      Our  only  problem  here is that we don't really know exactly how many
  445. key  frames  we  are  going  to create because the offsets between them are
  446. generated  randomly  (see below).  Since the meter will error if we step it
  447. too  many  times, but not if we step it too few, it is better to err on the
  448. side  of  caution.  We know that the minimum distance between key frames is
  449. the  variable  MINFRAME  and the maximum frame count of our envelope is the
  450. variable  ENDFRAMES,  so  the maximum number of key frames we could have in
  451. our  envelope is ENDFRAMES%MINFRAME (remember % is just an INTEGER divide).
  452. We pass that function as the first argument to our meter function.
  453.  
  454.  
  455.  
  456. Line 92
  457.  
  458.      Now  we  are  ready  to  begin  generating  the  key  frames and their
  459. intensities  one after another, until we reach or exceed the EndFrame value
  460. of  our  program.   Our  control  for this DO loop is the UNTIL keyword and
  461. LASTFRAME  >  ENDFRAMES  condition.   In  other  words,  the  program lines
  462. contained  with  the DO loop and its accompanying END statement (i.e.  line
  463. 102)  will  be  repeated  forever  until the value of the LASTFRAME variable
  464. exceeds the value of the ENDFRAMES variable.
  465.  
  466.  
  467.  
  468. Line 94
  469.  
  470.      If  our  MINMAX  variable  = 0 then we have a valley and our INTENSITY
  471. should   be  based  on  the  RANDOM()  value  between  LOWMIN  and  LOWMAX.
  472. Otherwise, proceed to the next line of our program.
  473.  
  474.  
  475.  
  476. Line 95
  477.  
  478.      If  our  MINMAX  variable  =  1  then we have a peak and our INTENSITY
  479. should  be  based  on  the  RANDOM()  value  between UPPERMIN and UPPERMAX.
  480. Otherwise,  proceed to the next line of our program.  By this time, we have
  481. covered both of the possible values of MINMAX, 0 and 1.
  482.  
  483.  
  484.  
  485. Line 96
  486.  
  487.      The actual frame number for the above INTENSITY variable is determined
  488. by  adding  a  RANDOM()  frame  offset between MINFRAME and MAXFRAME to the
  489. LASTKEY  value  of  the previously created key frame.  In this fashion, our
  490. key frames keep increasing one after another by a random amount.
  491.  
  492.  
  493.  
  494.  
  495.  
  496. NOTE  that we are creating an ARRAY of the variables INTENSITY and FRAME by
  497. creating  variables with the ".k" suffix.  For example, if K is 56, then we
  498. are  working  with  INTENSITY.56  and FRAME.56.  Each of these variables is
  499. unique and can have their own distinct values.  ARexx automatically sets up
  500. variable  space for our array as we go!  This is usually a BIG pain in most
  501. programming languages.
  502.  
  503.  
  504.  
  505. Line 97
  506.  
  507.      There  is  only  one  instance  when we know what the frame value of a
  508. given  key frame before hand.  The first key frame (i.e.  K=1) is always 0.
  509. In  this  line we check to see if K=1, and if it is we make the FRAME value
  510. equal to 0 automatically.
  511.  
  512.  
  513.  
  514. Line 98
  515.  
  516.      We  update  the  LASTKEY to our current FRAME value, so that next time
  517. around we build a new key frame starting on the value of this one.
  518.  
  519.  
  520.  
  521. Line 99
  522.  
  523.      We  add  1  to  K,  which  keeps internal track of our total number of
  524. created key frames so far.
  525.  
  526.  
  527.  
  528. Line 100
  529.  
  530.      Through  a  clever and standard programmer's trick, we force MINMAX to
  531. constantly  toggle  between 0 and 1 every time we pass through this program
  532. loop  by  subtracting MINMAX from 1 and then assigning the result to MINMAX
  533. again.   If  MINMAX  was  0 before then 1 - 0 = 1, so MINMAX becomes 1.  If
  534. MINMAX  was  1 before then 1 - 1 = 0, so MINMAX now becomes 0 the next time
  535. around.
  536.  
  537.  
  538.  
  539. Line 101
  540.  
  541.      To  update  our  meter  to show we have proceeded one time through our
  542. loop, we call the METER_STEP() function.  There are no arguments as Modeler
  543. again takes care of all its own housekeeping.
  544.  
  545.  
  546.  
  547. Line 102
  548.  
  549.      The END of this DO loop.  The program now checks to see if the updated
  550. LASTFRAME  value  is greater than the ENDFRAMES value (see Line 92).  If it
  551. isn't   we  start  this  loop  again,  creating  more  random  intensities,
  552. offsetting  new  frame  numbers,  toggling MINMAX, increasing our key frame
  553. total, K.  If it finally is, we drop to the next line of our program.
  554.  
  555.  
  556.  
  557. Line 103
  558.  
  559.      Now  that  we  have  completed  all of our calculations, it is time to
  560. close that requester we created in Line 91 that we've been stepping through
  561. during  that last loop.  We CALL the METER_END() function with no arguments
  562. to do this.
  563.  
  564.  
  565.  
  566. Line 106
  567.  
  568.      We  automatically added 1 to K at the end of that last loop, but since
  569. LASTFRAME  has  to  be greater than ENDFRAMES by this point in the program,
  570. there  never were any values assigned for INTENSITY and FRAME for this last
  571. value  of  K.   Since  this  is  our total number of key frames pointer, we
  572. should  make sure this is accurate by subtracting 1 from the final value of
  573. K.  Everything matches up now.
  574.  
  575.  
  576.  
  577. Line 107
  578.  
  579.      Remember  that  envelope file we opened up and initialized way back at
  580. lines  82-85?   Well,  LightWave  requires  one more thing before it starts
  581. reading  all  your intensity and key frame values...the total number of key
  582. frames in the envelope - our variable K.  Good thing we kept track of that,
  583. huh?
  584.  
  585.      We  call  the WRITELN() function and send the value of K as the second
  586. argument  to  the  program  file  identified as 'out' by us.  You should be
  587. getting use to the WRITELN() function by now.
  588.  
  589.  
  590.  
  591. Line 109
  592.  
  593.      Because  of  a feature I added in later, I have to run our intensities
  594. through  some  more  calculations  if the user has requested us to ramp the
  595. intensities down to our LOWMIN value across the envelope.  Since we did not
  596. know  the  final  key frame until after everything was calculated, we could
  597. not  perform  this function until now.  We begin with a simple IF statement
  598. that checks to see if RAMP=1, meaning 'yes, ramp the values'.  If it is, we
  599. do  everything  between  lines 110 and 115.  If not, we drop through to the
  600. END  statement  on  line 116 and proceed with the program.  Note that ARexx
  601. assumes  the  line IF RAMP THEN DO means if RAMP is BOOLEAN TRUE (numerical
  602. shortcut  is  1).   You  do  not  need  to expressly say IF RAMP=1 THEN DO.
  603. Notice that we could change line 37 to read IF X THEN as well, but I wanted
  604. to  give  you an example of both for this programming tutorial.  Notice how
  605. well Boolean logic works with Boolean requester function like the RampID.
  606.  
  607.  
  608.  
  609. Line 110
  610.  
  611.      Let's  start  a  new  meter  for  our  ramp  operation  by calling the
  612. METER_BEGIN()  function  like we did in line 91 earlier.  However, since we
  613. now  know  the  total number of key frames we have to process (i.e.  K), we
  614. can  tell  the  meter that we are sure to step K times through the meter as
  615. our first argument.
  616.  
  617.  
  618.  
  619. Line 111
  620.  
  621.      We set up a DO loop that will iterate the variable 'I' starting from 1
  622. and ending with K.  The END of this loop is on line 114.
  623.  
  624.  
  625.  
  626. Line 112
  627.  
  628.      Now  we  are  going  to Ramp our intensities lower until the final key
  629. frame is always equal to LOWMIN.
  630.  
  631.      Our formula does the following:
  632.  
  633.  
  634.  
  635. A) First it subtracts the LOWMIN value from the previous INTENSITY value so
  636. we are only going to scale in the range greater than LOWMIN.
  637.  
  638.  
  639.  
  640. B)  Then  we  calculate  the  percentage of ramping by dividing the current
  641. FRAME  value  by  the  maximum  frame value represented by LASTFRAME value.
  642. This  value  will  range  between  0  (at the beginning) and 1 (at the end)
  643. during the envelope.
  644.  
  645.  
  646.  
  647. C)  Since  we  want  to ramp DOWN, we subtract the result from B) from 1 so
  648. that the percentage is reversed.
  649.  
  650.  
  651.  
  652. D) We then multiply the percentage from C) times the adjusted FRAME value.
  653.  
  654.  
  655.  
  656. E)  Finally, we add LOWMIN again so that everything is shifted back the way
  657. we started and reassign the new value to FRAME.
  658.  
  659.  
  660.  
  661.      Can  you  figure  out  how to ramp up to UPPERMAX across the envelope?
  662. This  is  our  only nasty formula requiring some genuine math logic.  Sorry
  663. about that.
  664.  
  665.  
  666.  
  667. Line 113
  668.  
  669.      We  tell  the progress meter to step forward since we've completed one
  670. key frame by calling the METER_STEP() function as before.
  671.  
  672.  
  673.  
  674. Line 114
  675.  
  676.      END the loop and step forward until it reaches the final value of K.
  677.  
  678.  
  679.  
  680. Line 115
  681.  
  682.      We  are done with the ramp progress meter by this point in the program
  683. so close the meter with a CALL METER_END() function.
  684.  
  685.  
  686.  
  687. Line 116
  688.  
  689.      END  of  the  conditional IF statement that started this whole ramping
  690. mess.  On with the rest of the program.
  691.  
  692.  
  693.  
  694. Line 119
  695.  
  696.      Let's  open  one  final  progress  meter  to  begin  wrapping  up this
  697. envelope.  Again we'll step K times through the meter.
  698.  
  699.  
  700.  
  701. Line 120
  702.  
  703.      We're  going  to  loop  from  1 to K again, with a DO loop like above.
  704. This loop ENDs on Line 125.
  705.  
  706.  
  707.  
  708. Line 121
  709.  
  710.      By calling the WRITELN() function again, we write our INTENSITY values
  711. to  the  'out'  file.   We  divide  the INTENSITY by 100 because that's how
  712. LightWave likes it.
  713.  
  714.  
  715.  
  716. Line 122
  717.  
  718.      The  WRITELN()  function  sends  the  FRAME  and preset spline control
  719. settings  (i.e.   "0.0  0.0  0.0"  is Tension Continuity Bias) to our 'out'
  720. file.
  721.  
  722.      You  should now be able to figure a way for the user to set default or
  723. random spline settings in your version of the program.
  724.  
  725.  
  726.  
  727. Line 123
  728.  
  729.      Let's step that meter now that we've written out another key frame.
  730.  
  731.  
  732.  
  733. Line 125
  734.  
  735.      END  of the DO loop in line 120 that sends us back to the next I value
  736. until it reaches K.
  737.  
  738.  
  739.  
  740. Line 126
  741.  
  742.      We have finished stepping our meter, so let's close it out.
  743.  
  744.  
  745.  
  746. Line 127
  747.  
  748.      We  CALL  the  CLOSE()  function  to  close  the 'out' file we've been
  749. creating  all  this  time.   It  is  now  complete  and  can  be  loaded in
  750. LightWave's intensity envelope editor.
  751.  
  752.  
  753.  
  754. Line 129
  755.  
  756.      This is the END of the DO loop in line 76.  We'll start creating a new
  757. envelope  until  N reaches the value of ENVELOPES at which point we will go
  758. to the next line of the program
  759.  
  760.  
  761.  
  762. Line 130
  763.  
  764.      This is the END of our conditional IF that started this whole process.
  765. The  use  has  either  pressed  Cancel  or  all  of the envelopes have been
  766. generated.  In either case, proceed to the next line of the program.
  767.  
  768.  
  769.  
  770. Line 131
  771.  
  772.      EXIT the program and return Modeler control to the user.
  773.  
  774.  
  775.  
  776. END OF PROGRAM
  777.  
  778.  
  779.  
  780.  
  781.  
  782.      WHEW!!!   Hopefully, this showed you enough of the basic Modeler macro
  783. programming   functions  and  philosophy  to  get  you  started.   In  your
  784. Toaster/Arexx_Examples/LWM   directory   there   is   a  text  file  called
  785. ModelerARexx.doc.   Everything else you need to know to program the Modeler
  786. can be found there and most of it shouldn't look like APL to you after this
  787. example.   For  examples, just hunt and peck through the abundance of Arexx
  788. macros until you find something you need.
  789.  
  790.      Before wrapping this example up, I'd like to thank Stuart Ferguson for
  791. adding these features to the new Modeler.  Maybe next program I'll actually
  792. use  more  of  the  Modeler's features than just its interface handling!  I
  793. have  some  old  celestial  mechanics code I did in high school that's just
  794. dying for a rewrite.
  795.  
  796.      I  would also like to thank Arnie Cachelin of NewTek for his abundance
  797. of  Modeler  ARexx  macros I stole from heavily to produce this program.  I
  798. can only hope you readers do the same from me.
  799.  
  800.