[LESSON 01][LESSON 02][LESSON 03] [LESSON 04] [LESSON 05]

MAXSCRIPT PUBLIC TUTORIALS - LESSON 05


CONTENTS:

In this fifth  step, we are going to add animation capabilities to the utility.
We are going to use mulitframe bitmaps (animations) to kreate keyframes at different times.

The complete script source can be found under
http://www.scriptspot.com/bobo/mxs2/mxs_tut/lesson05.zip

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

Now let's extend our script with some animation capabilities:

CLICK HERE TO SEE THE WHOLE LESSON05 SOURCE CODE
 
 

utility lesson05 "Lesson05"
(

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

group "Settings:"
(
radiobuttons geom_mode "Geometry" labels:#("Box","Sphere","Color-Per-Vertex Mesh") align:#left
dropdownlist effect_mode "Effect Source" items:#("Luminosity","Red Channel","Green Channel","Blue Channel","Alpha Channel")
spinner sphere_segs "Sphere Segments:" range:[4,16,4] fieldwidth:40 align:#right type:#integer enabled:false
spinner base_size "Base Size:" range:[1,1000,100] fieldwidth:40 align:#right
spinner multiplier "Effect 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\\m.avi"
The name of the default file has changed to m.avi which has 10 frames of animation.

label size_label "Image Size:???x???"
label frame_label "Frames:?"
This new label is going to display the number of frames in the selected bitmap.

checkbox use_frames "Create Animation" checked:true enabled:false align:#center
In case there are more than 1 frames in the bitmap, this checkbox will become enabled.
It will let the user turn Animation on and off.

)

group "Action"
(
button start_process "LET'S DO IT!" width:140 height:30 align:#center enabled:false
button remove_all "Clean up scene" width:140 align:#center
)

fn Check_bmp_size bmp_n =
(
 try
 (
 bmp_check = openbitmap bmp_n
 bmp_w = bmp_check.width
 bmp_h = bmp_check.height
 num_frames = bmp_check.numframes
This variable will hold the number of frames as derived from the .numframes property of the bitmap.

 close bmp_check
 size_label.text = "Image Size:" + bmp_w as string + "x" + bmp_h as string

 frame_label.text = "Frames:"+num_frames as string
We put the number of frames into the label.

 if num_frames > 1 then use_frames.enabled = true else use_frames.enabled = false
In case there are more than one frames, the checkbox becomes enabled.

 )
 catch
 (
 start_process.enabled = false
 )
)

on geom_mode changed state do
(
if state == 2 then sphere_segs.enabled = true else sphere_segs.enabled = false
)

on bmp_name entered txt do
(
 file_check = getFiles bmp_name.text
 if file_check.count == 1 then
 (
 start_process.enabled = true
 Check_bmp_size bmp_name.text
 )
 else
 (
 size_label.text = "Image Size:???x???"
 start_process.enabled = false
 )
)

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
 )
)

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

progressstart "Generating Objects..."

mesh_verts = #()
mesh_cpv = #()

