Skip to main content

Snippets

Mesh

Explode EditPoly

fn explodeEPoly obj =
(
    -- Storing frequently used functions in user variables for faster access
    local getNumFaces = polyOp.getNumFaces
    local getElementsUsingFace = polyOp.getElementsUsingFace
    local detachFaces = polyOp.detachFaces
    
    if getNumFaces obj > 0 then
    (
        local element = getElementsUsingFace obj 1
        
        while element.numberset < (getNumFaces obj) do
        (
            detachFaces obj element asNode:true
            element = getElementsUsingFace obj 1
        )
    )
)

explodeEPoly $

User Interface

Open File Dialog Multiselect

fn openFileDialog title:"Open File" filter:"All Files|*.*" multiselect:false =
(
    local dialog = dotnetobject "System.Windows.Forms.OpenFileDialog"
    dialog.title = title
    dialog.filter = filter
    dialog.multiselect = multiselect
   
    local result = undefined
   
    if (dialog.showDialog() == (dotnetclass "System.Windows.Forms.DialogResult").Ok) then
    (
        if multiselect then
            result = dialog.filenames
        else
            result = dialog.filename
    )

    dialog.dispose()

    return result
)

openFileDialog multiselect:true

Advanced Message Box

fn MessageBoxEx message caption:"Message" buttons:#OK icon:#Information =
(
    local MsgBox = dotnetclass "System.Windows.Forms.MessageBox"
    local MessageBoxButtons = dotnetclass "System.Windows.Forms.MessageBoxButtons"
    local MessageBoxIcon = dotnetclass "System.Windows.Forms.MessageBoxIcon"
    local DialogResult = dotnetclass "System.Windows.Forms.DialogResult"
    
    local _buttons = case buttons of
    (
        #AbortRetryIgnore: MessageBoxButtons.AbortRetryIgnore
        #OK: MessageBoxButtons.OK
        #OKCancel: MessageBoxButtons.OKCancel
        #RetryCancel: MessageBoxButtons.RetryCancel
        #YesNo: MessageBoxButtons.YesNo
        #YesNoCancel: MessageBoxButtons.YesNoCancel
    )
    
    local _icon = case icon of
    (
        #Asterisk: MessageBoxIcon.Asterisk
        #Error: MessageBoxIcon.Error
        #Exclamation: MessageBoxIcon.Exclamation
        #Hand: MessageBoxIcon.Hand
        #Information: MessageBoxIcon.Information
        #None: MessageBoxIcon.None
        #Question: MessageBoxIcon.Question
        #Stop: MessageBoxIcon.Stop
        #Warning: MessageBoxIcon.Warning
    )
    
    local result = case MsgBox.Show message caption _buttons _icon of
    (
        (DialogResult.Abort): #Abort
        (DialogResult.Cancel): #Cancel
        (DialogResult.Ignore): #Ignore
        (DialogResult.No): #No
        (DialogResult.None): #None
        (DialogResult.OK): #OK
        (DialogResult.Retry): #Retry
        (DialogResult.Yes): #Yes
    )
    
    return result
)

MessageBoxEx "Some text"

Custom Picker Dialog

fn numberPicker =
(
    rollout picker "Number Picker" width:160
    (
        local result

        spinner spnValue "Value:" width:126 align:#center
        button btnOk "Ok" width:60 align:#center across:2
        button btnCancel "Cancel" width:60 align:#center
        
        on btnOk pressed do
        (
            result = spnValue.value
            destroyDialog picker
        )
        
        on btnCancel pressed do
            destroyDialog picker;
    )
    createDialog picker modal:true
    
    return picker.result
)

print (numberPicker())

SubAnim Picker

(
    local ContextMenuStrip = dotnetclass "System.Windows.Forms.ContextMenuStrip"
    local ToolStripMenuItem = dotnetclass "System.Windows.Forms.ToolStripMenuItem"
   
    local _contextMenuStrip
    local _pickSubAnimCallback
   
    fn ToolStripMenuItem_Click s e =
    (
        _pickSubAnimCallback s.Tag.Value
       
        if s.HasDropDownItems then
            _contextMenuStrip.Close()
    )
   
    fn subAnimsToMenuItem subanim =
    (      
        local item = dotnetobject ToolStripMenuItem subanim.name
        item.Tag = dotnetmxsvalue subanim
 
        if subanim.numSubs > 0 then
        (
            item.DoubleClickEnabled = true
            dotnet.addEventHandler item "DoubleClick" ToolStripMenuItem_Click
           
            for i = 1 to subanim.numSubs do
            (
                local subitem = subAnimsToMenuItem subanim[i]
                item.DropDownItems.Add subitem
            )
        )
        else dotnet.addEventHandler item "Click" ToolStripMenuItem_Click;
       
        return item
    )
   
    fn applyFilter menuitem filter =
    (
        if menuitem.HasDropDownItems then
        (
            local hasAvailableItems = false
           
            for i = 0 to menuitem.DropDownItems.Count - 1 do
            (
                applyFilter menuitem.DropDownItems.Item[i] filter
 
                if not hasAvailableItems then
                    hasAvailableItems = menuitem.DropDownItems.Item[i].Available
            )
           
            menuitem.Available = hasAvailableItems
            menuitem.DoubleClickEnabled = filter menuitem.Tag.Value
        )
        else menuitem.Available = filter menuitem.Tag.Value
    )
           
    fn pickSubAnim maxwrapperobj callback filter:unsupplied =
    (
        local isMaxWrapper = iskindof maxwrapperobj maxwrapper
        local isMaxScriptFunction = classof callback == maxscriptfunction
 
        if isMaxWrapper and isMaxScriptFunction then
        (
            _contextMenuStrip = dotnetobject ContextMenuStrip
            _pickSubAnimCallback = callback
           
            for i = 1 to maxwrapperobj.numsubs do
                _contextMenuStrip.Items.Add (subAnimsToMenuItem maxwrapperobj[i])
           
            if filter != unsupplied then
                for i = 0 to _contextMenuStrip.Items.Count - 1 do
                    applyFilter _contextMenuStrip.Items.Item[i] filter
           
            _contextMenuStrip.Show mouse.screenpos.x mouse.screenpos.y
        )
    )
   
    /* How to use */
   
    fn pickCallback subanim =
        print subanim;
   
    fn subAnimFilter subanim =
        return subanim.value != undefined;
   
    pickSubAnim $ pickCallback filter:subAnimFilter
)

