LastRenderRamPlay - Tutorial & Script 

MAXScript Release 0.1.1 Tutorial and Source for 3ds max 5 (05/02/2003)

Code by Borislav Petrov
Online Tutorial Text Last Edited on 05/02/2003, 3:00am CET

 

SHORT DESCRIPTION
LastRenderRamPlay is a rather simple macroScript which will load the output file 
defined in the Renderer dialog in the RAMplayer for review.
 
I WANT TO DOWNLOAD THE READY SCRIPT AND SKIP THE TUTORIAL...
.Click here to get the final script..
 
WHAT DO WE WANT TO ACHIEVE?
  • LastRenderRamPlay will be a macroScript which can be placed on a toolbar, menu, QuadMenu or assigned to a shortcut.
  • It will have to determine the type of the output file - multiframe movie (AVI, MOV, FLC) or a frame sequence of single frames.
  • In the latter case, a new IFL file for playing back the frame sequence will have to be generated
1. DEFINING THE MACROSCRIPT
macroScript LastRenderRAMplay

category:"Bobo_s Tools"

tooltip:"Load Render Output To RAM Player"

(

--The body of the macroScript will be placed here...

)

A macroScript defines a so-called ActionItem in 3ds max lingo.
ActionItems are all the tools you can find in the Customize UI dialog.
They can be dragged to a toolbar, placed in a Menu or QuadMenu or can be assigned to keyboard shortcuts.
ActionItems (and macroScripts) were introduced with 3ds max R3 in 1999.
 
A macroScript starts with the constructor keyword macroScript followed by the internal name of the ActionItem.
This name will appear on the toolbar unless you specify a buttontext parameter to replace it.
 
The category property defines where in the Customize UI dialog the ActionItem will be located.
 
The tooltip property defines the description text to be displayed in the Customize UI dialog and when the mouse rolls over the ActionItem's button.
 
Inside the () brackets, we will enter the actual body of the script.
 
NOTE that in reality, the developer usually writes the body of the script first and then encapsulates it in a macroScript when the code works.
It is easier to test the script by just evaluating the body without the need to press a toolbar button.
2. PREPARATION WORKS
renderSceneDialog.commit()

if rendOutputFilename != "" then

(

rend_path = getFileNamePath rendOutputFilename

rend_name = getFileNameFile rendOutputFilename

rend_type = getFileNameType rendOutputFilename

is_avi = matchPattern rend_type pattern:".avi" ignoreCase:true

is_mov = matchPattern rend_type pattern:".mov" ignoreCase:true

is_flc = matchPattern rend_type pattern:".flc" ignoreCase:true

)

In this code block, we will do some preparations before we can decide what type of output we have to deal with.
We will make sure the output file name is valid and updated.
Then we will collect the different parts of the file name (path, name and extension) and check the file type by the extension.
 
renderSceneDialog.commit()
This line updates the Render Scene Dialog in case it is open.

NOTE that this method has been implemented in VIZ 4 and 3ds max 5 but is not available in 3ds max 4.

Here is a case this would be useful:

  • You have rendered to some output file name before, for example "myanimation.avi". 
  • Then you have set the output to some other name, for example "newrender.avi"
  • Now you go back and select again the old output file name "myanimation.avi" .
  • The Render Scene Dialog is still open and MAXScript does not know that the output file name has changed from "newrender.avi" to "myanimation.avi"
  • The file name points at an existing file "myanimation.avi" so it would be valid for loading to RAMplayer.
  • If you don't call commit(), the utility would try to open the file "newrender.avi" which isn't displayed in the Render Scene Dialog anymore.
  • Calling commit updates the name internally to the actual "myanimation.avi" and MAXScript can see the correct file name.
if rendOutputFilename != "" then  
This line should make sure that the output file name set in the Render Scene Dialog is not empty.
The rest of the utility code will be execured only if the system global variable
rendOutputFilename contains some string.
 
rend_path = getFileNamePath rendOutputFilename

