Sunday 6 January 2019

Viewport Text

This is less of a snippet and more a full-blown script...

Allows you to display viewport text for:
  • object name and type
  • edge length
  • face area and texels
Text is displayed on the object or at the corner of the viewport, according to preference.

A UI is provided to configure the viewport settings, and may be closed once the viewport text is activated.





global ViewportText_EdgeEnabled
global ViewportText_EdgeEnabled
global ViewportText_EdgeCount
global ViewportText_EdgeTotal
global ViewportText_EdgeAverage

global ViewportText_ObjectEachEnabled
global ViewportText_ObjectName
global ViewportText_ObjectType
global ViewportText_ObjectColorBy

global ViewportText_ObjectSummaryEnabled
global ViewportText_ObjectSummaryName
global ViewportText_ObjectSummaryType

global ViewportText_FaceCount
global ViewportText_FaceArea
global ViewportText_FaceTexels

global ViewportText_TexelsPerMeter
global ViewportText_Position
global ViewportText_Enabled

global ViewportText_VerticalOffset
global GW_displayViewportText
global ViewportText_ObjectColorByColors = #("Always Yellow", "WireColor", "SuperClass", "Geometry SubClass") 


if ViewportText_Enabled == undefined then
(
    
    ViewportText_ObjectEachEnabled = True
    ViewportText_ObjectName = True
    ViewportText_ObjectType = True
    ViewportText_ObjectColorBy = "Geometry SubClass"

    ViewportText_ObjectSummaryEnabled = True
    ViewportText_ObjectSummaryName = True
    ViewportText_ObjectSummaryType = True
    
    ViewportText_EdgeCount = true
    ViewportText_EdgeTotal = true
    ViewportText_EdgeAverage = true
    
    ViewportText_FaceCount = True
    ViewportText_FaceArea = True
    ViewportText_FaceTexels = True
    
    ViewportText_VerticalOffset = 0
    ViewportText_TexelsPerMeter = 1024
    ViewportText_Position = "Bottom-left"
    ViewportText_Enabled = True

)

unregisterRedrawViewsCallback GW_displayViewportText

----------------------
-- Helper Funcitons
----------------------

fn round_to val n =
(
local mult = 10.0 ^ n
(floor ((val * mult) + 0.5)) / mult
)

fn systemAreaToMetersSquared systemUnitsArea =
(
    systemUnitsAreaInMeterSquare = case units.SystemType of
    (
        #Inches: 1550.0
        #Feet: 10.7639
        #Miles: 0.000000386102
        #Millimeters: 1000000.0
        #Centimeters: 10000.0
        #Meters: 1.0
        #Kilometers: 0.000001
    )
    
    meterssquared = systemUnitsArea / systemUnitsAreaInMeterSquare
    meterssquared *= (units.SystemScale ^ 2) 
    return round_to meterssquared 2
)

fn systemAreaToTexelArea systemUnitsArea =
(
    meterssquared = systemAreaToMetersSquared systemUnitsArea
    onesidetexels = (((meterssquared ^ 0.5) * ViewportText_TexelsPerMeter) as integer) as string
    
    return (onesidetexels + "x" + onesidetexels + "px")
)

fn colorFromSuperClass obj =
(
    cyan = color 0 255 255
    return case (superclassof obj) of
    (
        GeometryClass: white
        Helper: green
        Shape: cyan
        Camera: orange
        Light: yellow
        System: red
        SpaceWarpObject: red
        UndefinedClass: red
        default: red
    )
)

fn colorFromGeometryClass obj =
(
    cyan = color 0 255 255
    if superclassof obj != GeometryClass then return white
    return case (classof obj) of
    (
        Editable_Poly: green
        PolyMeshObject: green
        Editable_Mesh: yellow
        Box: cyan
        Plane: cyan
        cone: cyan
        geosphere: cyan
        sphere: cyan
        cylinder:cyan
        torus: cyan
        pyramid: cyan
        teapot: cyan
        default: if canConvertTo o Editable_Poly then orange else red
    )
)


----------------------
-- Viewport Draw
----------------------

