Chapter 2 · Examples

2.6Outfit Builder

Select the garment meshes in dressing order, innermost first (shirt, then jacket, then coat), and build them into one Solver group: every garment is rebuilt into even cloth, and all of them share one Solver, one Self Collision with layer ordering, one Gravity and one collider list. A second button prints a report of every cloth in the scene.

rollout OutfitBuilder "TenPix Cloth - Outfit Builder" (
    local body = undefined
    spinner spnEdge  "Cloth density"  range:[0.2,10,2.0]  fieldwidth:50
    spinner spnThick "Self thickness" range:[0.05,5,0.15] fieldwidth:50
    dropdownlist ddQ "Quality" items:#("Draft (1/1)", "Normal (4/2)", "High (8/4)") selection:2
    checkbox chkWind "Add wind rig" checked:false
    pickbutton pbBody "Pick Body (collider)" width:190
    button btnBuild  "Build Outfit From Selection" width:190
    button btnReport "Scene Cloth Report" width:190
    on pbBody picked obj do ( body = obj; pbBody.text = "Body: " + obj.name )
    on btnBuild pressed do (
        local order = selection as array
        if order.count == 0 then ( messageBox "Select the garments first (innermost first)."; return() )
        -- substeps, iterations, contact passes, stretch quality
        local q = #( #(1,1,1,1), #(4,2,1,2), #(8,4,4,4) )[ddQ.selection]
        for o in order do (                            -- the rebuild is per garment
            addModifier o (Tnpix_Garment())
            o.modifiers[#Tnpix_Garment].edgeLength = spnEdge.value
        )
        -- ONE Solver group: the same Self Collision, Gravity, Collision, Forces and Solver on every garment
        local sc = Tnpix_Self_Collision()
        sc.tnEnabled         = true
        sc.layerOrdering     = true
        sc.layerHoldStrength = 4.0
        sc.thickness         = spnThick.value
        local grav = Tnpix_Gravity()
        local col  = Tnpix_Collision()
        local fr   = undefined
        if chkWind.checked do (
            local w = Wind pos:[0,-250,120] strength:0.5 decay:0.0 turbulence:0.4 frequency:2.0
            w.rotation = eulerAngles 90 0 0            -- blow across the outfit, not straight up
            fr = Tnpix_Forces()
            append fr.forceNodes w
            append fr.forceMuted 0
        )
        local s = Tnpix_Solver()
        s.substeps       = q[1]
        s.iterations     = q[2]
        s.contactPasses  = q[3]
        s.stretchQuality = q[4]
        for i = 1 to order.count do (
            local o = order[i]
            addModifier o sc
            addModifier o grav
            addModifier o col
            if fr != undefined do addModifier o fr
            addModifier o s                            -- last on = on top
            -- the garment list behind the Self Collision panel: garment i sits on layer i - 1
            append sc.memberHandles     (o.inode.handle)
            append sc.memberLayers      (i - 1)
            append sc.memberFrictions   -1.0           -- -1 = inherit
            append sc.memberThicknesses -1.0
            format "layer %: % ready\n" (i - 1) o.name
        )
        if body != undefined then (
            TnpixTool.colAdd order[1] body
            format "OUTFIT BUILT - % layers in one Solver group, body added as collider. Play.\n" order.count
        ) else (
            format "OUTFIT BUILT - % layers in one Solver group. Pick the body on the Tnpix Collision panel, then play.\n" order.count
        )
    )
    on btnReport pressed do (
        format "\n===== SCENE CLOTH REPORT =====\n"
        for o in objects where o.modifiers[#Tnpix_Solver] != undefined do (
            local s = o.modifiers[#Tnpix_Solver]
            format "%  | substeps % iter % contact % | heatmap %\n" o.name s.substeps s.iterations s.contactPasses s.stressHeatmap
            local sc = o.modifiers[#Tnpix_Self_Collision]
            if sc != undefined do format "     self: on % thickness % friction % layers % hold %\n" sc.tnEnabled sc.thickness sc.friction sc.layerOrdering sc.layerHoldStrength
            local f = o.modifiers[#Tnpix_Forces]
            if f != undefined do for n in f.forceNodes do format "     force: %\n" n.name
            if o.modifiers[#Tnpix_Collision] != undefined do format "     colliders: %\n" (TnpixTool.colCount o)
        )
        format "==============================\n"
    )
)
createDialog OutfitBuilder 210 220

What you should see: select your garments in order, optionally pick the body, and click Build Outfit From Selection. The script names each layer as it goes. Open the Self Collision panel on any garment: the garment list shows every layer, innermost first. Play: the layers settle on the body and on each other instead of passing through. A new Self Collision starts switched on; the explicit sc.tnEnabled = true keeps the script's intent plain and does not depend on the default.