My own experience shows that MAXScript is easier than people think, and the biggest problem to learn and use it is to find an idea to start. The following discussions will NOT teach you MAXScript for Gurus. It will show you the basics and that there is nothing to be afraid of - MAXScript is easy, flexible, and FUN even for non-programmers.


As Espen Sande Larsen suggested on the discreet Online Forum, http://support.discreet.com.com
we will start with a short code that reads pixel information from a bitmap and creates geometry based on the pixel data.

We will also see that the same code can be used to create a multitude of effects by just changing a single line.

In this first step, we are going to write just the basic function code, not the interface (it will follow in LESSON 02).
Some values will be "hard-coded" in this first version, and will become user-defined in the later versions.

The complete script source and the required bitmap image (40x40x32bit) showing a single M on black background can be found under

Download the ZIP file, create a new directory on C: drive called "MXSTUT" and copy the content of the ZIP file into it.

Some words on the MAXScript (MXS) usage:

box ()

and press the [Enter] key in the NumPad (not the Return key!) A new Box with default settings will appear at coordinates [0,0,0]

You can read more about these different modes in the MAXScript Online Help.

Now let's write our script:


work_bmp = openbitmap "c:\\mxstut\\m.tga"
A Bitmap is a MAX object and as every MAX object used in MAXScript it is stored in a variable.
The openbitmap command opens an existing image. The file type can be any type supported by MAX,
including animations (multi-frame bitmaps) like AVI, MOV and IFL.
NOTE that you have to use \\ instead of \ when specifying paths in MAXScript!

The image looks like this (scaled 3 times here):

bmp_w = work_bmp.width

bmp_h = work_bmp.height
We create two new variables to hold the current size of the loaded image.
If you change the path to load a different image, the script will still work correctly.
The user variable work_bmp represents the bitmap object.
Every object in MAX has its own properties.
Properties are accessed by typing the object followed by a dot "." and the property name.
To check what properties are available for a certain object, you can either look in the Online Help or type
showproperties object

For example:
my_box = box ()                    [Enter]
showproperties my_box    [Enter]
  .height : float
  .length : float
  .lengthsegs : integer
  .width : float
  .widthsegs : integer
  .mapCoords : boolean
  .heightsegs : integer

progressstart "Generating Objects..."
MAXScript provides some commands to display and control a progress indicator.
We just started a new one.
It displays the prompt text and still does not update.

for h = 1 to bmp_h do
This is a LOOP which consists of a variable, a start and end value.
The variable changes the value starting with the start value and stops growing when the end value is reached.
Our variable h starts with 1 and ends with the last line in the bitmap, bmp_h
All statements following the DO statement and between the brackets ( ) are repeated until the end value is reached.
It is a good idea to use a REMARK like "--end x loop" to denote the closing bracket so you can easily identify closing brackets.
Also, you can indent the whole loop for visual clearness.
You can also click near a bracket and press the Ctrl+B keys to invoke the "Bracket Balancer".
It shows the bracket pairs in the script by selecting them and any code inbetween.
NOTE that loops in MXS can be used in many different ways - to collect scene objects etc.

pixel_line = getpixels work_bmp [0,(h-1)] bmp_w
We start reading the Bitmap now.
"pixel_line" is the user name of an ARRAY that will hold a whole row of pixels.
An array is a one-dimensional list of values which can be accessed using an index value.
All arrays in MAXScript are 1-based, this means that the first element has index 1.
For example, the first pixel in our case wouldl be accessed through pixel_line[1]
MAXScript uses dynamic arrays that change their size and have automatic memory management as you add values to the list.
If you want to initialize an empty array, you have to write my_array = #().
Since a MAXScript variable can be a scene object, text string or number, you can for example build lists of scene objects, or their names, or any of their numeric properies...

The getpixels command operates on the bitmap object.
It expects two values for the X and Y coordinates of the first pixel  [X,Y] and a number of pixels to read (in our case the width of the image)
This function is relatively slow, it is a good idea to read as many pixels as possible into an array, and access the array elements instead of
getting single pixels.
NOTE that other than arrays, a Bitmap is a 2-dimensional 0-based list, where the first pixel in the upper left corner has coords [0,0] and NOT [1,1].
You must keep this in mind when looping through the image.
This is why we have the [0,h-1] instead of [1,h].

progressupdate (h as float /bmp_h *100)
We have already started a new progress indicator.
Now we will update it.
It accepts values between 0 and 100.(%).
By dividing the current loop value taken as a floating point number through the end value, we get values between 0.0 and 1.0.
Multiplied with 100 it returns what we need - percentage between 0 and 100.
The progress indicator can be also used to test the Esc/Cancel user input, but we will skip this today.

for w = 1 to bmp_w do
Now we start a second loop inside of the first one that counts from 1 to the last pixel in the row.

size_value = 100 + (pixel_line[w].r+pixel_line[w].g+pixel_line[w].b)/3
This line is the actual formula used to get a relation between the pixels and the geometry we will create.
In the future, we will play with different formulas to get lots of functions in the same script.
In this case, we take the Red, Green and Blue values from the single pixel variable's color saved in the array.
We add them together and divide by 3.
Then we add 100 to have at least 100 units for our geometry when the pixel is Black [0,0,0].
The value is thus based on the pixel's LUMINOSITY. (Greyscale Intensity).
You could easily change the formula to something like
size_value = 100 + (pixel_line[w].r)
In this case, only the red value will be used.
Pure red, violet and yellow colors will result in a value of 355 (100+255).
Any other colors with less Red components will be lower, down to 100. (100+0)

new_object = box length:100 width:100 height:size_value
Now we create a new BOX primitive with length and width = 100, and the already calculated value as height.
The new object is put into a new variable (as every MAX object).
We can now access the object's properties through variable name, dot, property name:

new_object.pos = [w*100, (-h*100), 0]
The .POS (or .POSITION) property defines the position in 3D space of any geometry object.
We use a Point3 value in the form [X,Y,Z] to specify 3D coordinates.
NOTE that in MAX, [0,0,0] is lower left corner when seen from the Top Viewport
In images, the UPPER left corner is [0,0] - this explains the sign in (-h*100)
100 is the distance between objects.

new_material = standardmaterial diffuse:pixel_line[w]
We create a new standard material with diffuse color taken from the pixel's color.
The material is also a MAX object saved in a variable and has its properties.

new_object.material = new_material
We change the .MATERIAL property of the new object - we assign the new material variable to it.

)--end w loop
Before the w loop has finished, the script WILL NOT go any farther...

)--end h loop
Everytime the w loop finishes, the h loop increases by 1. After it has finished, the script goes on...

progressend ()
We hide the progress display now.

close work_bmp
We also close the bitmap.


 This is what you should get after executing the script (Ctrl+E):

In the next LESSON 02, we will create a User Interface to the utility, and define some variables to achieve different effects.

Have fun!

Borislav Petrov [FA]