I am working on sort of complex (for me) 3D printing model. I am a relic so I don't want to switch to other modeling softwares. But the STL export is bit of a pita - for one my objects come in wrong scale (1/10th) in the slicer and any mirrored part that looks fine in Max side has inverted normals. This means any export from work file takes several steps to get it right. below is export script that creates a clean mesh entity attaches snapshot copies of your selected parts, exports with a scale multiplier option (defaults to 10 but you can change it). exports and deletes the temp objects, seems to work exactly right currently. Claude work. https://preview.redd.it/8d9ggugcj6xh1.png?width=1914&format=png&auto=webp&s=6f253de3ec70ae9b935e2930d404b58a1abb434b /* Export Collapsed STL (v9) 1. Snapshots the selected geometry at the current frame (no cloning, hierarchy untouched) 2. Creates a box at the origin, deletes all its faces, attaches the snapshots to it - after each attach, the part's winding is verified against its expected world-space normal (mirroring taken into account) and the part is flipped if it disagrees 3. Asks for scale multiplier (default 10), scales vertices about the origin 4. Exports (scene export path, exporter's selection-only option), deletes the temp mesh, restores selection + frame */ global _stlExpScale = undefined format "=== Export Collapsed STL v9 ===\n" rollout rolStlScale "Export STL v9" width:210 ( spinner spnScale "Scale multiplier:" range:[0.0001, 100000.0, 10.0] type:#float fieldWidth:60 align:#center button btnOK "Export" width:80 across:2 button btnCancel "Cancel" width:80 on btnOK pressed do ( _stlExpScale = spnScale.value; destroyDialog rolStlScale ) on btnCancel pressed do ( _stlExpScale = undefined; destroyDialog rolStlScale ) ) fn findStlExporter = ( local found = undefined for c in exporterPlugin.classes do ( if (matchPattern (c as string) pattern:"*stl*") do ( found = c exit ) ) found ) -- world-space normal of face f using the raw winding, vertices transformed by tm fn faceNormalAt m f tm = ( local fv = getFace m f local a = (getVert m fv.x) * tm local b = (getVert m fv.y) * tm local c = (getVert m fv.z) * tm cross (b - a) (c - a) ) -- first face with a usable (non-degenerate) normal, 0 if none fn firstGoodFace m tm = ( local idx = 0 for f = 1 to m.numfaces while idx == 0 do ( if (length (faceNormalAt m f tm)) > 0.000001 do idx = f ) idx ) fn exportCollapsedSTL = ( local origSel = selection as array local geo = for o in origSel where (superClassOf o == GeometryClass) and (canConvertTo o Editable_Mesh) collect o if geo.count == 0 do ( messageBox "Select at least one geometry object." title:"Export Collapsed STL" return false ) _stlExpScale = undefined createDialog rolStlScale modal:true if _stlExpScale == undefined do return false local mult = _stlExpScale local baseName = "export" if maxFileName != "" do baseName = getFilenameFile maxFileName local outFile = getSaveFileName caption:"Export collapsed STL" filename:(maxFilePath + baseName + ".stl") types:"STL (*.stl)|*.stl|" if outFile == undefined do return false if (toLower (getFilenameType outFile)) != ".stl" do outFile += ".stl" local origFrame = sliderTime local dups = #() local shell = undefined local errMsg = undefined try ( -- duplicates: snapshots at the current frame (modifiers/animation/dynamics baked, -- hierarchy and originals untouched; the snapshot node keeps the original's world transform) for o in geo do append dups (snapshot o) -- empty shell at origin shell = box pos:[0,0,0] length:1 width:1 height:1 name:"__stl_export_tmp__" convertToMesh shell meshop.deleteFaces shell #{1..shell.numfaces} delIsoVerts:true update shell -- attach with winding verification for d in dups do ( local t = d.transform local par = 1.0 if (dot (cross t.row1 t.row2) t.row3) < 0 do par = -1.0 -- expected world normal of a reference face (raw winding, flipped for mirrored parts) local fi = firstGoodFace d t local expN = undefined if fi > 0 do expN = (faceNormalAt d fi t) * par local startFaces = shell.numfaces local done = false try ( attach shell d; done = true ) catch () if not done and isValidNode d do meshop.attach shell d attachMat:#neither deleteSourceNode:true update shell local flipped = false if fi > 0 and shell.numfaces > startFaces do ( local gotN = faceNormalAt shell (startFaces + fi) (matrix3 1) if (dot expN gotN) < 0 do ( meshop.flipNormals shell #{(startFaces + 1)..shell.numfaces} flipped = true ) ) format "part: parity %, native attach %, flipped %\n" par done flipped ) dups = #() update shell if shell.numfaces == 0 do throw "No faces after attach" -- scale vertices about the origin if mult != 1.0 do ( for i = 1 to shell.numverts do setVert shell i ((getVert shell i) * mult) update shell ) -- export select shell local expCls = findStlExporter() format "STL exporter: %\n" expCls if expCls != undefined then exportFile outFile #noPrompt using:expCls else exportFile outFile #noPrompt if not (doesFileExist outFile) do throw ("exportFile ran but no file was created: " + outFile) ) catch ( errMsg = getCurrentException() ) -- cleanup local leftovers = for d in dups where isValidNode d collect d if leftovers.count > 0 do delete leftovers if shell != undefined and isValidNode shell do delete shell sliderTime = origFrame if origSel.count > 0 then select origSel else clearSelection() if errMsg != undefined then ( messageBox ("Export failed:\n" + errMsg) title:"Export Collapsed STL v9" false ) else ( format "Exported: %\n" outFile true ) ) exportCollapsedSTL()   submitted by   /u/kerosene350   to   r/3dsmax [link]   [comments]