Voxelize Meshes Script

February 10th, 2010

This script will voxelize selected meshes over the frame range of the timeline.

It is currently rather slow.

Pymel code:

### start: voxelize meshes v.1

from pymel import *
from math import fmod
import pymel.core.datatypes as dt
import maya.OpenMaya as om
import random, time

global cubeCount
cubeCount = 0

# A simple wrapper for the maya api function allIntersections to test if point lies inside of mesh - returns true or false
#
# point is the position of interest in world space
# direction is the ray direction to test in
# mesh is the geometry to test against

def rayIntersect(mesh, point, direction=(0.0, 1.0, 0.0)):
  om.MGlobal.selectByName(mesh)
  sList = om.MSelectionList()
  #Assign current selection to the selection list object
  om.MGlobal.getActiveSelectionList(sList)

  item = om.MDagPath()
  sList.getDagPath(0, item)
  item.extendToShape()

  fnMesh = om.MFnMesh(item)

  raySource = om.MFloatPoint(point[0], point[1], point[2], 1.0)
  rayDir = om.MFloatVector(direction[0], direction[1], direction[2])
  faceIds = None
  triIds = None
  idsSorted = False
  testBothDirections = False
  worldSpace = om.MSpace.kWorld
  maxParam = 999999
  accelParams = None
  sortHits = True
  hitPoints = om.MFloatPointArray()
  hitRayParams = om.MFloatArray()
  hitFaces = om.MIntArray()
  hitTris = None
  hitBarys1 = None
  hitBarys2 = None
  tolerance = 0.0001
  hit = fnMesh.allIntersections(raySource, rayDir, faceIds, triIds, idsSorted, worldSpace, maxParam, testBothDirections, accelParams, sortHits, hitPoints, hitRayParams, hitFaces, hitTris, hitBarys1, hitBarys2, tolerance)

  result = int(fmod(len(hitFaces), 2))

  #clear selection as may cause problem if the function is called multiple times in succession
  om.MGlobal.clearSelectionList()
  return result

# progress bar, enabling "Esc"
def makeProgBar(length):
  global gMainProgressBar
  gMainProgressBar = mel.eval('$tmp = $gMainProgressBar');
  cmds.progressBar( gMainProgressBar,
        edit=True,
        beginProgress=True,
        isInterruptable=True,
        maxValue=length
        )
        
def promptNumber():
  result = cmds.promptDialog(
      title='Grow Shrub',
      message='Block size:',
      text="1",
      button=['OK', 'Cancel'],
      defaultButton='OK',
      cancelButton='Cancel',
      dismissString='Cancel')

  if result == 'OK':
    return float(cmds.promptDialog(query=True, text=True))
  else: return 0

def doit1(cubeSize):
  global cubeCount
  # get the bounding box of the ctrl
  xdist = abs(xmin)+abs(xmax)
  ydist = abs(ymin)+abs(ymax)
  zdist = abs(zmin)+abs(zmax)
  cubeCount = xdist * ydist * zdist / cubeSize**3
  print "Generating", int(cubeCount), "cubes..."
  print "Press ESC to cancel"

  makeProgBar(cubeCount)
  fac = 1/cubeSize
  
  # make an array of cubes to encompass the bounding box
  for x in range(xmin*fac, xmax*fac+1):
    for y in range(ymin*fac, ymax*fac+1):
      for z in range(zmin*fac, zmax*fac+1):
        stepint = 1
        if cmds.progressBar(gMainProgressBar, query=True, isCancelled=True ):
          break
          return 0
        cmds.progressBar(gMainProgressBar, edit=True, step=1)
        # create and place a cube
        cube = polyCube(sz=1, sy=1, sx=1, cuv=4, d=cubeSize, h=cubeSize, w=cubeSize, ch=1)[0]
        xform(t=(x*cubeSize, y*cubeSize, z*cubeSize))
        cubes.append(cube)

# set a control object
if len(selected()) == 0 or \
    objectType(selected()[0].getShape()) != "mesh":
  result = cmds.confirmDialog(
      title='Mesh selection',
      message='Please select a mesh.',
      button=['OK'])