fn drawText str textcolor worldpos textposition =
(
    extents = gw.getTextExtent str
    if textposition == "Selection" then
    (
        screenpos = gw.wTransPoint worldpos
        screenpos = [screenpos.x, gw.getWinSizeY()-screenpos.y,0]
        
        if ViewportText_VerticalOffset > 15 or ViewportText_VerticalOffset < -15 then
        (
            gw.setColor #line textcolor
            endoffset = if ViewportText_VerticalOffset > 0 then -10 else 5
            gw.hPolyline #(screenpos, screenpos + [0,ViewportText_VerticalOffset + endoffset,0]) false
        )
        
        screenpos = [screenpos.x - (extents.x / 2.0), screenpos.y - (extents.y / 2.0) + ViewportText_VerticalOffset, 0]
        gw.htext screenpos str color:textcolor
        
        
    )
    else
    (
        case textposition of
        (
            "Bottom-left": pos = [50, 20, 0]
            "Bottom-Right": pos = [gw.getWinSizeX()-extents.x, 20, 0]
            "Top-Right": pos = [gw.getWinSizeX()-extents.x, gw.getWinSizeY()-extents.y, 0]
        )
        gw.htext pos str color:textcolor
    )
  gw.enlargeUpdateRect #whole  
)


fn DisplayEdgeMeasure =
(
    str = ""
    worldpos = [0,0,0]
    
    obj = selection[1]
    op = if classof obj == editable_poly or classof obj == polymeshobject then polyop else meshop
    
    edges = getedgeselection obj as array
    if edges.count == 0 then return false
    edgelengths = #()
    midpoints = #()

    for i = 1 to edges.count do
    (
        verts = op.getvertsusingedge obj edges[i] as array
        v1 = op.getvert obj verts[1]
        v2 = op.getvert obj verts[2]
        edgelengths[i] = distance v1 v2
        midpoints[i] = (v1 + v2) / 2.0
    )

    totallength = 0
    for edlen in edgelengths do totallength += edlen
        
    midpoint = 0
    for m in midpoints do midpoint += m
    midpoint = midpoint / (midpoints.count as float)
    worldpos = midpoint

    --format "edges: %, total: %, average: %\n" edges.count totallength (totallength / edges.count)
    if ViewportText_EdgeCount then 
    (
        str += "edges: " + edges.count as string + "     "
    )
    if ViewportText_EdgeTotal then
    (
        str += "total: " + units.formatValue totallength + "    "
    )
    if ViewportText_EdgeAverage then 
    (
        str += "average: " + units.formatValue (totallength / (edges.count as float)) + "  "
    )
    
    drawText str white worldpos ViewportText_Position
)

fn DisplayFaceMeasure =
(
    str = ""
    
    worldpos = [0,0,0]
    obj = selection[1]
    op = if classof obj == editable_poly or classof obj == polymeshobject then polyop else meshop
    
    faces = (getfaceselection obj) as array
    if faces.count == 0 then return false
    faceareas = #()
    midpoints = #() 

    for i = 1 to faces.count do
    (
        faceareas[i] = op.getfacearea obj faces[i]
        midpoints[i] = op.getfacecenter obj faces[i]
    )

    totalarea = 0
    for a in faceareas do totalarea += a
    
        
    midpoint = [0,0,0]
    for m in midpoints do midpoint += m
    midpoint = midpoint / (midpoints.count as float)
    worldpos = midpoint
--     print "--"
--     print (midpoints as array)
--     print worldpos
    
    if ViewportText_FaceCount then 
    (
        str += "faces: " + faces.count as string + "     "
    )
    if ViewportText_FaceArea then
    (
        str += "area: " + ((systemAreaToMetersSquared totalarea) as string) + "msq    "
    )
    if ViewportText_FaceTexels then
    (
        str += "texels: " + systemAreaToTexelArea totalarea + "    "
    )
    drawText str white worldpos ViewportText_Position
)


