A MAXScript Perspective – The Power of Automation!


In this post I will go into the power of automation – using MAXScript. Recently I was tasked with automating some image renders as the way we were rendering them wasn’t very efficient. Inefficiency can become very costly as you scale up a project and so I set out to speed things up and make the rendering part (at least) a bit more automated.

Since we had managed to set up the lighting in our “master” file and made sure that each of the meshes were all prepared, all I had todo then was import, apply materials and render!

The Process:

  1. Import the mesh(es).

  2. Apply material(s) > Render.

  3. Pick directory, save the image with the correct name and settings.

  4. Apply new material(s) > Render.

  5. Pick directory, save the image with the correct name and settings.

  6. Repeat steps 2-5 until you have the specified amount of image renders (In the current script it is set to 4 materials in the sample slots 1-4).

  7. Import new mesh(es).

  8. Repeat steps 2-6 for the new mesh(es).

  9. Continue rendering each new mesh with the specified material(s) until you have the required amount of renders.

The Analysis:

Already there is a clear pattern and we can see where errors could occur:

  • Importing the Mesh – Even though we may have prepared the meshes, errors could slip through such as: misaligned pivot points, scale / rotations not reset (Reset XForm Utility in 3ds Max fixes this), “rogue” vertices (vertices which are not connected to anything or causing n-gon (polygons with more than 4 sides) issues), wrong assignment of the material(s) etc.

  • Rendering – Has the lighting and camera(s) been setup correctly? Has the resolution been specified? etc.

  • Saving the image files – Have you picked the right folder directory? Is it the right image type (png or jpeg etc.)? Does it need to have transparency enabled or require an alpha channel? Does the file name follow the specified naming convention?

The Solution:

The script I produced for the project automated the rendering part and the subsequent file naming duties. However, I wasn’t able to implement importing of external files due to time constraints.

The example code can be found on GitHub or here:
(there are many comments in as this was also a learning experience too!)


Example Material Batch Render Script in 3ds Max 2014
For my blog post on using MAXScript for automation in 3ds Max:
A MAXScript Perspective – The Power of Automation!
Developed by Andrew "teessider" Bell Copyright 2015 */ ( -- These are the variables which are being declared here for just the script. local matBatch local currentSel local imagePath global rl_egMatBatch /* Define the rollout as a global variable so that the DestroyDialog can close any rollout with that name. */ -- SETS PNG OPTIONS (Give user options for this in future iteration) pngio.settype(#true24) --setting png option to be 8bits/channel pngio.setAlpha false --Don't render an alpha (for now at least) local png = ".png" -- png filetype as string Try(DestroyDialog rl_egMatBatch)catch() Rollout rl_egMatBatch "TSAB Batch Render" ( button render_btn "Batch Render" on render_btn pressed do ( imagePath = getSavePath() --opens save dialog browser to get file path if imagePath != undefined then ( imagePath += "\\" -- appends these to complete a valid file path ) print imagePath currentSel = selection as array -- In order to "keep" the current selection, I've made a new variable to store it. -- only need to change everytime the button is pressed. --user will be able to define these materials in future iteration --the loop is used in this case for demonstration purposes. There are 4 materials in the example slots 1-4 for i = 1 to 4 do ( matBatch i ) ) ) Createdialog rl_egMatBatch fn matBatch mat1 = ( --define the current selection(s) material currentSel.material = meditMaterials[mat1] --Variables for the strings of the assets for the final filename. -------------------- -- the if statement is for groups and single object. if selection.count == 1 then local objName = currentSel[1].name as string else local objName = currentSel[2].name as string local objMat = meditMaterials[mat1].name as string local uScore = "_" -- Variable instead of writing "_" all the time and its used multiple times! local hyphen = "-" -- This is mainly for the date (eg 2015-03-05) local dateManip -- Setup the variable for my date formatting function. This may end up in a separate script in future. -- Custom function for formatting the date values so that they have a 0 if they are single digits eg 5 = 05 fn dateManip dateVal = -- Function has 1 argument (the index of date array) ( formattedPrint dateVal format: "02u" /* FORMATTEDPRINT HELP formats the value so that there is a leading zero - 0 and becomes two digits - 2 and is an unsigned decimal integer - u > It also converts it to a string! < */ ) -- Setting up the "Dynamic Date" variables -- Careful with this as it relies on Operating System (OS) time/date settings so it could change the index values local gLocalTime = getLocalTime() local year = gLocalTime[1] as string -- Use function to format month and day local month = dateManip gLocalTime[2] local day = dateManip gLocalTime[4]-- 4 equals day of the month local tempSS = stringStream "" -- Create a temp stringStream to store the strings for the filename. -- The % signs are placeholders for the strings to be formatted, arguments come afterwords. -- Add all my date values together to form my date for filename. format "%%%%%" \ year \ hyphen \ month \ hyphen \ day to:tempSS myDate = tempSS as string free tempSS -- clear the stringstream so i can use it again! -- Add all the object attributes (eg. object name and material) to the date for filename. format "%%%%%" \ objName \ uScore \ objMat \ uScore \ myDate to:tempSS objFileName = tempSS as string free tempSS -- Finally add the file path before the filename and end with the image type. format "%%%" \ imagePath \ --file path objFileName \ --object name and material png to:tempSS --image type imageFile = tempSS as string --------------------------------------------------------------------------------------- -- Where imagePath is undefined (cancelled), it catches the error and goes back to main rollout. -- May try and put it a conditional statement in the future. try ( render \ -- Each argument is separated onto a new line with the \ otherwise it would be a very long line! camera: $camera001 \ -- Explicitly select camera001 outputsize: [640, 480] \ --[width, height in pixels] - Fixed (static) at the moment. outputfile: imageFile \ vfb: false \ progressbar: true -- Make sure to change the file output directory to wherever you wish. -- Next iteration will have this as a choice in the GUI ) catch() ) ) )

Leave a Reply