Array

Shuffle

fn shuffle arr =
(
    local num = arr.count
    local j

    for i = 1 to num do
    (
        j = random 1 num
        swap arr[i] arr[j]
    )

    return arr
)

String

Regex Substitution

fn gsub string pattern evaluator =
(
    global __RegexMatch
    
    if __RegexMatch == undefined then 
        __RegexMatch = (dotnetclass "System.Text.RegularExpressions.Regex").Match;

    local match = __RegexMatch string pattern
    local result
    local pos = 1
    
    if match.Success then
    (
        local stream = stringstream ""

        while match.Success do
        (
            append stream (substring string pos (match.Index + 1 - pos))
            append stream (evaluator match)
            pos = match.Index + match.Length + 1
            match = match.NextMatch()
        )

        if pos < string.count then
            append stream (substring string pos -1);
        
        result = stream as ::string
        free stream
    )
    else result = copy string
    
    return result
)

-- Finding and replacing integer decimal numbers with hexadecimal
fn eval match =
    return "0x" + bit.intAsHex (match.Value as integer);

gsub "5 a 200 b 500 c" @"\d+" eval
-- 0x5 a 0xc8 b 0x1f4 c

Formatted Print

fn printf str args =
(
    global __printfMatches
    
    if __printfMatches == undefined then
    (
        local pattern = @"%([-+#0 ]*\d*(?:\.\d+)?(?:l|l32|l64)?[dioucxXeEfgGs])"
        __printfMatches = (dotnetobject "System.Text.RegularExpressions.Regex" pattern).Matches;
    )
    
    local matches = __printfMatches str
    local output = stringstream "" 
    local pos = 1

    for i = 1 to matches.count do
    (
        local m = matches.Item[i - 1]
        local g = m.Groups.Item[1]
        local type = m.Groups.item["type"].Value
    
        append output (substring str pos (m.Index + 1 - pos))
        append output (formattedprint args[i] format:g.Value)

        pos = m.Index + 1 + m.Length
    )

    append output (substring str pos (str.count - pos))
    
    return output as string
)

printf "%.5f %s %x %c" #(1.234, "maxscript", 1000, 97)
-- "1.23400 maxscript 3e8 a"

Bitmap

Line Drawing

fn drawLine bmp x0 y0 x1 y1 color:white =
(
    local dx = x1 - x0
    local dy = y1 - y0
    local col = #(color)
            
    if abs dx > abs dy then
    (
        local step = dy / float dx
        for x = x0 to x1 by (if dx > 0 then 1 else -1) do
            setPixels bmp [x, y0 + step * (x - x0) + 0.5] col;
    )
    else
    (
        local step = dx / float dy
        for y = y0 to y1 by (if dy > 0 then 1 else -1) do
            setPixels bmp [x0 + step * (y - y0) + 0.5, y] col;
    )
)

(
    local bmp = bitmap 500 500 color:black
    
    for i = 1 to 120 do
    (
        local x1 = random 0 500
        local y1 = random 0 500
        local x2 = random 0 500
        local y2 = random 0 500
        local col = random black white
        
        drawLine bmp x1 y1 x2 y2 color:col
    )
    display bmp
)

Color

Color To Integer

fn colorToInt color =
    return (bit.shift (integer color.r) 16) + (bit.shift (integer color.g) 8) + integer color.b;

fn intToColor integer =
    return color \
        (bit.and (bit.shift integer -16) 255) \
        (bit.and (bit.shift integer -8) 255) \
        (bit.and integer 255);

Viewport

Material Display (Display Filter Fix)

fn restoreMaterialDisplay =
(
    -- 0x00000241 - REFMSG_NODE_DISPLAY_PROP_CHANGED - maxsdk\include\ref.h
    for i in objects where not i.ishiddenInVpt do
        notifyDependents i partID:#display msg:0x00000241;
)

callbacks.addScript #customDisplayFilterChanged "restoreMaterialDisplay()" id:#restoreMaterialDisplay