fn DisplayObjects =
(
    worldpos = [0,0,0]
    sel = getcurrentselection()
    
    matchingclasses = true
    (
        for o in sel do
        (
            if classof o != classof sel[1] then matchingclasses = false
        )
    )
    matchingsuperclasses = true
    for o in sel do (if superclassof o != superclassof sel[1] then matchingsuperclasses = false; exit)

    if ViewportText_ObjectEachEnabled then
    (
        if sel.count < 1000 then -- too many objects would be very cluttered on screen and hurt perf
        (            
            for obj in sel do
            (
                str = ""
                if ViewportText_ObjectName then str += obj.name + "    "
                if ViewportText_ObjectType then str += ((classof obj) as string) + "    "
                case ViewportText_ObjectColorBy of
                (
                    "Always Yellow": textcolor = yellow
                    "WireColor": try (textcolor = obj.wirecolor) catch (textcolor = yellow)
                    "SuperClass": textcolor = colorFromSuperClass obj
                    "Geometry SubClass": textcolor = colorFromGeometryClass obj
                )
                if textcolor == undefined then (print obj.name; print obj.wirecolor)
                
                worldpos = obj.pos
                drawText str textcolor worldpos "Selection"
            )
        )
    )

    if ViewportText_ObjectSummaryEnabled then
    (
        str = ""
        if ViewportText_ObjectSummaryName then
        (
            if sel.count == 1 then str += sel[1].name + "    "
                else str += sel.count as string + " objects    "
        )
        
        if ViewportText_ObjectSummaryType then
        (
            if matchingclasses then str += (classof sel[1]) as string + " (" + sel.count as string + ")    "
                else str += "various types (" + sel.count as string + ")    "
        )
        
        pos = ViewportText_Position
        -- prevent summary and each from overlapping
        if ViewportText_ObjectEachEnabled and ViewportText_Position == "Selection" then
        (
            pos = "Bottom-left"
        )
        
        worldpos = [0,0,0]
        for o in sel do worldpos += o.pos
        worldpos = worldpos / (sel.count as float)
        
        drawText str yellow worldpos pos
    )    
)


fn GW_displayViewportText =
(
    
    gw.setTransform (matrix3 1)
    
    -- OBJECTS
    if subobjectlevel == 0 or subobjectlevel == undefined and selection.count > 0 then
    (
        DisplayObjects()
    )
    -- EDGES
    if (subobjectlevel == 2 or subobjectlevel == 3) and selection.count == 1 then
    (
        DisplayEdgeMeasure()
    )
    -- FACES
    if (subobjectlevel == 4 or subobjectlevel == 5) and selection.count == 1 then
    (
        DisplayFaceMeasure()
    )
)


----------------------
--            UI
----------------------

try (destroydialog rol_ViewportText) catch()