rend_name = getFileNameFile rendOutputFilename

rend_type = getFileNameType rendOutputFilename

These 3 lines split the output file name to path, base name and extension.
The results will be stored in 3 user variables.

These variables are implicitly local, this means that they are valid and seen by the code only inside of this macroScript even without typing "local" in front of them!

 
is_avi = matchPattern rend_type pattern:".avi" ignoreCase:true

is_mov = matchPattern rend_type pattern:".mov" ignoreCase:true

is_flc = matchPattern rend_type pattern:".flc" ignoreCase:true

These 3 lines will check whether the extension of the output file is one of the known multiframe formats - Video For Windows AVI, QuickTime MOV or Autodesk FLC
Using the matchPattern function on the variable containing the file extension, we compare with the specified pattern string while ignoring the case.

matchPattern returns true when the supplied string matches the pattern string, or false when it doesn't.
We are not using the equality comparison operator "==" because we want a non-case sensitive test which matchPattern provides as an option.

 
3. LOADING A MULTIFRAME FILE FORMAT 
if is_avi or is_mov or is_flc then

(

try

(

ramPlayer rendOutputFilename ""

format "Opening RAM Player for Movie [%]\n" rendOutputFilename

)catch()

)

 

In case the output file name points at an AVI, MOV or FLC file, we will try to load this file in the 3ds max RAMplayer.  .
 
Using if and logical or, we tell the utility to only execute the code inside the () brackets when any of the 3 variables is true.
In other words, if the matchPattern has successfully detected an AVI, MOV or an FLC in the code block before, the following code will be executed.
 
MAXScript provides access to the RAMplayer by calling the ramPlayer function with two string parameters - the movie to be loaded in the left and right channel.
Since we want only one movie, we set the second parameter to empty string "" which means "do nothing with this channel".
 
After opening the RAMplayer, we output a single line of text to the MAXScript Listener to tell the user what happened.
The format function expects a formatting string which can contain control characters like \n (New Line), \t (Tabulator) etc.
The control symbol % means an occurrence of a parameter inside the format string.
Each % character will be replaced with the next parameter specified after the formatting string.
There must be as many parameters as % characters.
The parameter will be converted to string automatically, there is no need to call as string for numeric values!

In our case, a  text like "Opening RAM Player for Movie [c:/3dsmax5/images/myanimation.avi]" will be printed to the Listener,
assuming that the output file name is pointing at a file called c:/3dsmax5/images/myanimation.avi

 
The complete code is encapsulated in a try()catch() context which is there to catch any errors that might occur.
This is just a general precaution to avoid unexpected crashes in case the file name is not valid after all.
 
4. LOADING A FRAME SEQUENCE AS A SINGLE ANIMATION FILE

else

