In this third step, we are going to extend the utility to make it more professional.
Error traps, bitmap info function, scene clean up and more will be implemented.

The complete script source can be found under

Download the ZIP file and copy its content into the same directory as in LESSON 01.

Now let's extend our script:


utility lesson03 "Lesson03"

group "About..."
label lesson_label01 "Lesson 03 Script"
label lesson_label02 "by Borislav Petrov"
label lesson_label03 "Kinetix Forum Assistant"

group "Settings:"

radiobuttons geom_mode "Geometry" labels:#("Box","Sphere") align:#left
This new UI element is a set of radio buttons.
Radio buttons are similar to drop-down lists, but allow the user to see all possible options all the time.
The options are listed in an array after the labels: statement.

dropdownlist effect_mode "Effect Mode" items:#("Luminosity","Red Channel","Green Channel","Blue Channel","Alpha Channel")
Note the new 5th item "Alpha Channel".
With this change and an additional line in the case statement in the code bellow we extended the utility to use Alpha data.

spinner sphere_segs "Sphere Segments:" range:[4,16,4] fieldwidth:40 align:#right type:#integer enabled:false
This is an additional value spinner which is disabled by default.
The keyword enabled: can be used for any UI element, and accepts boolean values - TRUE or FALSE.
Because the radio buttons show the Box as default selection, we don't need the Sphere segments yet.

spinner base_size "Base Size:" range:[1,1000,100] fieldwidth:40 align:#right
spinner multiplier "Size Multiplier:" range:[0.1,10,2] fieldwidth:40 align:#right
spinner nth_pixel "Every Nth Pixel:" range:[1,10,1] fieldwidth:40 align:#right type:#integer
button get_image "Get Image File" width:140

edittext bmp_name "File" text:"c:\\mxstut\\m3.tga"
Note that the name has changed to m3.tga, which is a NONEXISTING file.
We need this to test our Error Trap :o)

label size_label "Image Size:???x???"
This new label is used to display the size of the currently selected bitmap.


group "Action"
button start_process "LET'S DO IT!" width:140 height:30 align:#center enabled:false
In this new group, the start_process button is disabled by default.
It will be enabled after the Error Trap has checked for valid image file.

button remove_all "Clean up scene" width:140 align:#center
This new button will let the user delete all objects created by the utility.


fn Check_bmp_size bmp_n =
We are starting a FUNCTION DEFINITION here.
FUNCTIONS are used to automatize scripted actions that are used often by different parts of the code.
A function starts with fn , has a user-defined name and accepts any number of parameters followed by = and the function body.
The parameters have to be specified when the function is called, and can be used inside of the function body only.
At the end of the function, one can specify a calculated value which is returned as result. (We don't use this in our case).
Our function is a PROCEDURE - it does certain jobs and sets some object properties without returning any values back.

MAXScript provides only one very basic Error Trap in the form try (...) catch (...)
The statements inside of the try body are executed, and if ANY error occurs, the statements in the catch body are executed.

bmp_check = openbitmap bmp_n
bmp_w = bmp_check.width
bmp_h = bmp_check.height
close bmp_check
size_label.text = "Image Size:" + bmp_w as string + "x" + bmp_h as string
The function opens the bitmap with the name sent as parameter and checks its size (See LESSON01).
If no errors are encountered while doing this, the last line puts the size variables as text strings into a single string.
This string is assigned to the .text property of the size_label

start_process.enabled = false
If any errors have been encountered (the only error could be: an existing file name has been opened and it wasn't a bitmap),
the above statement will be processed and the start button will be disabled, so the user cannot do anything wrong.

)--end of catch
)--end of function

on geom_mode changed state do
The radiobuttons element returns a property called .state which contains the current option setting.
The above line checks for changes to the radio button settings.

if state == 1 then sphere_segs.enabled = false else sphere_segs.enabled = true
In case the state has changed to 1 (Box), the Sphere Segments value is not allowed and is being disabled.
In the other case (Sphere), the value is needed and is being enabled.

on bmp_name entered txt do
This line checks if the user has entered new text in the edittext UI element.
The events MXS scans for are ENTER and TAB being pressed.
The new text is written in the variable following the entered statement, in our case txt

 file_check = getFiles bmp_name.text
The command getFiles followed by a wildcard path name returns an array of the files found.
Since we call it using an unique file name instead of a wildcard path, the result can be either a single name, or none at all.

 if file_check.count == 1 then
We check if the count of the elements in the array equals to 1 (file found) or 0 (file not found).

 start_process.enabled = true
If the count of discovered files is 1, the file seems to be an existing one.
This means we can enable the start button for now...

 Check_bmp_size bmp_name.text