else:
  startTime=timerX()
  cubeSize = promptNumber()

  ctrl = selected()
  for c in ctrl:
    c.visibility.set(1)

  firstFrame = int(playbackOptions(query=1, min=1))
  lastFrame = int(playbackOptions(query=1, max=1))
  duration = int(lastFrame-firstFrame)
  
  print "duration*len(ctrl):", duration*len(ctrl)
  makeProgBar(duration*len(ctrl))
  cmds.progressBar(gMainProgressBar, edit=True, beginProgress=1)

  bb = ctrl[0].getBoundingBox()
  xmin = bb[0][0]
  xmax = bb[1][0]
  xdist = abs(xmin)+abs(xmax)
  ymin = bb[0][1]
  ymax = bb[1][1]
  ydist = abs(ymin)+abs(ymax)
  zmin = bb[0][2]
  zmax = bb[1][2]
  
  print "Finding bounding box of animation..."
  print "Press ESC to cancel"
  # find outer boundaries of animation
  for f in range(firstFrame,lastFrame):
    for c in ctrl:
      if cmds.progressBar(gMainProgressBar, query=1, isCancelled=1 ):
        break
      currentTime(f)
      cmds.progressBar(gMainProgressBar, edit=1, step=1)

      bb = c.getBoundingBox()
      xmin = min(xmin, bb[0][0])
      xmax = max(xmax, bb[1][0])
      ymin = min(ymin, bb[0][1])
      ymax = max(ymax, bb[1][1])
      zmin = min(zmin, bb[0][2])
      zmax = max(zmax, bb[1][2])

  cmds.progressBar(gMainProgressBar, edit=1, endProgress=1)

  # make cubes array
  cubes = []

  doit1(cubeSize)

  cmds.progressBar(gMainProgressBar, edit=1, endProgress=1)
  makeProgBar(duration*cubeCount)
  cmds.progressBar(gMainProgressBar, edit=1, beginProgress=1)

  print "Animating visibility over", duration, "frames..."
  print "Press ESC to cancel"

  # animate cube visibility
  for f in range(firstFrame,lastFrame): # for each frame
    currentTime(f, edit=1, update=1)
    for x in cubes: # for each cube
      x.visibility.set(1)
    for x in cubes: # for each cube
      currentTime(f)
      if cmds.progressBar(gMainProgressBar, query=1, isCancelled=1 ):
        break
      cmds.progressBar(gMainProgressBar, edit=1, step=1)
      for c in ctrl:
        ctrlLoc = dt.Vector(c.t.get())
        key = keyframe(x, t=(f,f), at="scale", valueChange=1, query=1)
        if key == [] or key[0] != 1.0:
          setKeyframe(x, attribute = "scale", v = 0, t = f)
          # get the vector from x to ctrl
          xLoc = x.t.get()
          ctrlVec = dt.Vector(xLoc-ctrlLoc).normal()
          sc = rayIntersect(c, xLoc, ctrlVec)
          if sc == 1:
            setKeyframe(x, attribute = "scale", v = sc, t = f)
      currentTime(f, edit=1, update=1)


  cutKey(ctrl, time=(0,(cmds.findKeyframe(which='last'))), attribute='visibility', clear=1, option='keys')
  #c.visibility.set(0)

  cmds.progressBar(gMainProgressBar, edit=1, endProgress=1)
  totalTime = timerX(startTime=startTime)
  print("Total Time: "+str(totalTime))    

### end voxelize meshes v.1
« previously: Pivot | Home | next: Voxelize Meshes Script v.2 »

2 Responses to “Voxelize Meshes Script”

  1. Peter P Says:

    :)
    Love it. I think something like this would benefit from being developed as a maya plug-in. Perhaps faster execution time and maybe even a maya node that you can just plug the geo into, set frame range.
    Great stuff.

  2. zoomy Says:

    I’d love to make it a plug-in, at the moment it requires so much preprocessing it wouldn’t work for something that had to evaluate every frame. There’s another approach I want to try though.

    Will definitely have to be faster.

Leave a Reply