rollout rol_ViewportText "Viewport Text"
(
    group "Per Object"
    (
        checkbox chk_objectname "Object Name"
        checkbox chk_objecttype "Object Type"
        radiobuttons rdo_colorby "Color By" labels:ViewportText_ObjectColorByColors
        checkbox chk_objecteach "Enabled"
    )
    group "Object Summary"
    (
        checkbox chk_objectsummaryname "Object Name / Count"
        checkbox chk_objectsummarytype "Object Type + Count"
        checkbox chk_objectsummaryenabled "Enabled"
    )
    group "Edge"
    (
        checkbox chk_edgecount "Edge Count"
        checkbox chk_edgetotal "Edge Total"
        checkbox chk_edgeaverage "Edge Average"
    )
    group "Face"
    (
        checkbox chk_facecount "Face Count"
        checkbox chk_facearea "Surface Area"
        checkbox chk_facetexels "Texel Area"
    )
    
    spinner spn_texelsPerMeter "Texels per Meter  " range:[128, 4096, 1024] scale:128 type:#integer offset:[46,4] width:110
    label lbl_pos "Position" across:2 width:80 offset:[8,2]
    dropdownlist ddl_position "" items:#("Selection", "Bottom-left", "Bottom-Right", "Top-Right") width:90 offset:[-20,0]
    spinner spn_verticalOffset "Vertical Offset      " range:[-50,50,0] scale:5 type:#integer offset:[46,-3] width:110
    checkbutton btn_enable "Enable" width:100 offset:[0,-2]
    
    fn updatePrefs =
    (
        ViewportText_EdgeCount = chk_edgecount.checked
        ViewportText_EdgeTotal = chk_edgetotal.checked
        ViewportText_EdgeAverage = chk_edgeaverage.checked
        
        ViewportText_ObjectEachEnabled = chk_objecteach.checked
        ViewportText_ObjectName = chk_objectname.checked
        ViewportText_ObjectType = chk_objecttype.checked
        ViewportText_ObjectColorBy = ViewportText_ObjectColorByColors[rdo_colorby.state]
        
        ViewportText_ObjectSummaryEnabled = chk_objectsummaryenabled.checked
        ViewportText_ObjectSummaryName = chk_objectsummaryname.checked
        ViewportText_ObjectSummaryType = chk_objectsummarytype.checked
        
        ViewportText_FaceCount = chk_facecount.checked
        ViewportText_FaceArea = chk_facearea.checked
        ViewportText_FaceTexels = chk_facetexels.checked
        
        ViewportText_TexelsPerMeter = spn_texelsPerMeter.value
        ViewportText_Position = ddl_position.selected
        ViewportText_VerticalOffset = spn_verticalOffset.value
        ViewportText_Enabled = btn_enable.checked
        
        
        btn_enable.text = if ViewportText_Enabled then "Active" else "Click to Activate"
        unregisterRedrawViewsCallback GW_displayViewportText
        if ViewportText_Enabled then registerRedrawViewsCallback GW_displayViewportText 
        redrawViews()     
    )
    
    on rol_ViewportText open do
    (
        chk_edgecount.checked = ViewportText_EdgeCount
        chk_edgetotal.checked = ViewportText_EdgeTotal
        chk_edgeaverage.checked = ViewportText_EdgeAverage
        
        chk_objecteach.checked = ViewportText_ObjectEachEnabled
        chk_objectname.checked = ViewportText_ObjectSummaryName
        chk_objecttype.checked = ViewportText_ObjectType
        rdo_colorby.state = finditem ViewportText_ObjectColorByColors ViewportText_ObjectColorBy
        
        chk_objectsummaryname.checked = ViewportText_ObjectSummaryEnabled
        chk_objectsummarytype.checked = ViewportText_ObjectSummaryType
        chk_objectsummaryenabled.checked = ViewportText_ObjectSummaryEnabled
        
        chk_facecount.checked = ViewportText_FaceCount
        chk_facearea.checked = ViewportText_FaceArea
        chk_facetexels.checked = ViewportText_FaceTexels
        
        spn_texelsPerMeter.value = ViewportText_TexelsPerMeter
        ddl_position.selection = finditem ddl_position.items ViewportText_Position
        spn_verticalOffset.value = ViewportText_VerticalOffset
        btn_enable.checked = ViewportText_Enabled
        
        updatePrefs()
    )
    
    on chk_edgecount changed val do updatePrefs()
    on chk_edgetotal changed val do updatePrefs()
    on chk_edgeaverage changed val do updatePrefs()
    
    on chk_objectname changed val do updatePrefs()
    on chk_objecttype changed val do updatePrefs()
    on rdo_colorby changed val do updatePrefs()
    on chk_objecteach changed val do updatePrefs()
    
    on chk_objectsummaryname changed val do updatePrefs()
    on chk_objectsummarytype changed val do updatePrefs()
    on chk_objectsummaryenabled changed val do updatePrefs()
    
    on chk_facecount changed val do updatePrefs()
    on chk_facearea changed val do updatePrefs()
    on chk_facetexels changed val do updatePrefs()
    on spn_texelsPerMeter changed val do updatePrefs()
    
    on btn_enable changed val do updatePrefs()
    on ddl_position selected val do updatePrefs()
    on spn_verticalOffset changed val do updatePrefs()
)

createdialog rol_ViewportText

Sunday 16 March 2014

Fix nGons

Long time no post... A full year in fact! Work has kept me busy, and I've come on a lot with maxscript writing, but haven't had much time (or need!) to write any outside of work. I'm working on a big tool, more info later.

But for now, here's a script to try and correct ngons. It's got a few problems, I haven't had time to debug. Also you'll notice it's a bit of a monster script and for anyone trying to follow along... good luck, you'll need it!

Important: There are some instances where this script crashes max. I have not fully debugged the cause yet. Use with caution!

USAGE

  1. Select one or more objects
  2. run script


* there seems to be some cases where it doesn't completely finish. Run it again if so.


  


