FrameRenderLog - Tutorial & Script 

MAXScript Release 0.1 Tutorial and Source for 3ds max 4 and higher (05/03/2003)

Code by Borislav Petrov
Online Tutorial Text Last Edited on 05/03/2003, 5:00pm CET

 

SHORT DESCRIPTION
FrameRenderLog is a simple callback script to write frame rendering time information to an external file.
 
I WANT TO DOWNLOAD THE READY SCRIPT AND SKIP THE TUTORIAL...
.Click here to get the final script..
 
WHAT DO WE WANT TO ACHIEVE?
  • FrameRenderLog will consist of two macroScripts which can be placed on a toolbar, menu, QuadMenu or assigned to a shortcut.
  • There will be 3 callbacks - one PreRender callback to initialize the output file, one PreRenderFrame to get the start time, and a PostRenderFrame to output the frame time.
1. INITIALIZING THE LOG FILE
txt1 = "frame_render_log = openFile ((GetDir #Image)+\"/RenderFrames.log\") mode:\"a\" \n "

txt1 +="if frame_render_log == undefined then frame_render_log = createFile ((GetDir #Image)+\"/RenderFrames.log\") \n"

txt1 +="format \"Rendering [%] \n\" rendOutputFileName to:frame_render_log \n"

txt1 +="close frame_render_log \n"

 

callbacks.addScript #PreRender txt1 id:#frame_render_times

First we will define a text variable containing the log file initialization code. 
The variable will be then assigned to a PreRender callback and will be executed every time a new rendering is started.

We define a text variable called txt1 containing the callback code. 
All special symbols like quotation marks (\"), new line (\n) etc. have to be prefixed by backslash.
The frame_render_log variable will contain the FileStream to the external log file to write to.
First we try to open the file RenderFrames.log located in the \Images directory of 3ds max.
We specify the mode as APPEND by setting the mode parameter to "a".
Any output to this file will be appended to the end of the file.
When a file does not exist and you try to open it, a value of undefined will be returned. 
If the file exists, a FileStream value will be returned. You can write to this value using format or print

So now we have to check the value and see if it is undefined or not.
If it is, we have to create a new file with the same name and path.
Note that we add the code to the existing txt1 string variable using +=. It is equal to txt1=txt1 + ...

Then we will output the text "Rendering " and the output file name as a starting line for the following rendering sequence.
The % symbol in the formatting string will be replaced with the output file name.
Using the to: parameter, we tell the format function to output to the FileStream instead of the Listener.

Finally, we add a close function call to close the FileStream.

The resulting string variable txt1 contains the complete code for the first callback.
Now we will add the callback to the callbacks system.
The #PreRender flag tells the callback to intercept callbacks broadcasted by the 3ds max rendering system when a rendering starts.
The id:#frame_render_time is user-defined and a unique identification.
It will let us remove the callback by name whenever we want. 
 

  
2. STOPPING THE FRAME START TIME
txt2 = "global frame_rendering_start_time = timestamp()\n"

callbacks.addScript #PreRenderFrame txt2 id:#frame_render_times

The second callback code will stop the start frame time. 

We define a second text variable called txt2 containing a single line.
In this line, we define a global variable called frame_rendering_start_time and assign a time stamp to it.
The timestamp function returns the operation system time in milliseconds.

Then we add the callback script using the #PreRenderFrame flag. 
This way, the code will be executed once for each frame just before the frame rendering begins.
Note that we use the same id so we can remove it by name together with the other callbacks.

  
3. OUTPUTTING THE FRAME RENDER TIME
txt3 = "frame_render_log = openFile ((GetDir #Image)+\"/RenderFrames.log\") mode:\"a\" \n "

txt3 +="format \"Frame Time: % sec. \n\" ((timestamp()-frame_rendering_start_time)/1000.0) to:frame_render_log \n"

txt3 +="close frame_render_log \n"

callbacks.addScript #PostRenderFrame txt3 id:#frame_render_times

The last callback code will stop the end frame time and output it to the external file. 

We define a text variable called txt3.
Just like in the first callback, we open the external file. 
But this time we know it exists since it was either created by the first callback or has existed before.

Then we use format to output the frame time to the file.
We call the timestamp function a second time and subtract the start time stored in the global variable defined by the second callback script.
The result is in milliseconds, so we divide by 1000.0 to get seconds.

We close the file after outputting the line. 

Then we add the callback script using the #PostRenderFrame flag. 
This way, the code will be executed once for each frame just after the frame rendering is finished.
Note that we use the same id so we can remove it by name together with the other callbacks.

  
4. DISABLING THE CALLBACKS
callbacks.removeScripts id:#frame_render_times
Before defining the callbacks, we should call the removeScripts function using the user-defined id name.
Adding the scripts without removing existing ones could lead to a large number of identical callbacks executed all at the specified event.
This would mean in our case that multiple lines could be outputted for the same frame.

The same line will be used in the disabling MacroScript (see below). 

  
5. DEFINING THE MACROSCRIPTS
macroScript enableFrameLog category:"Bobo_s Tools"

(

)

 

macroScript disableFrameLog category:"Bobo_s Tools"

(

)

We will need two MacroScripts - the one called enableFrameLog will define the code, remove existing and add the new callback scripts.
The second will only remove any callback scripts by id. If there are none, nothing will happen.

See below for the complete source code. 

  
COMPLETE SOURCE:
macroScript enableFrameLog category:"Bobo_s Tools"

(

txt1 = "frame_render_log = openFile ((GetDir #Image)+\"/RenderFrames.log\") mode:\"a\" \n "

txt1 +="if frame_render_log == undefined then frame_render_log = createFile ((GetDir #Image)+\"/RenderFrames.log\") \n"

txt1 +="format \"Rendering [%] \n\" rendOutputFileName to:frame_render_log \n"

txt1 +="close frame_render_log \n"

 

txt2 = "global frame_rendering_start_time = timestamp()\n"

 

txt3 = "frame_render_log = openFile ((GetDir #Image)+\"/RenderFrames.log\") mode:\"a\" \n "

txt3 +="format \"Frame Time: % sec. \n\" ((timestamp()-frame_rendering_start_time)/1000.0) to:frame_render_log \n"

txt3 +="close frame_render_log \n"

 

callbacks.removeScripts id:#frame_render_times

callbacks.addScript #PreRender txt1 id:#frame_render_times

callbacks.addScript #PreRenderFrame txt2 id:#frame_render_times

callbacks.addScript #PostRenderFrame txt3 id:#frame_render_times

)

macroScript disableFrameLog category:"Bobo_s Tools"

(

callbacks.removeScripts id:#frame_render_times

)