[Closed] CgTalk Maxscript Challenge 006: "Object Photo Album"
CgTalk Maxscript Challenge 006: “Object Photo Album”
DESCRIPTION: Create a tool that focuses on each object in a scene and renders each object as a seperate image. For example, if there are three spheres in a scene you would end up with a folder containing three images, one of each object. You can either render the object or viewport snap it.
INTERMEDIATE SCRIPTERS: Embed the images in a HTML document and add information about each object (poly count, textures etc.)
ADVANCED SCRIPTERS: Compile the whole thing (scene, images and html document) into it's own folder or zip file, ready for "release". Consider this like a tool to keep track of finished max files in a development environment.
RULES:
[ul]
[li]Code from scratch. Try not to use pre-exisitng functions or plugins.[/li][li]Show your script references, if any (eg. Looking at another script to assist you).[/li][li]You are encouraged to ask for help where needed, but try to do it on your own. The maxscript reference is an invaluable resource.[/li][li]Post your final script inside [/li]“`
tags (located on your posting toolbar).
[li]Post all code into THIS thread.[/li][li]Post the max version you coded in, plus any maxscript extensions you used. (Thanks galagast!)[/li][li]Try to finish the challenge in a week. There is no definite time limit.[/li][li]Voting will occur at the end of each ‘month’ (every 4 challenges).[/li][/ul]NOTES: None this week… I feel it’s pretty straightforward. If you don’t know HTML, it’s not too hard to learn, there’s numerous sites on how to write simple code for this. Remember to vote for the first 4 challenges: http://www.cgtalk.com/showthread.php?t=257580
this one appeared to be interesting to me, so here’s my take on it:
try (destroyDialog ro_ContactSheet) catch()
rollout ro_ContactSheet "Object Contact Sheet"
(
-- Local Variable declerations
--------------------------------------------------------------
local indent = ""
local HTMLFilename = ""
-- User Interface
--------------------------------------------------------------
group "Output Folder: " (
radioButtons rbOutputFolder "" labels:#("Use the current max file folder.", "Use a custom folder:") align:#left columns:1 default:(if maxFilePath != "" then 1 else 2)
edittext edOutputFolder "" fieldWidth:270 align:#left across:2 offset:[-8,0]
button bnOutputFolderBrowse "..." width:18 height:16 align:#right offset:[3,0]
)
group "Render Options: " (
spinner spOutputWidth "Output Size: Width:" fieldWidth:45 type:#integer range:[1, 9e5, renderWidth] align:#left across:2
spinner spOutputHeight "Height:" fieldWidth:45 type:#integer range:[1, 9e5, renderHeight] align:#left offset:[20,0]
label lblImageType "Image Type: " align:#left across:2 offset:[0,3]
dropDownList ddlImageType "" width:50 items:#("jpg", "png") default:1 align:#left offset:[-80,0]
colorPicker cpBGColor "Background Color: " align:#right offset:[0,-26] color:backgroundColor
spinner spCameraFOV "Camera FOV: " fieldWidth:40 range:[1.0, 179., 30.] type:#float align:#left offset:[0,3]
radioButtons rbLights "Lights:" labels:#("Use Scene Lights", "Use Internal Light Rig") default:2 align:#left columns:1
)
group "Zip Files: " (
checkbox cbEnableZip "Zip files into file:" align:#left
edittext edZipFile "" fieldWidth:250 align:#left across:2 offset:[12,0] enabled:cbEnableZip.checked
button bnZipFileBrowse "..." width:18 height:16 align:#right offset:[3,0] enabled:cbEnableZip.checked
checkbox cbZipDeleteFiles "Delete contact sheet files after zipping." align:#left enabled:cbEnableZip.checked
button bnZipOpenFolder "Open Zip File Folder" width:290 height:20 enabled:(doesFileExist edZipFile.text)
)
group "Progress: " (
label lblProgTotal "Processed/Total number of objects: " align:#left
label lblProgExported "Actual number of exported objects: " align:#left
progressBar pbProgress ""
)
button bnGo "Create Contact Sheet" width:300 height:25 enabled:(rbOutputFolder.state == 1)
button bnView "View Contact Sheet" width:145 height:25 enabled:false align:#left across:2 offset:[-8,0]
button bnOpenFolder "Open HTML Folder" width:145 height:25 enabled:false align:#right offset:[8,0]
-- General Functions
--------------------------------------------------------------
fn getVisibleObjects =
(
for o in objects where not o.isHidden collect o
)
fn getEnabledLights =
(
for l in lights where (not isKindOf l TargetObject and l.baseObject.on) collect l
)
fn getNumVerts obj =
(
obj.mesh.verts.count
)
fn getNumFaces obj =
(
try (
obj.faces.count
) catch (
obj.mesh.faces.count
)
)
fn getObjectTextueFiles obj =
(
local tex = #()
if obj != undefined then (
for i = 1 to obj.numSubs do (
if isKindOf obj[i] Bitmaptexture then
append tex obj[i].filename
join tex (getObjectTextueFiles obj[i])
)
)
tex
)
-- HTML Writing Functions
--------------------------------------------------------------
fn incIndent &i = i += " "
fn decIndent &i =
(
if i.count > 0 then
i = subString i 1 (i.count - 1)
else
""
)
fn writeObjTexturesHTML HTMLFileHandle obj photoName &indent =
(
local tex = getObjectTextueFiles obj.material
if tex.count > 0 then (
incIndent &indent
format "%<b>Texture Files:</b><br />
" indent to:HTMLFileHandle
format "%<ul>
" indent to:HTMLFileHandle
incIndent &indent
for t in tex do
format "%<li>%</li>
" indent t to:HTMLFileHandle
decIndent &indent
format "%</ul>
" indent to:HTMLFileHandle
decIndent &indent
)
)
fn writeObjHTMLCode HTMLFileHandle obj photoName &indent =
(
try (
format "%<tr>
" indent to:HTMLFileHandle
incIndent &indent
format "%<td>
" indent to:HTMLFileHandle
incIndent &indent
format "%<img src=\"%\" style=\"border: 1px solid black; display: inline;\" alt=\"%\" />
" indent photoName obj.name to:HTMLFileHandle
decIndent &indent
format "%</td>
" indent to:HTMLFileHandle
format "%<td valign=\"top\" align=\"left\" width=\"100\%\">
" indent to:HTMLFileHandle
incIndent &indent
format "%<b>Object Name:</b>%<br />
" indent obj.name to:HTMLFileHandle
format "%<b>Vertices:</b>%<br />
" indent (getNumVerts obj) to:HTMLFileHandle
format "%<b>Faces:</b>%<br />
" indent (getNumFaces obj) to:HTMLFileHandle
writeObjTexturesHTML HTMLFileHandle obj photoName &indent
decIndent &indent
format "%</td>
" indent to:HTMLFileHandle
decIndent &indent
format "%</tr>
" indent to:HTMLFileHandle
-- create a divider
format "%<tr>
" indent to:HTMLFileHandle
incIndent &indent
format "%<td colspan=\"2\">
" indent to:HTMLFileHandle
incIndent &indent
format "%<hr />
" indent photoName obj.name to:HTMLFileHandle
decIndent &indent
format "%</td>
" indent to:HTMLFileHandle
decIndent &indent
format "%</tr>
" indent to:HTMLFileHandle
)catch()
)
fn writeHTMLHeaderCode HTMLFileHandle &indent =
(
try (
format "%<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
" indent to:HTMLFileHandle
format "%<html>
" indent to:HTMLFileHandle
format "%<head>
" indent to:HTMLFileHandle
incIndent &indent
format "%<title>contactSheet - 3DS Max Object Contact Sheet</title>
" indent to:HTMLFileHandle
-- write the styles
format "%<style type=\"text/stylesheet\">
" indent to:HTMLFileHandle
incIndent &indent
format "%hr {
" indent to:HTMLFileHandle
incIndent &indent
format "%height: 1px;
" indent to:HTMLFileHandle
format "%color: Gray;
" indent to:HTMLFileHandle
format "%border-style: solid;
" indent to:HTMLFileHandle
decIndent &indent
format "%}
" indent to:HTMLFileHandle
decIndent &indent
format "%</style>
" indent to:HTMLFileHandle
decIndent &indent
format "%</head>
" indent to:HTMLFileHandle
format "%<body>
" indent to:HTMLFileHandle
incIndent &indent
format "%<table width=\"95\%\" align=\"center\" style=\"border: 2px solid black;\">
" indent to:HTMLFileHandle
incIndent &indent
)catch()
)
fn writeHTMLFooterCode HTMLFileHandle &indent =
(
try (
decIndent &indent
format "%</table>
" indent to:HTMLFileHandle
decIndent &indent
format "%</body>
" indent to:HTMLFileHandle
decIndent &indent
format "%</html>
" indent to:HTMLFileHandle
)catch()
)
-- Render Functions
--------------------------------------------------------------
-- taken form the maxscript help, and slightly modified.
fn GetVFOV fov renderSize:[renderWidth, renderHeight] =
(
local r_aspect=(renderSize.x as float)/renderSize.y
2.0*atan(tan(fov/2.0)/r_aspect)
)
fn genContactSheetCam obj fov renderSize =
(
-- create the temp camera
local cam = freeCamera name:"ContactSheetCam" fov:fov
-- move and rotate the camera to match the object's transformation
cam.transform = obj.transform
cam.pos = obj.center
in coordsys cam
rotate cam (eulerAngles 90 0 0)
-- calculate the distance needed to fit the entire object
local bBoxSize = (obj.max - obj.min) * 1.1
local distWidth = (bBoxSize.x / 2.) / tan (fov / 2.)
local distHeight = (bBoxSize.y / 2.) / tan ((getVFOV fov renderSize:renderSize) / 2.)
local dist = amax (abs distWidth) (abs distHeight)
-- move the camera back so it sees the whole object.
in coordsys cam
cam.pos.z += dist
-- return the camera object
cam
)
fn genObjRender obj renderSize fov useSceneLights:false =
(
-- create the temp camera object.
local camObj = genContactSheetCam obj fov renderSize
-- if we're not going to use the scene lights, create the temp light rig
if not useSceneLights then (
local keyLight = omniLight name:"ContactSheetTempLight" color:white multiplier:1.0
local fillLight = omniLight name:"ContactSheetTempLight" color:white multiplier:0.5
in coordsys camObj (
local dist = distance camObj.pos obj.center
keyLight.pos = [dist, dist, 0]
fillLight.pos = [-dist, -dist, 0]
)
)
-- render the object
local rend = render camera:camObj outputSize:renderSize renderhiddenobjects:false vfb:false
-- delete the camera
delete camObj
-- delete the temp lights
if isValidNode keyLight then
delete keyLight
if isValidNode fillLight then
delete fillLight
-- return the rendered bitmap
rend
)
-- contactSheet Functions
--------------------------------------------------------------
-- this function creates a contact sheet entry for a single object.
-- it first renders the object, then generates the HTML code.
fn contactSheetObj obj outputDir HTMLFileHandle renderSize fov useSceneLights:false imgType:"jpg" mode:#render =
(
-- check if the object is a geometry object, if not quit the fn.
if not (isValidNode obj) or not (isKindOf obj GeometryClass) or (isKindOf obj targetObject) then
return false
-- store the hidden state of the object to restore it later.
local objIsHidded = obj.isHidden
-- make sure the object is not hidden
obj.isHidden = false
local rend = bitmap 30 30 color:black
case mode of (
#render: rend = genObjRender obj renderSize fov useSceneLights:useSceneLights
) -- end case
-- save the bitmap
local photoName = obj.name + "." + imgType
rend.filename = outputDir + photoName
save rend
writeObjHTMLCode HTMLFileHandle obj photoName &indent
-- restore the object hidden state
obj.isHidden = objIsHidded
-- return true
true
)
-- This function loops through all objects and calls the single object
-- contact sheet to do the actual job.
-- This function also updates the UI, and gets the parameters set in
-- the UI and passes them on.
fn contactSheet =
(
-- declare output name and directory variables.
local outputName = if maxFileName == "" then "Untitled" else (getFilenameFile maxFileName)
local outputDir = if rbOutputFolder.state == 2 then edOutputFolder.text else maxFilePath
if outputDir[outputDir.count] != "\\" and outputDir[outputDir.count] != "/" then
outputDir += "\\"
outputDir += outputName + "\\"
makedir outputDir
local HTMLFileName = outputDir + outputName + ".html"
-- create the HTML file
local HTMLFileHandle = createFile HTMLFileName
-- if the HTML file was not created, abort.
if HTMLFileHandle == undefined then return()
-- Write the HTML headers
writeHTMLHeaderCode HTMLFileHandle &indent
disableSceneRedraw()
-- store the scene state for restoring later.
local renderSize = [spOutputWidth.value, spOutputHeight.value]
local visObjs = getVisibleObjects()
local enabledLights = getEnabledLights()
local BGCol = backgroundColor
backgroundColor = cpBGColor.color
-- turn off scene lights and hide all objects.
if rbLights.state != 1 then (
for l in enabledLights do
l.baseObject.on = off
)
hide objects
-- create the contact sheet elements for each object.
local counter = 0 -- actual number of objects that were processed
local i = 0. -- a counter for the progress bar
local imgType = ddlImageType.selected
local useSceneLights = (rbLights.state == 1)
local fov = spCameraFOV.value
for o in objects do (
try (
if (contactSheetObj o outputDir HTMLFileHandle renderSize fov useSceneLights:useSceneLights imgType:imgType mode:#render) then
counter += 1
) catch(print "error")
i += 1.
lblProgTotal.text = "Processed/Total number of objects: " + (i as integer) as string + "/" + (objects.count) as string
pbProgress.value = i / (objects.count as float) * 100.
)
lblProgExported.text = "Actual number of exported objects: " + counter as string
-- write the HTML footers
writeHTMLFooterCode HTMLFileHandle &indent
-- close the HTML file
close HTMLFileHandle
-- restore scene to original state.
for l in enabledLights do
l.baseObject.on = on
unHide visObjs
backgroundColor = BGCol
enableSceneRedraw()
-- return the HTML filename
HTMLFileName
)
-- Zip Functions
-------------------------------------------------------------------
-- archives all the files in dir into zipFilename using maxzip.exe
fn zipFiles dir zipFilename =
(
if zipFilename == "" then
return false
if dir[dir.count] != "\\" and dir[dir.count] != "/" then
dir += "\\"
local files = getFiles (dir + "*")
if not doesFileExist dir or files.count == 0 then
return false
-- generate a file with the list of files to be archived
local listFilename = sysInfo.tempDir + "~maxzipTempList" + timeStamp() as string + ".lst"
local f = createFile listFilename
if f == undefined then
return false
for i in files do
format "%
" i to:f
flush f
close f
-- archive the files into the zip
local curDir = sysInfo.currentDir
local zipUtil = getDir #maxRoot + "maxzip.exe"
local zipFile = getFileNameFile zipFilename + getFileNameType zipFilename
local cmd = "" as stringStream
sysInfo.currentDir = getFilenamePath zipFilename
format "\"%\" % @%" zipUtil zipFile listFilename to:cmd
local archive = dosCommand cmd
sysInfo.currentDir = curDir
-- delete the temporary list file
deleteFile listFilename
-- return true on success false on failure
archive == 0
)
-- deletes all files in a give folder
fn deleteFiles dir =
(
if dir[dir.count] != "\\" and dir[dir.count] != "/" then
dir += "\\"
for f in getFiles (dir + "*") do
deleteFile f
)
-- UI Functions
-------------------------
-- This function is responsible for all the UI elements' update such as enabling/disabling etc.
fn updateUI =
(
local f = if rbOutputFolder.state == 1 then maxFilePath else edOutputFolder.text
if f != "" and f[f.count] != "\\" and f[f.count] != "/" then
f += "\\"
bnGo.enabled = doesFileExist f
edOutputFolder.enabled = rbOutputFolder.state == 2
bnOutputFolderBrowse.enabled = rbOutputFolder.state == 2
edZipFile.enabled = cbEnableZip.checked
bnZipFileBrowse.enabled = cbEnableZip.checked
cbZipDeleteFiles.enabled = cbEnableZip.checked
bnZipOpenFolder.enabled = doesFileExist (getFilenamePath edZipFile.text)
bnView.enabled = doesFileExist HTMLFilename
bnOpenFolder.enabled = doesFileExist (getFilenamePath HTMLFilename)
)
-- Event Handlers
-----------------------------------------------------------------------
on rbOutputFolder changed state do (
if state == 1 then (
if maxFilePath == "" then (
messageBox "It appears the current max file has never been save.
Please save it before using this option."
rbOutputFolder.state = 2
state = 2
)
)
updateUI()
)
on edOutputFolder changed txt do (
updateUI()
)
on bnOutputFolderBrowse pressed do (
local f = getSavePath()
if f != undefined then (
edOutputFolder.text = f
updateUI()
)
)
on cbEnableZip changed state do (
updateUI()
)
on edZipFile changed txt do (
updateUI()
)
on bnZipFileBrowse pressed do (
local f = getSaveFilename filename:(maxFilePath + getFilenameFile maxFileName + ".zip") types:"ZIP Files (*.zip)|*.zip|All Files (*.*)|*.*"
if f != undefined then (
edZipFile.text = f
updateUI()
)
)
on bnZipOpenFolder pressed do (
if doesFileExist (getFilenamePath edZipFile.text) then
shellLaunch (getFilenamePath edZipFile.text) ""
)
on bnGo pressed do (
HTMLFilename = contactSheet()
local dir = getFilenamePath HTMLFilename
if cbEnableZip.checked then (
local zipped = zipFiles dir edZipFile.text
if zipped then (
if cbZipDeleteFiles.checked and (queryBox "Delete all files in the contact sheet folder?
This cannot be undone!") then
deleteFiles dir
) else (
messageBox "The zipping of the files failed."
)
)
updateUI()
)
on bnView pressed do (
shellLaunch HTMLFilename ""
)
on bnOpenFolder pressed do (
shellLaunch (getFilenamePath HTMLFilename) ""
)
on ro_ContactSheet open do (
updateUI()
)
)-- end of ro_ContactSheet
createDialog ro_ContactSheet width:310
tested only on max 7
cheers,
o
Wow, great work ofer_z! I like the handling of render styles and the easy access to the html sheet afterwards. Very well done!
Hey ofer I thought you wanted to leave the 3d arena, I liked it!!
very well done.
here’s an improved version, it allows to select the camera angle to render from.
--*************************************************************************************
--* Object Contact Sheet
--* By Ofer Zelichover
--* www.oferz.com
--*
--* Created for a CGTalk MaxScript Challenge. 21/7/2005
--*************************************************************************************
try (destroyDialog ro_ContactSheet) catch()
rollout ro_ContactSheet "Object Contact Sheet"
(
-- Local Variable declerations
--------------------------------------------------------------
local indent = ""
local HTMLFilename = ""
-- User Interface
--------------------------------------------------------------
group "Output Folder: " (
radioButtons rbOutputFolder "" labels:#("Use the current max file folder.", "Use a custom folder:") align:#left columns:1 default:(if maxFilePath != "" then 1 else 2)
edittext edOutputFolder "" fieldWidth:270 align:#left across:2 offset:[-8,0]
button bnOutputFolderBrowse "..." width:18 height:16 align:#right offset:[3,0]
)
group "Render Options: " (
spinner spOutputWidth "Output Size: Width:" fieldWidth:45 type:#integer range:[1, 9e5, renderWidth] align:#left across:2
spinner spOutputHeight "Height:" fieldWidth:45 type:#integer range:[1, 9e5, renderHeight] align:#left offset:[20,0]
label lblImageType "Image Type: " align:#left across:2 offset:[0,3]
dropDownList ddlImageType "" width:50 items:#("jpg", "png") default:1 align:#left offset:[-80,0]
colorPicker cpBGColor "Background Color: " height:18 fieldWidth:25 align:#right offset:[0,-26] color:backgroundColor
spinner spCameraFOV "Camera FOV: " fieldWidth:40 range:[1.0, 179., 30.] type:#float align:#left offset:[0,3]
radioButtons rbCameraAngle "Camera Angle:" labels:#("Front", "Top", "Side", "Perspective", "All") default:1 align:#left columns:3 offset:[0,3]
radioButtons rbLights "Lights:" labels:#("Use Scene Lights", "Use Internal Light Rig") default:2 align:#left columns:1 offset:[0,3]
)
group "Zip Files: " (
checkbox cbEnableZip "Zip files into file:" align:#left
edittext edZipFile "" fieldWidth:250 align:#left across:2 offset:[12,0] enabled:cbEnableZip.checked
button bnZipFileBrowse "..." width:18 height:16 align:#right offset:[3,0] enabled:cbEnableZip.checked
checkbox cbZipDeleteFiles "Delete contact sheet files after zipping." align:#left enabled:cbEnableZip.checked
button bnZipOpenFolder "Open Zip File Folder" width:290 height:20 enabled:(doesFileExist edZipFile.text)
)
group "Progress: " (
label lblProgTotal "Processed/Total number of objects: " align:#left
label lblProgExported "Actual number of exported objects: " align:#left
progressBar pbProgress ""
)
button bnGo "Create Contact Sheet" width:300 height:25 enabled:(rbOutputFolder.state == 1)
button bnView "View Contact Sheet" width:145 height:25 enabled:false align:#left across:2 offset:[-8,0]
button bnOpenFolder "Open HTML Folder" width:145 height:25 enabled:false align:#right offset:[8,0]
-- General Functions
--------------------------------------------------------------
fn getVisibleObjects =
(
for o in objects where not o.isHidden collect o
)
fn getEnabledLights =
(
for l in lights where (not isKindOf l TargetObject and l.baseObject.on) collect l
)
fn getNumVerts obj =
(
obj.mesh.verts.count
)
fn getNumFaces obj =
(
try (
obj.faces.count
) catch (
obj.mesh.faces.count
)
)
fn getObjectTextueFiles obj =
(
local tex = #()
if obj != undefined then (
for i = 1 to obj.numSubs do (
if isKindOf obj[i] Bitmaptexture then
append tex obj[i].filename
join tex (getObjectTextueFiles obj[i])
)
)
tex
)
-- HTML Writing Functions
--------------------------------------------------------------
fn incIndent &i = i += " "
fn decIndent &i =
(
if i.count > 0 then
i = subString i 1 (i.count - 1)
else
""
)
fn writeObjTexturesHTML HTMLFileHandle obj photoName &indent =
(
local tex = getObjectTextueFiles obj.material
if tex.count > 0 then (
incIndent &indent
format "%<b>Texture Files:</b><br />
" indent to:HTMLFileHandle
format "%<ul>
" indent to:HTMLFileHandle
incIndent &indent
for t in tex do
format "%<li>%</li>
" indent t to:HTMLFileHandle
decIndent &indent
format "%</ul>
" indent to:HTMLFileHandle
decIndent &indent
)
)
fn writeObjHTMLCode HTMLFileHandle obj photoName &indent =
(
try (
format "%<tr>
" indent to:HTMLFileHandle
incIndent &indent
format "%<td>
" indent to:HTMLFileHandle
incIndent &indent
format "%<img src=\"%\" style=\"border: 1px solid black; display: inline;\" alt=\"%\" />
" indent photoName obj.name to:HTMLFileHandle
decIndent &indent
format "%</td>
" indent to:HTMLFileHandle
format "%<td valign=\"top\" align=\"left\" width=\"100\%\">
" indent to:HTMLFileHandle
incIndent &indent
format "%<b>Object Name:</b>%<br />
" indent obj.name to:HTMLFileHandle
format "%<b>Vertices:</b>%<br />
" indent (getNumVerts obj) to:HTMLFileHandle
format "%<b>Faces:</b>%<br />
" indent (getNumFaces obj) to:HTMLFileHandle
writeObjTexturesHTML HTMLFileHandle obj photoName &indent
decIndent &indent
format "%</td>
" indent to:HTMLFileHandle
decIndent &indent
format "%</tr>
" indent to:HTMLFileHandle
-- create a divider
format "%<tr>
" indent to:HTMLFileHandle
incIndent &indent
format "%<td colspan=\"2\">
" indent to:HTMLFileHandle
incIndent &indent
format "%<hr />
" indent photoName obj.name to:HTMLFileHandle
decIndent &indent
format "%</td>
" indent to:HTMLFileHandle
decIndent &indent
format "%</tr>
" indent to:HTMLFileHandle
)catch()
)
fn writeHTMLHeaderCode HTMLFileHandle &indent =
(
try (
format "%<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
" indent to:HTMLFileHandle
format "%<html>
" indent to:HTMLFileHandle
format "%<head>
" indent to:HTMLFileHandle
incIndent &indent
format "%<title>contactSheet - 3DS Max Object Contact Sheet</title>
" indent to:HTMLFileHandle
-- write the styles
format "%<style type=\"text/stylesheet\">
" indent to:HTMLFileHandle
incIndent &indent
format "%hr {
" indent to:HTMLFileHandle
incIndent &indent
format "%height: 1px;
" indent to:HTMLFileHandle
format "%color: Gray;
" indent to:HTMLFileHandle
format "%border-style: solid;
" indent to:HTMLFileHandle
decIndent &indent
format "%}
" indent to:HTMLFileHandle
decIndent &indent
format "%</style>
" indent to:HTMLFileHandle
decIndent &indent
format "%</head>
" indent to:HTMLFileHandle
format "%<body>
" indent to:HTMLFileHandle
incIndent &indent
format "%<table width=\"95\%\" align=\"center\" style=\"border: 2px solid black;\">
" indent to:HTMLFileHandle
incIndent &indent
)catch()
)
fn writeHTMLFooterCode HTMLFileHandle &indent =
(
try (
decIndent &indent
format "%</table>
" indent to:HTMLFileHandle
decIndent &indent
format "%</body>
" indent to:HTMLFileHandle
decIndent &indent
format "%</html>
" indent to:HTMLFileHandle
)catch()
)
-- Render Functions
--------------------------------------------------------------
-- taken form the maxscript help, and slightly modified.
fn GetVFOV fov renderSize:[renderWidth, renderHeight] =
(
local r_aspect=(renderSize.x as float)/renderSize.y
2.0*atan(tan(fov/2.0)/r_aspect)
)
fn genContactSheetCam obj fov renderSize view:#front =
(
-- create the temp camera
local cam = freeCamera name:"ContactSheetCam" fov:fov
-- move and rotate the camera to match the object's transformation
cam.transform = obj.transform
cam.pos = obj.center
local angs = #()
case view of (
#front: angs = #(eulerAngles 90 0 0)
#side: angs = #(eulerAngles 0 90 0, eulerAngles 0 0 90)
#top: angs = #(eulerAngles 0 0 0)
#persp: angs = #(eulerAngles 45 0 45)
default: angs = (eulerAngles 90 0 0)
)
in coordsys cam (
for a in angs do
rotate cam a
)
-- calculate the distance needed to fit the entire object
local bBoxSize = (obj.max - obj.min) * 1.1
local distWidth = (bBoxSize.x / 2.) / tan (fov / 2.)
local distHeight = (bBoxSize.y / 2.) / tan ((getVFOV fov renderSize:renderSize) / 2.)
local dist = amax (abs distWidth) (abs distHeight)
-- move the camera back so it sees the whole object.
in coordsys cam
cam.pos.z += dist
-- return the camera object
cam
)
fn genObjRender obj renderSize fov useSceneLights:false view:#front =
(
-- create the temp camera object.
local camObj = genContactSheetCam obj fov renderSize view:view
-- if we're not going to use the scene lights, create the temp light rig
if not useSceneLights then (
local keyLight = omniLight name:"ContactSheetTempLight" color:white multiplier:1.0
local fillLight = omniLight name:"ContactSheetTempLight" color:white multiplier:0.5
in coordsys camObj (
local dist = distance camObj.pos obj.center
keyLight.pos = [dist, dist, 0]
fillLight.pos = [-dist, -dist, 0]
)
)
-- render the object
local rend = render camera:camObj outputSize:renderSize renderhiddenobjects:false vfb:false
-- delete the camera
delete camObj
-- delete the temp lights
if isValidNode keyLight then
delete keyLight
if isValidNode fillLight then
delete fillLight
-- return the rendered bitmap
rend
)
-- combine 4 images into one image.
fn combineImages images rendSize =
(
-- create a compositeTexture map that will do the compositing
local comp = CompositeTexturemap name:"tempComp"
local filenames = #()
for i = 1 to images.count do (
-- to be able to use a bitmap value as a bitmaptexture the bitmap must first be saved.
-- so, save the bitmap to a temporary file.
images[i].filename = sysInfo.tempDir + "~tempCompBitmap" + i as string + "_" + timeStamp() as string + ".png"
save images[i]
-- append the temp bitmap filename to a list, so it can be deleted later.
append filenames images[i].filename
-- create a bitmapTexture map for the image
local b = Bitmaptexture name:("tempBitmap" + i as string) bitmap:images[i] filtering:1
-- and set some parameters. the compositing is done by setting the UV tiling values to 2
-- so the image is going to be half the size of the final image.
b.coords.U_Mirror = false
b.coords.V_Mirror = false
b.coords.U_Tile = false
b.coords.V_Tile = false
b.coords.U_Tiling = 2.
b.coords.V_Tiling = 2.
b.coords.U_Offset = 0.25 * (if (mod i 2) == 0 then 1 else -1)
b.coords.V_Offset = 0.25 * (if i > 2 then -1 else 1)
-- add the bitmapTexture to the composite map list
comp.mapList[i] = b
)
-- delete the temp files
for f in filenames do
deleteFile f
-- render the composite texture and return the result
rend = renderMap comp size:rendSize display:false
)
fn genMultiRender obj renderSize fov useSceneLights:false =
(
local rendSize = renderSize / 2
local images = #()
-- generate image for each of the 4 views
append images (genObjRender obj rendSize fov useSceneLights:useSceneLights view:#top)
append images (genObjRender obj rendSize fov useSceneLights:useSceneLights view:#front)
append images (genObjRender obj rendSize fov useSceneLights:useSceneLights view:#side)
append images (genObjRender obj rendSize fov useSceneLights:useSceneLights view:#persp)
-- return the combined image
combineImages images renderSize
)
-- contactSheet Functions
--------------------------------------------------------------
-- this function creates a contact sheet entry for a single object.
-- it first renders the object, then generates the HTML code.
fn contactSheetObj obj outputDir HTMLFileHandle renderSize fov useSceneLights:false imgType:"jpg" mode:#render view:#front=
(
-- check if the object is a geometry object, if not quit the fn.
if not (isValidNode obj) or not (isKindOf obj GeometryClass) or (isKindOf obj targetObject) then
return false
-- store the hidden state of the object to restore it later.
local objIsHidded = obj.isHidden
-- make sure the object is not hidden
obj.isHidden = false
local rend = bitmap 30 30 color:black
case mode of (
#render: rend = genObjRender obj renderSize fov useSceneLights:useSceneLights view:view
#multiRender: rend = genMultiRender obj renderSize fov useSceneLights:useSceneLights
) -- end case
-- save the bitmap
local photoName = obj.name + "." + imgType
rend.filename = outputDir + photoName
save rend
writeObjHTMLCode HTMLFileHandle obj photoName &indent
-- restore the object hidden state
obj.isHidden = objIsHidded
-- return true
true
)
-- This function loops through all objects and calls the single object
-- contact sheet to do the actual job.
-- This function also updates the UI, and gets the parameters set in
-- the UI and passes them on.
fn contactSheet =
(
-- declare output name and directory variables.
local outputName = if maxFileName == "" then "Untitled" else (getFilenameFile maxFileName)
local outputDir = if rbOutputFolder.state == 2 then edOutputFolder.text else maxFilePath
if outputDir[outputDir.count] != "\\" and outputDir[outputDir.count] != "/" then
outputDir += "\\"
outputDir += outputName + "\\"
makedir outputDir
local HTMLFileName = outputDir + outputName + ".html"
-- create the HTML file
local HTMLFileHandle = createFile HTMLFileName
-- if the HTML file was not created, abort.
if HTMLFileHandle == undefined then return()
-- Write the HTML headers
writeHTMLHeaderCode HTMLFileHandle &indent
disableSceneRedraw()
-- store the scene state for restoring later.
local renderSize = [spOutputWidth.value, spOutputHeight.value]
local visObjs = getVisibleObjects()
local enabledLights = getEnabledLights()
local BGCol = backgroundColor
backgroundColor = cpBGColor.color
-- turn off scene lights and hide all objects.
if rbLights.state != 1 then (
for l in enabledLights do
l.baseObject.on = off
)
hide objects
-- create the contact sheet elements for each object.
local counter = 0 -- actual number of objects that were processed
local i = 0. -- a counter for the progress bar
local imgType = ddlImageType.selected
local useSceneLights = (rbLights.state == 1)
local view = case rbCameraAngle.state of (1:#front; 2:#top; 3:#side; 4:#persp; 5:#all)
local mode = if rbCameraAngle.state == 5 then #multiRender else #render
local fov = spCameraFOV.value
lblProgTotal.text = "Processed/Total number of objects: 0" + "/" + (objects.count) as string
pbProgress.value = 0.
for o in objects do (
try (
if (contactSheetObj o outputDir HTMLFileHandle renderSize fov useSceneLights:useSceneLights imgType:imgType mode:mode view:view) then
counter += 1
) catch(print ("error (object:" + o.name + ")"))
i += 1.
lblProgTotal.text = "Processed/Total number of objects: " + (i as integer) as string + "/" + (objects.count) as string
pbProgress.value = i / (objects.count as float) * 100.
)
lblProgExported.text = "Actual number of exported objects: " + counter as string
-- write the HTML footers
writeHTMLFooterCode HTMLFileHandle &indent
-- close the HTML file
close HTMLFileHandle
-- restore scene to original state.
for l in enabledLights do
l.baseObject.on = on
unHide visObjs
backgroundColor = BGCol
enableSceneRedraw()
-- return the HTML filename
HTMLFileName
)
-- Zip Functions
-------------------------------------------------------------------
-- archives all the files in dir into zipFilename using maxzip.exe
fn zipFiles dir zipFilename =
(
if zipFilename == "" then
return false
if dir[dir.count] != "\\" and dir[dir.count] != "/" then
dir += "\\"
local files = getFiles (dir + "*")
if not doesFileExist dir or files.count == 0 then
return false
-- generate a file with the list of files to be archived
local listFilename = sysInfo.tempDir + "~maxzipTempList" + timeStamp() as string + ".lst"
local f = createFile listFilename
if f == undefined then
return false
for i in files do
format "%
" i to:f
flush f
close f
-- archive the files into the zip
local curDir = sysInfo.currentDir
local zipUtil = getDir #maxRoot + "maxzip.exe"
local zipFile = getFileNameFile zipFilename + getFileNameType zipFilename
local cmd = "" as stringStream
sysInfo.currentDir = getFilenamePath zipFilename
format "\"%\" % @%" zipUtil zipFile listFilename to:cmd
local archive = dosCommand cmd
sysInfo.currentDir = curDir
-- delete the temporary list file
deleteFile listFilename
-- return true on success false on failure
archive == 0
)
-- deletes all files in a give folder
fn deleteFiles dir =
(
if dir[dir.count] != "\\" and dir[dir.count] != "/" then
dir += "\\"
for f in getFiles (dir + "*") do
deleteFile f
)
-- UI Functions
-------------------------
-- This function is responsible for all the UI elements' update such as enabling/disabling etc.
fn updateUI =
(
local f = if rbOutputFolder.state == 1 then maxFilePath else edOutputFolder.text
if f != "" and f[f.count] != "\\" and f[f.count] != "/" then
f += "\\"
bnGo.enabled = doesFileExist f
edOutputFolder.enabled = rbOutputFolder.state == 2
bnOutputFolderBrowse.enabled = rbOutputFolder.state == 2
edZipFile.enabled = cbEnableZip.checked
bnZipFileBrowse.enabled = cbEnableZip.checked
cbZipDeleteFiles.enabled = cbEnableZip.checked
bnZipOpenFolder.enabled = doesFileExist (getFilenamePath edZipFile.text)
bnView.enabled = doesFileExist HTMLFilename
bnOpenFolder.enabled = doesFileExist (getFilenamePath HTMLFilename)
)
-- Event Handlers
-----------------------------------------------------------------------
on rbOutputFolder changed state do (
if state == 1 then (
if maxFilePath == "" then (
messageBox "It appears the current max file has never been save.
Please save it before using this option."
rbOutputFolder.state = 2
state = 2
)
)
updateUI()
)
on edOutputFolder changed txt do (
updateUI()
)
on bnOutputFolderBrowse pressed do (
local f = getSavePath()
if f != undefined then (
edOutputFolder.text = f
updateUI()
)
)
on cbEnableZip changed state do (
updateUI()
)
on edZipFile changed txt do (
updateUI()
)
on bnZipFileBrowse pressed do (
local f = getSaveFilename filename:(maxFilePath + getFilenameFile maxFileName + ".zip") types:"ZIP Files (*.zip)|*.zip|All Files (*.*)|*.*"
if f != undefined then (
edZipFile.text = f
updateUI()
)
)
on bnZipOpenFolder pressed do (
if doesFileExist (getFilenamePath edZipFile.text) then
shellLaunch (getFilenamePath edZipFile.text) ""
)
on bnGo pressed do (
bnGo.enabled = false
HTMLFilename = contactSheet()
local dir = getFilenamePath HTMLFilename
if cbEnableZip.checked then (
local zipped = zipFiles dir edZipFile.text
if zipped then (
if cbZipDeleteFiles.checked and (queryBox "Delete all files in the contact sheet folder?
This cannot be undone!") then
deleteFiles dir
) else (
messageBox "The zipping of the files failed."
)
)
updateUI()
)
on bnView pressed do (
shellLaunch HTMLFilename ""
)
on bnOpenFolder pressed do (
shellLaunch (getFilenamePath HTMLFilename) ""
)
on ro_ContactSheet open do (
updateUI()
)
)-- end of ro_ContactSheet
createDialog ro_ContactSheet width:310
again, max 7
LOL. I though so too…
I guess I’m too lazy to do anything about it, and I’m getting a bit bored
cheers,
o
so ofer does it mean you are ready to work (join us)?
we might use another scripter its getting busy
man it is sweet
I would add an option to break it into groups by hierarchy or skin etc` so that it would be more production worthy but it is so cool non the less