( 
 
 local CurrentObj
 local CurrentFace
 
 local FirstVert
 
 local faceedges = #()
 local faceedgeverts = #()
 local faceverts = #()
 local facevertedges = #()
 local vertneighbourverts = #()
 
 local StructFaceVerts = #()
 local StructVertConnections = #()
 
 
 fn ConnectAndWalk v1 v2 innerverts =
 (
  --format "ConnectAndWalk: %, %, %\n" v1 v2 innerverts
  nextv1 = ((vertneighbourverts[v1] - innerverts) as array)[1]
  nextv2 = ((vertneighbourverts[v2] - innerverts) as array)[1]
  if nextv1 == v2 or nextv2 == v1 then return true --end of the line, punk!
  
  polyop.createedge CurrentObj v1 v2
  
  -- this has become so ridiculously abstract, i guarantee i wont understand this when i look back. OH HEY FUTURE MIKE!
  -- if the neighbouring verts of the next verts have none in common, we can connect
  if ((vertneighbourverts[nextv1] * vertneighbourverts[nextv2])).numberset == 0 then
   (ConnectAndWalk nextv1 nextv2 #{v1,v2})
  return true
 )
 
 
 
 struct StructVertConnection
 (
  v1,
  v2,
  --FormsTriangle,
  --FormsQuad,
  InsideVerts,
  VertDistance,
  
  on create do
  (
   VertDistance = distance (polyop.getvert CurrentObj v1) (polyop.getvert CurrentObj v2)
  )
  
 )


 fn BuildConnectionsFromVert v1 v2 ed  =
 (
  NextEdge = ((facevertedges[v2] - facevertedges[v1]) as array)[1]
  NextVert = ((faceedgeverts[NextEdge] - faceedgeverts[ed]) as array)[1]
  
  vertneighbourverts[v1][v2] = true
  vertneighbourverts[v2][v1] = true
  
  if faceverts.count <= 5 then --we only care about tri-making connections if we have a 5-sided 
   (append StructVertConnections (StructVertConnection v1 NextVert InsideVerts:#{v2})) --  FormsTriangle:true FormsQuad:false  (these work but arent needed under new system)
  
  NextNextEdge = ((facevertedges[NextVert] - facevertedges[v2]) as array)[1]
  NextNextVert = ((faceedgeverts[NextNextEdge] - faceedgeverts[NextEdge]) as array)[1]
  
  --FormsQuad = if faceverts.count <= 5 then false else true
  append StructVertConnections (StructVertConnection v1 NextNextVert InsideVerts:#{v2, NextVert}) --  FormsTriangle:(not FormsQuad) FormsQuad:FormsQuad (these work but arent needed under new system)
  
  if v2 == FirstVert then return true -- end recursion
  BuildConnectionsFromVert v2 NextVert NextEdge
  
  return true
 )



 fn FixFaceNgons obj f =
 (
  
  
 
 faceedges = #()
 faceedgeverts = #()
 faceverts = #()
 facevertedges = #()
 vertneighbourverts = #()
 
 StructFaceVerts = #()
 StructVertConnections = #()
  
  
  
  CurrentObj = obj
  CurrentFace = f
  
  faceverts = polyop.getFaceVerts obj f
  if faceverts.count < 5 then return false
  facevertsba = faceverts as bitarray
  
  faceedges = polyop.getFaceEdges obj f
  faceedgesba = faceedges as bitarray
  
  faceedgeverts = #()
  facevertedges = #()
  vertneighbourverts = #()
  
  for v in faceverts do facevertedges[v] = ((polyop.getEdgesUsingVert obj v) as bitarray) * faceedgesba
  for ed in faceedges do faceedgeverts[ed] = ((polyop.getVertsUsingEdge obj ed) as bitarray) * facevertsba
  for v in faceverts do vertneighbourverts[v] = #{} -- we populate this later
  
  StructFaceVerts = #()
  StructVertConnections = #()
  
  firstedgeverts = for v in faceedgeverts[faceedges[1]] collect v
  FirstVert = firstedgeverts[1]
  
  StructVertConnections = #()
  BuildConnectionsFromVert firstedgeverts[1] firstedgeverts[2] faceedges[1]
  
  --for vc in StructVertConnections do (print vc)
  
  shortestvc = StructVertConnections[1]
  for vc = 2 to StructVertConnections.count do
  (
   if StructVertConnections[vc].VertDistance < shortestvc.VertDistance then 
    (shortestvc = StructVertConnections[vc])
  )
  
  ConnectAndWalk shortestvc.v1 shortestvc.v2 shortestvc.InsideVerts
  
  return false
  
 )
  
  

fn FixAllNgons obj =
(
 --disablesceneredraw()
 polyop.retriangulate obj #{1..(polyop.getnumfaces obj)}
 f = 0
 while 1 == 1 do 
 (
  f += 1
  if (FixFaceNgons obj f) then f = 0
  if f >= (polyop.getnumfaces obj) then exit
 )
 enablesceneredraw()
 completeredraw()
)

fn FixAllConcave obj =
(
)



fn FixSelectedObjectsNgons =
(
 usersel = getcurrentselection()
 for o in usersel where superclassof o == geometryclass do
 (
  if classof o != editable_poly then
  (
   messagebox (o.name + " is not an editable poly")
   continue
  )
  
  FixAllNgons o
  FixAllConcave o
 )
)

FixSelectedObjectsNgons()

)

  

Wednesday 3 April 2013

Splay Normals

Foliage models often require edited normals. There are already normal-splaying scripts out there but I wanted to be able to set a position and use a strength value to effectively splay them partly from the centre of the "bush", and partly from the branch or trunk. I also wanted to be able to splay only the selected verts. This is the result.

USAGE
  • Position the working pivot
  • optionally select some of the verts (if none selected, will select all)
  • optionally set strength to a value like 0.4
  • run script



  
-- MixeScript snippet: SplayNormals
-- Splays selected normals away from the working pivot

--USAGE
-- Position the working pivot
-- select some or all of the verts (if none selected, will select all)
-- optionally set strength to a value like 0.4
-- run script
 
--SETTINGS
Strength = 1.0 -- when set to 1.0, entirely replaces existing normal direction. at 0.0, does not adjust normal at all.

max modify mode
obj = $
modPanel.setCurrentObject obj
enMods = (for m in obj.modifiers where classof m == editnormals collect m)
enMod = enMods[1]
 
subobjectLevel = 1 --verts 
vertarray = #{}
vertarray = polyop.getVertSelection obj
if vertarray.numberSet == 0 then -- no selection, select all
(
 polyop.setVertSelection obj #all
 vertarray = polyop.getVertSelection obj
)


if enMod == undefined then
(
 enMod = editnormals()
 modPanel.addModToSelection (enMod) ui:on
)

 
WPTM = WorkingPivot.getTM()
splayfrom = (WPTM[4])
modPanel.setCurrentObject obj

normarray = enMod.GetSelection()
--enMod.MakeExplicit ()
objpos = obj.pos

modPanel.setCurrentObject enMod
  
for vert in vertarray do (
 vertpos = (enMod.GetVertex vert node:obj) + objpos
 newnormangle = normalize (vertpos - splayfrom)
 tempbitarray = #{vert}
 norms = #{}
 enMod.ConvertVertexSelection tempbitarray norms
 enMod.MakeExplicit selection:norms
 for norm in norms do 
 (  
  existingnormangle = normalize (enMod.GetNormal (norm))
  finalangle = normalize ( newnormangle * Strength + existingnormangle * (1 - Strength))
  enMod.SetNormal norm finalangle
 )
)
  

Wednesday 27 March 2013

Fix Meshes

Lots of users experience issues with their meshes. This script provides a convenient way to do all of the common mesh-fixing steps.

USAGE
  • Select one or more geometry objects
  • run script
  • choose from any of the below buttons
    • Reset Xform - Resets selected objects XForms
    • Reset Normals - applies an edit normals modifier and resets all of the normals (so that they are again controlled by smoothing groups)
    • Reset Object - creates a new box object and attaches the existing mesh to it, then deletes the temporary box.. This essentially wipes all object properties. material, wire colour and pivot are all retained.
    • New Scene - exports the selected geometry, creates a new scene and imports it again. deletes the FBX file when done.
    
-- Mixescript snippet: Fix Objects
-- attempts to correct common problems with meshes

-- USAGE
-- select the bad meshes
-- run script
-- choose any of the buttons. I recommend working top to bottom
 
rollout rol_FixObject "Fix Object" width:104 height:168
(
 button btn_ResetXForm "Reset XForm" pos:[8,40] width:88 height:24
 button btn_ResetNormals "Reset Normals" pos:[8,72] width:88 height:24
 button btn_ResetObject "Reset Object" pos:[8,104] width:88 height:24
 button btn_NewScene "New Scene" pos:[8,136] width:88 height:24
 label lbl1 "Warning: will collapse stack" pos:[16,8] width:72 height:32
 
 on btn_ResetXForm pressed do
 (
  undo on 
  (
   objarray = getcurrentselection()
   for obj in objarray do 
   (
    ResetXform obj
    ConvertTo obj Editable_poly
   )
   select objarray
  )
 )
 
 on btn_ResetObject pressed do
 (
  undo on 
  (
   max modify mode
   objarray = getcurrentselection()
   newsel = #{}
   for i = 1 to objarray.count do 
   (
    obj = objarray[i]
    fixedobj = Box name:obj.name lengthsegs:1 widthsegs:1 heightsegs:1 length:1 width:1 height:1 mapcoords:on pos:[0,0,0] isSelected:off
    fixedobj.pivot = obj.position
    fixedobj.wirecolor = obj.wirecolor
    fixedobj.mat = obj.mat
    ConvertTo fixedobj Editable_poly
    polyop.attach fixedobj obj
    polyop.deleteverts fixedobj #{1..8} --delete original box
    objarray[i] = fixedobj
   )
   
   select objarray
  )
 )
 
 on btn_ResetNormals pressed do 
 (
  undo on 
  (
   max modify mode 
   objarray = getcurrentselection()
   for obj in objarray do 
   (
    ConvertTo obj Editable_poly
    select obj
    modPanel.setCurrentObject obj
    polyop.setVertSelection obj #all
    allverts = polyop.getVertSelection obj
    allnormals = #{}
    enMod = Edit_Normals ()
    modPanel.addModToSelection (enMod) ui:on
    enMod.ConvertVertexSelection allverts allnormals
    enMod.Reset selection:allnormals
    ConvertTo obj Editable_poly
   )
   select objarray
  )
 )
 
 
 on btn_NewScene pressed do 
 (
  --checks
  cancel = false
  if selection.count == 0 then (messagebox ("please select one or more objects"); cancel = true)
  if maxfilepath == "" then (messagebox ("please save your scene first."); cancel = true)
  
  if cancel == false then (
   ContinueScript = queryBox "This will create a new scene and only copy across geometry. Unsaved changes will be lost. Continue?"
   if ContinueScript then
   (
    exportpath = maxfilepath + maxfilename + ".FBX"
    exportFile exportpath #noPrompt selectedOnly:true
    actionMan.executeAction 0 "16" --new scene
    --actionMan.executeAction 0 "40005" -- reset scene -switch this out for above line if you want to keep materials etc. this is essentially less aggressive.
    importFile exportpath #noPrompt
    deleteFile exportpath
   )
  )
 )
 
)

CreateDialog rol_FixObject NewDlg
  

Monday 25 March 2013

Snap to nearest gridpoint

By request, a script to move selected vertices to the nearest gridpoint. I also threw in snapping selected objects to nearest gridpoint.




Usage
  • Select verts of an editable poly or select objects
  • run script
  • verts/objects will be at nearest gridpoints. (note: uses objects pivot)
  
-- MixeScript snippet: Snap to Nearest Grid Point
-- Snaps selected verts or objects to nearest grid point

--USAGE
-- Select verts of an editable poly or select objects
-- run script
-- choose button to snap to
-- verts/objects will be at nearest gridpoints. (note: uses objects pivot)

fn findnearestgridpoint frompoint axis = (
 spacing = getGridSpacing()
 
 nearestx = frompoint.x
 nearesty = frompoint.y
 nearestz = frompoint.z
 
 nearestx = (floor (frompoint.x / spacing + 0.5)) * spacing
 nearesty = (floor (frompoint.y / spacing + 0.5)) * spacing
 nearestz = (floor (frompoint.z / spacing + 0.5)) * spacing
 
 if axis == "x" then ( nearesty = frompoint.y; nearestz = frompoint.z)
 if axis == "y" then ( nearestx = frompoint.x; nearestz = frompoint.z)
 if axis == "z" then ( nearesty = frompoint.y; nearestx = frompoint.x)
  
 return [nearestx,nearesty,nearestz]
)

fn DoSnap axis = 
(
 if subobjectlevel == 1 then 
 ( 
   obj = $
   vertselection = polyop.getVertSelection obj
  max modify mode
  for vert in vertselection do (
   polyop.setVert obj vert (findnearestgridpoint (polyop.getVert obj vert) axis)
  )
 ) else (
  objarray = getcurrentselection()
  for obj in objarray do (
   obj.pos = findnearestgridpoint obj.pos axis
  )
 )
)

rollout rol_SnapToGrid "Snap To Grid Points" width:136 height:48
(
 button snapX "X" pos:[8,24] width:24 height:19
 label lbl1 "Snap to Grid Points" pos:[24,5] width:96 height:16
 button snapY "Y" pos:[40,24] width:24 height:19
 button snapZ "Z" pos:[72,24] width:24 height:19
 button SnapAll "All" pos:[104,24] width:24 height:19
 
 on snapX pressed do (undo on (DoSnap "x"))
 on snapY pressed do (undo on (DoSnap "y"))
 on snapZ pressed do (undo on (DoSnap "z"))
 on SnapAll pressed do (undo on (DoSnap "all"))
)

createDialog rol_SnapToGrid dlg_SnapToGrid
  

Sunday 24 March 2013

MixeScript Roundup 1

Thought I'd show in video what the scripts posted up to now do. Also I've whacked them all in a zip if you want to use them as buttons in your interface.







Colour Objects (by random/by position)

I needed a script that would give a random colour to my leaves and also give a colour to them based on height so that I could use this to make the leaves at the back disappear first in autumn.

For the sake of functionality, I gave it three purposes:

Usage:
  • select many objects
  • adjust settings between height by colour / random colour / random greyscale
  • run script
Update:
  • I needed this to give an even distribution of colours, so introduced a new method which sorts based on position, then assigns a colour based on sort order. slightly long-winded code, sorry!
  
  
-- MixeScript snippet: Colour Objects (random/by pos)
-- Removes material from objects and applies a random colour, or colour based on their position.

--USAGE
-- select many objects
-- adjust settings between height by colour / random colour / random greyscale
-- run script

--SETTINGS
useposcolour = true --if false, uses a random colour.
greyscale = false -- if true then r=g=b (grey tones)
sortbasedcolouring = false -- if true, sorts objects then colours based on sort order. ensures even distribution of colours and no matching colours.
 
 
if sortbasedcolouring == false then
(
 minx = $.min.x
 maxx = $.max.x
 xdifference = maxx - minx
 miny = $.min.y
 maxy = $.max.y
 ydifference = maxy - miny
 minz = $.min.z
 maxz = $.max.z
 zdifference = maxz - minz
 sort 

 for o in $ do (
  o.mat = undefined
  
  xcolour = (o.center.x - minx) / xdifference * 255
  ycolour = (o.center.y - miny) / ydifference * 255
  zcolour = (o.center.z - minz) / zdifference * 255 
  
  if useposcolour then (
   r = zcolour ;  g = zcolour ;  b = zcolour
  ) else (
   if greyscale then (
    r = random 0 255 ;  g = r ;  b = r
   ) else (
    r = random 0 255 ;  g = random 0 255 ;  b = random 0 255
   )
  )
  
  o.wirecolor = [r,g,b]
 )
)
else 
(
 
 mysel = getcurrentselection()
  
 fn CompareX newobj prevobj =
 ( 
  case of
  (
   (newobj.center.x >  prevobj.center.x): 1
   (newobj.center.x <  prevobj.center.x): -1
   default:0
  )
 )
  
 fn CompareY newobj prevobj =
 ( 
  case of
  (
   (newobj.center.y >  prevobj.center.y): 1
   (newobj.center.y <  prevobj.center.y): -1
   default:0
  )
 )

 fn CompareZ newobj prevobj =
 ( 
  case of
  (
   (newobj.center.z >  prevobj.center.z): 1
   (newobj.center.z <  prevobj.center.z): -1
   default:0
  )
 )

 selsize = mysel.count
 --print ("SELSIZE = " + (selsize as string))

 qsort mysel CompareX
 for i = 1 to selsize do ( 
  newcolour = (((i-1.0) / (selsize-1.0) * 255) as string)
  mysel[i].wirecolor = [(newcolour as integer),mysel[i].wirecolor.g,mysel[i].wirecolor.b]
  --print ("XVALUE: i = " + (i as string) + " , colour = " + (mysel[i].wirecolor as string))
 )

 qsort mysel CompareY
 for i = 1 to selsize do (
  newcolour = (((i-1.0) / (selsize-1.0) * 255) as string)
  mysel[i].wirecolor = [mysel[i].wirecolor.r,(newcolour as integer),mysel[i].wirecolor.b]
  --print ("YVALUE: i = " + (i as string) + " , colour = " + (mysel[i].wirecolor as string))
 )

 qsort mysel CompareZ
 for i = 1 to selsize do (
  newcolour = (((i-1.0) / (selsize-1.0) * 255) as string)
  mysel[i].wirecolor = [mysel[i].wirecolor.r,mysel[i].wirecolor.g,(newcolour as integer)]
  --print ("ZVALUE: i = " + (i as string) + " , colour = " + (mysel[i].wirecolor as string))
 )

 --optionally, use this to greyscale.
 --for o in mysel do (o.wirecolor = [o.wirecolor.b,o.wirecolor.b,o.wirecolor.b])
)