Voxelize Mesh Script
February 8th, 2010This 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 »