(

try

(

check_for_digits = substring rend_name (rend_name.count-3) 4

if classof (execute check_for_digits) == Integer then

rend_name = substring rend_name 1 (rend_name.count-4)

)catch()

image_files = getFiles (rend_path+ rend_name +"*"+rend_type)

if image_files.count > 0 then

(

ram_player_ifl = ((getDir #image)+ "/LastRender.ifl")

temp_ifl = createFile ram_player_ifl

sort image_files

for f in image_files do

format "%\n" f to:temp_ifl

close temp_ifl

ramPlayer ram_player_ifl ""

format "Opening RAM Player for Sequence [%]\n" rendOutputFilename

)

)

This is the code executed when none of the multiframe movie formats has been detected.
In this case, we have to assume we are dealing with a sequence of single frames.
Now we will have to detect all frames from this sequence, generate an IFL listing these files, and load the IFL in the RAMplayer.
 
try

(

check_for_digits = substring rend_name (rend_name.count-3) 4

if classof (execute check_for_digits) == Integer then

rend_name = substring rend_name 1 (rend_name.count-4)

)catch()

There are two possible cases for single frame file names - the base file name is specified in the output, or a single frame name including 4 digits is specified.
In the latter case, we will have to remove the digits to get the actual base name we need to look for.

The code above takes the last 4 characters of the base file name using the substring function, starting with the length of the name minus 3.

Then it tries to convert this substring to a value by calling execute. If the four characters are a number inside a string, like "1234" or "0001", the result will be a number value 1234 resp. 1.

Checking the class using classof of the result tells us whether the characters are a number or not.
If they are, the class returned will be Integer.
In this case, we will have to reduce the base file name in the local variable we defined before to the first n-4 characters.
This will purge the numbers from the name.
Note that is is allowed to take a variable, perform changes to it and assign the result to the same variable.

The whole code is inside a try()catch() context to avoid crashes, for example in the case the length of the base file name is less than 4 characters.
In such a case, an error would occur when trying to get a substring in an invalid range. 

 
image_files = getFiles (rend_path+ rend_name +"*"+rend_type)

if image_files.count > 0 then

(

ram_player_ifl = ((getDir #image)+ "/LastRender.ifl")

temp_ifl = createFile ram_player_ifl

Now we get all files at the path that start with the base name and end with the extension of the render output file.
The getFiles function expects a file name pattern where "*" stands for any number of characters.
The result is a list of all files matching the pattern.

In case the number of returned files is higher than 0, we will generate a new IFL file and load it in the RAMplayer. 

Next we get the Images path defined in the 3ds max path configuration and add the fixed name "LastRender.ifl"
We will use this name to generate a new IFL file listing all single files from the image sequence.
To do this, we call the createFile function with the IFL file name as parameter. 
The temp_ifl variable will contain a so-called FileStream value - a pipeline to a disk file you can write to.

  

sort image_files

for f in image_files do format "%\n" f to:temp_ifl

close temp_ifl

ramPlayer ram_player_ifl ""

format "Opening RAM Player for Sequence [%]\n" rendOutputFilename

)

The file list will be originally in the creation order as returned by the OS. 
To get a correct IFL, we will need to sort the files in ascending order. 
Since they have the same base name, they will be sorted by their four-digit number.
To sort the files, we call the sort function with the file list as parameter.

Then we use a for loop to go through all files in the list and output them to the temp_ifl FileStream value using the format function. 

When we are ready with the output, we have to close the FileStream so it can be accessed.

Now we can open this new IFL file in the RAMplayer just like we did with AVI, MOV and FLC files.

Finally, we will report the result to the user by printing a message to the Listener.

 
COMPLETE SOURCE:
macroScript LastRenderRAMplay

category:"Bobo_s Tools"

tooltip:"Load Render Output To RAM Player"

(

renderSceneDialog.commit()

if rendOutputFilename != "" then

(

rend_path = getFileNamePath rendOutputFilename

rend_name = getFileNameFile rendOutputFilename

rend_type = getFileNameType rendOutputFilename

is_avi = matchPattern rend_type pattern:".avi" ignoreCase:true

is_mov = matchPattern rend_type pattern:".mov" ignoreCase:true

is_flc = matchPattern rend_type pattern:".flc" ignoreCase:true

if is_avi or is_mov or is_flc then

(

try

(

ramPlayer rendOutputFilename ""

format "Opening RAM Player for Movie [%]\n" rendOutputFilename

)catch()

)

else

(

try

(

check_for_digits = substring rend_name (rend_name.count-3) 4

if classof (execute check_for_digits) == Integer then

rend_name = substring rend_name 1 (rend_name.count-4)

)catch()

image_files = getFiles (rend_path+ rend_name +"*"+rend_type)

if image_files.count > 0 then

(

ram_player_ifl = ((getDir #image)+ "/LastRender.ifl")

temp_ifl = createFile ram_player_ifl

sort image_files

for f in image_files do format "%\n" f to:temp_ifl

close temp_ifl

ramPlayer ram_player_ifl ""

format "Opening RAM Player for Sequence [%]\n" rendOutputFilename

)

)

)

)