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()

)