nodes_array = #()
This new array will be used to hold all objects we create in Box and Sphere mode.
Later on, when applying animation, we will use this array to access every single object.

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

 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)
  )

  case geom_mode.state of
  (
  1: (
   new_object = box length:base_size.value width:base_size.value height:size_value

   append nodes_array new_object
We put the newly created object into the array.

   )

  2: (
   new_object = sphere radius:(size_value/2) segs:sphere_segs.value

   append nodes_array new_object
Same as above.

   )

  3: (
   append mesh_verts [w*base_size.value, (-h*base_size.value), size_value]
   append mesh_cpv pixel_line[w]
   )
  )

  if geom_mode.state != 3 then
  (
  new_object.pos = [w*base_size.value, (-h*base_size.value), 0]
  new_object.name = uniquename "5th_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

if geom_mode.state == 3 then
 (
 new_object = mesh length:(bmp_h*base_size.value) width:(bmp_w*base_size.value) lengthsegs:(bmp_h/nth_pixel.value-1) widthsegs:(bmp_w/nth_pixel.value-1)
 setnumcpvverts new_object mesh_cpv.count
 for i = 1 to mesh_verts.count do
  (
  setvert new_object i mesh_verts[i]
  setvertcolor new_object i mesh_cpv[i]
  )
 normal_mod = normalModifier flip:true
 addmodifier new_object normal_mod
 collapsestack new_object
 defaultVCfaces new_object
 update new_object
 new_object.name = uniquename "5th_Lesson"
 vert_color = Vertex_Color ()
 new_material = standardmaterial diffusemap:vert_color
 new_material.name = new_object.name
 new_object.material = new_material
 new_object.showvertexcolors = true

 )

progressend ()
We have to end the progress indicator here.
We will have new progress indicators for the animation creation.

if num_frames > 1 and use_frames.checked then
(
We check for animation - if there are more than one frames and the user has decided to use them, we go on...
 

if geom_mode.state == 3 then animatevertex new_object #all
The animatevertex command creates animation tracks for vertices in an editable mesh.
This function is new in MAX 2.5 - in R2, the user had to create animation tracks "by hand".
This makes our script MAX 2.5 specific...

animate on
(
We start an ANIMATION CONTEXT - in the body of this statement, any changes will create keyframes.
It is equal to pressing the Animate button in MAX; but it works internally without the red pressed button displayed...

for t = 1 to num_frames do
(
This loop will go through all frames in the bitmap.

progressstart ("Animating Frame "+t as string + " of "+num_frames as string)
We start a new progress indicator wilsting the current frame and the total frames.

gotoframe work_bmp t
This command repositions inside the bitmap - it changes the current frame.

mesh_verts = #()
mesh_cpv = #()
Same is in the geometry creation code.

node_counter = 0
This counter is initialized here and will point at the current object as we go through the object array.

anim_len = (animationrange.end - animationrange.start)+1
This variable stores the total number of frames in the active animation segment.
The animationrange system variable returns the active segment as INTERVAL [start,end]
You can access the .start and .end properties to get the respective frames.
 

anim_delta = anim_len/num_frames
This is a relation between the length of the bitmap animation and the scene animation.

at time (t*anim_delta)
(
The at time statement repositions us in the scene time - any changes to the scene occur on the respective frame.
It is similar to moving the time slider.
By multiplying the bitmap frame and the relation value we get the correct frame to apply the animation.

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

 for w = 1 to bmp_w by nth_pixel.value do
  (
The above lines are the same as in the geometry creation code.

  node_counter += 1
We increase the counter by one with every single pixel read...
The above reads: "Add 1 to the cirrent value of the variable".

  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 is the same as in the geometry creation code.

  case geom_mode.state of
  (
  1: (
   nodes_array[node_counter].height=size_value
We access the current box from the array and change the height to the new value from the new frame.

   nodes_array[node_counter].material.diffuse=pixel_line[w]
We also access the material of the box and change the diffuse color to the color of the pixel in the new frame.

   )

  2: (
   nodes_array[node_counter].radius=(size_value/2)
   nodes_array[node_counter].material.diffuse=pixel_line[w]
Same as above

   )
  3: (
        new_object[4][node_counter].value = [w*base_size.value, (-h*base_size.value), size_value]
This is an example of an indexed TrackView access - each object is represented in TrackView by a tree of tracks.
You can access them by index, in our case, index [4] is the Mesh Object itself, and the [node_counter] index is the respective vertex animation track.
We assign the new value directly to the track and MAXScript creates a key.

NOTE that there is no way to animate VertexColors in the Mesh since there are no tracks representing the UVW2 texture/color vertices.
You can apply the same animation as Bitmap Texture with slow motion to get a similar effect.

       )
  )--end case

  )--end w loop

 )--end h loop

progressend ()
 

)--end at time
)--end t loop
)--end anim on

)--end num_frames
 

max views redraw
close work_bmp

)--end on button pressed
 

on remove_all pressed do
(
q_answer = queryBox "You are about to delete all objects\ncreated by the Utility.\nAre you sure?"
if q_answer then
 (
 delete_array = $5th_Lesson* as array
 delete delete_array
 )
)
 

on lesson05 open do
(

 file_check = getFiles bmp_name.text
 print file_check.count
 if file_check.count == 1 then
 (
 start_process.enabled = true
 Check_bmp_size bmp_name.text
 )
 else
 (
 messagebox "The Image file couldn't be found!\nPlease select a valid one."
 )

)

)--end utility
 
 

Here is what happens when you execute the script with the animation creation,
Box, Blue Channel, Base 100, Scale 10.0, Nth 2


 

Have fun!

Borislav Petrov [FA]