Now we call our self-defined function with the name of the bitmap as parameter in order to update the Size Info label,
and to make sure the encountered exisiting file is REALLY a bitmap.
If the existing file isn't a bitmap, the  function will disable the start button again.
 size_label.text = "Image Size:???x???"
 start_process.enabled = false
This is what happens in case the getFiles command did not find any files.
The Image Size label is set to UNKNOWN, and the start button is disabled again.


on get_image pressed do
pick_image = selectBitmap caption:"Get Image File"
if pick_image != undefined then
 bmp_name.text = pick_image.filename

 start_process.enabled = true
 Check_bmp_size bmp_name.text
Because we know that the selectBitmap cannot select anything else than a valid bitmap, we enable the start button.
We also update the Size Info.


on start_process pressed do
work_bmp = openbitmap bmp_name.text
bmp_w = work_bmp.width
bmp_h = work_bmp.height

progressstart "Generating Objects..."

for h = 1 to bmp_h by nth_pixel.value do
 pixel_line = getpixels work_bmp [0,(h-1)] bmp_w

 if not progressupdate (h as float /bmp_h *100) then exit
As mentioned in LESSON01, the progressupdate can be used to check for user Cancel/Esc.
In case the user has pressed Esc or the Cancel button, a prompt appears.
If the user selects Yes, the progressupdate returns FALSE.
The NOT statement inverts it to TRUE and the if check executes the exit command.
The exit command cancels all the loops and event checks and returns control to the main part of the script.

 for w = 1 to bmp_w by nth_pixel.value do

  case effect_mode.selection of
  1: size_value = base_size.value + multiplier.value*(pixel_line[w].r+pixel_line[w].g+pixel_line[w].b)/3
  2: size_value = base_size.value + multiplier.value*(pixel_line[w].r)
  3: size_value = base_size.value + multiplier.value*(pixel_line[w].g)
  4: size_value = base_size.value + multiplier.value*(pixel_line[w].b)
  5: size_value = base_size.value + multiplier.value*(pixel_line[w].alpha)
This additional line enables the access to the Alpha channel of the bitmap.

  case geom_mode.state of
We have this additional case check to create different geometry according to the radio buttons state.

  1: new_object = box length:base_size.value width:base_size.value height:size_value
  2: new_object = sphere radius:(size_value/2) segs:sphere_segs.value
This additional line creates a sphere object with radius half the calculated value, and segment count based on the spinner value.

  new_object.pos = [w*base_size.value, (-h*base_size.value), 0]
  new_object.name = uniquename "3rd_Lesson"
  new_material = standardmaterial diffuse:pixel_line[w]
  new_material.name = new_object.name
  new_object.material = new_material

  )--end w loop

 )--end h loop

progressend ()

max views redraw
The max command provides a way for executing standard MAX functions available through mouse clicks or keyboard shortcuts in the main MAX UI.
For the whole list, check the MAXScript Online Help.
This particular line executes the Redraw All Viewports MAX command.

close work_bmp

)--end on button pressed

on remove_all pressed do
This is the event check for the Clean up button.

q_answer = queryBox "You are about to delete all objects\ncreated by the Utility.\nAre you sure?"
The queryBox command prompts with the specified text and two possible answers - YES or NO.
The variable q_answer becomes TRUE when the user has clicked YES.
NOTE that you have to use the "\n" character to denote CariageReturn (ENTER).

if q_answer then
q_answer can be only TRUE or FALSE, so we don't need to write if q_answer == true then - it is the same.
If the user has answered YES (TRUE)...

 delete_array = $3rd_Lesson* as array
...a new array is created containing all objects with names starting with "3rd_Lesson".
Scene objects can be accessed using Path Names beginning with the sign $ and the object's name.
You can use the wildcard * to sequence all matching names.

 delete delete_array
The whole array is deleted from the scene.
The delete command deletes single scene objects, or (as in this case) arrays of objects.

on lesson03 open do
MAXScript provides you with a way to execute code when  the utility is starting.
You can use the on...open to perform initialization works, like checking for the MAXScript version used, opening floaters and rollouts etc.
A similar event check, on...close lets you perform certain actions when the program is closing, like deleting temp. files, closing floaters and rollouts etc.
Both require the utility name.

 file_check = getFiles bmp_name.text
 if file_check.count == 1 then
 Check_bmp_size bmp_name.text
 start_process.enabled = true
We perform the same check as with the "Get Image File" button, but just once when the utility is started.
This way the utility can make sure the default image is really present.

 messagebox "The Image file couldn't be found!\nPlease select a valid one."
The messagebox command provides just a text message with an OK button.


)--end on

)--end utility

Here is what happens when you execute the script with
Sphere, RGB mode, Sphere Segments 4, Base 100, Scale 2.0, Nth 2


Have fun!

Borislav Petrov [FA]