Voxelize Mesh Script

February 8th, 2010

This script will voxelize an animated mesh. It creates an array of cubes which fills the bounding box of the mesh’s motion through its animated range, and animates the visibility of each cube over the frame range based on its proximity to the mesh.

It’s quite slow, and would be faster if it used my octree, but it’s a start.

Written in Python with PyMEL for Maya.

Code follows:

Python code:

### start: voxelize mesh 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.MScriptUtil().asFloatPtr()
  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 \
    len(selected()) > 1 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()[0]
  ctrl.visibility.set(1)

  frames = keyframe(ctrl, time=(0,(cmds.findKeyframe(which='last'))), query=1)
  firstFrame = int(min(frames))
  lastFrame = int(max(frames))
  duration = int(lastFrame-firstFrame)
  
  makeProgBar(lastFrame-firstFrame)
  cmds.progressBar(gMainProgressBar, edit=True, beginProgress=True)

  bb = ctrl.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):
    if cmds.progressBar(gMainProgressBar, query=1, isCancelled=1 ):
      break
    currentTime(f)
    cmds.progressBar(gMainProgressBar, edit=1, step=1)

    bb = ctrl.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
  ctrlLoc = dt.Vector(ctrl.t.get())
  for f in range(firstFrame,lastFrame): # for each frame
    for x in cubes: # for each cube
      if cmds.progressBar(gMainProgressBar, query=1, isCancelled=1 ):
        break
      currentTime(f)
      cmds.progressBar(gMainProgressBar, edit=1, step=1)
      # get the vector from x to ctrl
      xLoc = x.t.get()
      ctrlVec = dt.Vector(xLoc-ctrlLoc).normal()
      sc = rayIntersect(ctrl, xLoc, ctrlVec)
      setKeyframe(x, attribute = "scale", v = sc, t = f)

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

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

### end voxelize mesh v.1
« previously: Pymel OOOctree | Home | next: Pivot »

Leave a Reply