install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/3dsmax-scripting" ~/.claude/skills/comeonoliver-skillshub-3dsmax-scripting && rm -rf "$T"
manifest:
skills/TerminalSkills/skills/3dsmax-scripting/SKILL.mdsource content
3ds Max Scripting
Automate 3ds Max workflows with MAXScript (native) and Python (3ds Max 2022+).
MAXScript Basics
MAXScript is 3ds Max's built-in scripting language. Run scripts from the MAXScript Listener (F11), Script Editor, or command line.
Objects and Scene
-- Create objects local b = Box width:100 height:50 length:100 pos:[0, 0, 0] name:"MyBox" local s = Sphere radius:25 pos:[200, 0, 25] segments:32 local p = Plane width:500 length:500 pos:[0, 0, 0] -- Access objects local obj = $MyBox -- By name ($ selector) local obj = getNodeByName "MyBox" -- By name (function) local all = objects -- All scene objects local sel = selection -- Current selection -- Transform obj.pos = [100, 200, 0] -- Position obj.rotation = eulerAngles 0 0 45 -- Rotation (degrees) obj.scale = [2, 2, 2] -- Scale -- Properties obj.wirecolor = color 255 0 0 -- Wireframe color obj.renderable = true obj.isHidden = false -- Iterate all objects for obj in objects do ( if classOf obj == Box then ( format "Box: % at %\n" obj.name obj.pos ) )
Materials
-- Standard material local mat = StandardMaterial() mat.name = "Red Glossy" mat.diffuseColor = color 200 30 30 mat.specularLevel = 80 mat.glossiness = 60 -- V-Ray material (requires V-Ray installed) local vmat = VRayMtl() vmat.name = "Wood Floor" vmat.diffuse = color 180 140 100 vmat.reflection = color 30 30 30 -- Subtle reflection vmat.reflectionGlossiness = 0.85 -- Slightly rough vmat.texmap_diffuse = BitmapTexture filename:"D:/textures/wood_diffuse.jpg" vmat.texmap_bump = BitmapTexture filename:"D:/textures/wood_normal.jpg" vmat.texmap_bump_multiplier = 1.5 -- Bump strength -- Apply material to object $MyBox.material = vmat -- Multi-sub material (different material per face ID) local multi = MultiSubMaterial numsubs:3 multi[1] = VRayMtl name:"Wall Paint" diffuse:(color 240 238 232) multi[2] = VRayMtl name:"Wood Trim" diffuse:(color 160 120 80) multi[3] = VRayMtl name:"Glass" diffuse:(color 200 220 230) refraction:(color 250 250 250)
Cameras
-- V-Ray Physical Camera (archviz standard) fn createArchvizCamera name pos target fov:65.0 = ( local cam = VRayPhysicalCamera() cam.name = name cam.pos = pos cam.targeted = true cam.target.pos = target -- Lens cam.specify_fov = true cam.fov = fov -- Exposure cam.ISO = 400 cam.shutter_speed = 60.0 cam.f_number = 2.8 -- Auto white balance cam.white_balance_preset = 1 -- Daylight -- Vertical correction (crucial for archviz — keeps verticals straight) cam.auto_vertical_tilt_correction = 1.0 cam ) createArchvizCamera "LivingRoom" [5, -3, 1.5] [-2, 5, 1.2] fov:75
Lights
-- V-Ray Sun + Sky (exterior lighting) local sun = VRaySun pos:[100, -50, 80] sun.intensity_multiplier = 1.0 sun.size_multiplier = 3.0 -- Soft shadows sun.turbidity = 3.0 -- Atmosphere haze -- V-Ray Rectangle Light (interior fill) local rect = VRayLight() rect.type = 1 -- Plane light rect.pos = [0, 0, 2.8] -- Ceiling height rect.multiplier = 15.0 rect.color = color 255 244 229 -- Warm white (3000K) rect.width = 60 rect.height = 60 rect.invisible = true -- Don't render the light shape -- V-Ray IES Light (architectural fixtures) local ies = VRayIES() ies.pos = [1.5, 3.0, 2.7] ies.ies_file = "D:/ies/downlight.ies" ies.multiplier = 800.0 -- Lumens ies.color_mode = 1 -- Temperature ies.color_temperature = 3000 -- Warm
File I/O
-- Read JSON config (3ds Max 2022+) fn readJSON path = ( local f = openFile path mode:"r" local str = "" while not eof f do str += readLine f + "\n" close f -- Use .NET JSON parser local jObj = (dotNetClass "Newtonsoft.Json.Linq.JObject").Parse str jObj ) -- Write log file fn writeLog path msg = ( local f = openFile path mode:"a" if f == undefined then f = createFile path format "% | %\n" localTime msg to:f close f ) -- Import/export importFile "D:/models/furniture.fbx" #noPrompt exportFile "D:/export/scene.fbx" #noPrompt selectedOnly:true
Python in 3ds Max
3ds Max 2022+ includes Python 3 with
pymxs module for accessing MAXScript objects:
"""scene_audit.py — Audit scene for common archviz issues.""" import pymxs from pymxs import runtime as rt def audit_scene(): """Check scene for common issues: missing textures, high-poly objects, etc.""" issues = [] for obj in rt.objects: # Check for high-poly objects (>500K faces in archviz is suspicious) if rt.classOf(obj) in [rt.Editable_Poly, rt.Editable_Mesh]: face_count = rt.getNumFaces(obj) if face_count > 500000: issues.append(f"High poly: {obj.name} ({face_count:,} faces)") # Check for missing materials if obj.material is None and obj.renderable: issues.append(f"No material: {obj.name}") # Check for missing texture files for mat in rt.sceneMaterials: check_material_textures(mat, issues) return issues def check_material_textures(mat, issues): """Recursively check material tree for missing texture files.""" if hasattr(mat, 'texmap_diffuse') and mat.texmap_diffuse: tex = mat.texmap_diffuse if hasattr(tex, 'filename') and tex.filename: import os if not os.path.exists(tex.filename): issues.append(f"Missing texture: {tex.filename} (in {mat.name})")
Batch Operations
Command Line Rendering
# Render a scene from command line (no GUI) "C:\Program Files\Autodesk\3ds Max 2025\3dsmax.exe" ^ -silent -mxs "loadMaxFile \"D:/scene.max\"; render()" ^ -o "D:/output/render.exr" -w 4000 -h 2250 # Run a MAXScript file 3dsmax.exe -silent -mxs "fileIn \"D:/scripts/batch_render.ms\"" # Run with specific camera 3dsmax.exe -silent -mxs "loadMaxFile \"D:/scene.max\"; viewport.setCamera (getNodeByName \"Camera01\"); render()"
Batch Process Multiple Files
-- batch_process.ms — Process all .max files in a directory fn processAllScenes folderPath = ( local files = getFiles (folderPath + "/*.max") local results = #() for f in files do ( format "Processing: %\n" f loadMaxFile f quiet:true -- Do something with each scene local objCount = objects.count local camCount = (for c in cameras where classOf c != Targetobject collect c).count append results #(getFilenameFile f, objCount, camCount) resetMaxFile #noPrompt ) results )
Scene Management
-- Layer management fn organizeByType = ( local layerMgr = LayerManager -- Create layers local furnitureLayer = layerMgr.newLayerFromName "Furniture" local architectureLayer = layerMgr.newLayerFromName "Architecture" local lightsLayer = layerMgr.newLayerFromName "Lights" for obj in objects do ( case (superClassOf obj) of ( Light: lightsLayer.addNode obj default: ( if matchPattern obj.name pattern:"*chair*" or matchPattern obj.name pattern:"*table*" or matchPattern obj.name pattern:"*sofa*" then furnitureLayer.addNode obj else architectureLayer.addNode obj ) ) ) ) -- Selection sets selectionSets["Interior Cameras"] = for c in cameras where matchPattern c.name pattern:"int_*" collect c -- Named selection sets for render elements fn selectByMaterialName matName = ( select (for obj in objects where obj.material != undefined and obj.material.name == matName collect obj) )
Guidelines
- Always
for batch operations — without it, file dialogs block script execution#noPrompt - Use
blocks for destructive operations — wrap scene changes so they can be undoneundo on
(garbage collect) in loops — MAXScript leaks memory in long-running scripts. Callgc()
periodically.gc light:true- Test in Listener first — debug scripts interactively before running them in batch mode
- V-Ray objects require V-Ray loaded — check
before creating V-Ray-specific objectsrenderers.current - File paths use forward slashes or escaped backslashes —
or"D:/path"
, never raw"D:\\path""D:\path" - Python
is slower than MAXScript — use Python for file I/O and logic, MAXScript for scene manipulationpymxs - Save before batch operations —
as a safety netsaveMaxFile (maxFilePath + maxFileName)