Geometry Fracture
Computer code which fractures 3D geometry. Written in Python2 for Autodesk Maya
When run in Maya, this script creates a user interface with controls for creating a variety of breaks and fractures in geometry. The script was written as a learning exercise to explore the voronoi diagram algorithm, and its applications in procedural destruction.
import maya.cmds as cmds
import random
import string
import math
#debug new file
#cmds.file(f=True, new=True)
#before anything else, check for necessary plugin(s)
if (cmds.pluginInfo("nearestPointOnMesh", q=True, loaded=True) == False):
try:
cmds.loadPlugin("nearestPointOnMesh.mll")
except:
cmds.error("Cannot load necessary plugins (nearestPointOnMesh.mll). Script Cannot Continue.")
##############################
## ##
## UI SYNC FUNCTIONS ##
## ##
##############################
##################################
## On selection ##
##################################
def selectionChange():
numVertices = 0
numFaces = 0
numEdges = 0
selectedObjects = getSelectedMeshes()
if len(selectedObjects) == 0:
cmds.text("chipEdge", label="\n# of Edges selected: 0", e=True)
cmds.text("chipVertex", label="\n# of Vertices selected: 0", e=True)
cmds.text("chipFaces", label="\n# of Faces selected: 0", e=True)
for obj in selectedObjects:
ObjType = cmds.objectType(obj)
if (ObjType == "mesh"):
#get nums of current selection
numVertices = cmds.polyEvaluate(selectedObjects[0], v=True)
numFaces = cmds.polyEvaluate(selectedObjects[0], f=True)
numEdges = cmds.polyEvaluate(selectedObjects[0], e=True)
cmds.text("chipEdge", label="\n# of Edges selected: "+str(numEdges), e=True)
cmds.text("chipVertex", label="\n# of Vertices selected: "+str(numVertices), e=True)
cmds.text("chipFaces", label="\n# of Faces selected: "+str(numFaces), e=True)
else:
cmds.text("chipEdge", label="\n# of Edges selected: 0", e=True)
cmds.text("chipVertex", label="\n# of Vertices selected: 0", e=True)
cmds.text("chipFaces", label="\n# of Faces selected: 0", e=True)
##################################
## Populate Node List ##
##################################
def findDestNodes():
#remove old nodes
destNodes = cmds.optionMenu("destNodeSelect", q=True, ill=True)
for node in destNodes:
cmds.deleteUI(node)
destNodes = []
destNodes.append( cmds.menuItem(label="None", parent="destNodeSelect") )
nodes = cmds.ls()
for node in nodes:
if cmds.objExists(node + ".destType"):
destNodes.append( cmds.menuItem(label=node, parent="destNodeSelect") )
##################################
## Selecte Node ##
##################################
def selectDestNode( nodeName ):
if(nodeName == "None"):
return
else:
populateUI( nodeName )
##################################
## Select Chip Type ##
##################################
def chipOnChecked(checked):
checked1 = cmds.checkBoxGrp("chipOn", q=True, v1=True)
checked2 = cmds.checkBoxGrp("chipOn", q=True, v2=True)
checked3 = cmds.checkBoxGrp("chipOn", q=True, v3=True)
if checked1 == True:
cmds.columnLayout("Vertices", e=True, en=True)
else:
cmds.columnLayout("Vertices", e=True, en=False)
if checked2 == True:
cmds.columnLayout("Edges", e=True, en=True)
else:
cmds.columnLayout("Edges", e=True, en=False)
if checked3 == True:
cmds.columnLayout("Faces", e=True, en=True)
else:
cmds.columnLayout("Faces", e=True, en=False)
##################################
## Select Break Type ##
##################################
def changeBreakPieces(selected):
selected = cmds.radioButtonGrp("breakPieces", q=True, sl=True)
if(selected == 2):
cmds.rowLayout("breakPlane", edit=True, visible=False)
else:
cmds.rowLayout("breakPlane", edit=True, visible=True)
##################################
## Change Destruction Type ##
##################################
def changeDefType(sel):
sel = cmds.radioButtonGrp("defType", q=True, sl=True)
cmds.columnLayout("Fracture", e=True, visible=False)
cmds.columnLayout("Chip", e=True, visible=False)
cmds.columnLayout("Break", e=True, visible=False)
if(sel == 1):
cmds.columnLayout("Fracture", e=True, visible=True)
elif(sel == 2):
cmds.columnLayout("Chip", e=True, visible=True)
elif(sel == 3):
cmds.columnLayout("Break", e=True, visible=True)
##################################
## Change Attributes Function ##
##################################
def changeAttributes( meshType ):
options = ["Concrete", "Glass", "Plastic", "Styrofoam", "Stone", "Random" ]
presetAttr = [ [ 0.7, 1, 1 ], [1, 3, 1], [0.4, 5, 0.2], [0.1, 0.5, 0.1], [1, 5, 0.9], [random.randrange(0,10), random.randrange(0,10), random.randrange(0,10), random.randrange(0,10), random.randrange(0,10)] ]
for option in options:
if option == meshType:
optionNum = options.index(option)
#print presetAttr[optionNum]
cmds.floatSliderGrp("matDensity", e=True, v=presetAttr[optionNum][0] )
cmds.floatSliderGrp("matGrainSize", e=True, v=presetAttr[optionNum][1] )
cmds.floatSliderGrp("matScatterSize", e=True, v=presetAttr[optionNum][2] )
##############################
## ##
## UI CREATION ##
## ##
##############################
if(cmds.window("meshDes", exists=True)):
cmds.deleteUI("meshDes", window=True)
win = cmds.window("meshDes", title="Mesh Destruction", menuBar=True, widthHeight=(483, 600))
cmds.columnLayout(rs=15)
###############################
## Presets ##
###############################
cmds.optionMenu(ni=6, label="Preset Options", cc=changeAttributes)
cmds.menuItem(label="Concrete")
cmds.menuItem(label="Glass")
cmds.menuItem(label="Plastic")
cmds.menuItem(label="Styrofoam")
cmds.menuItem(label="Stone")
cmds.menuItem(label="Random")
###############################
## Destruction History ##
###############################
cmds.optionMenu("destNodeSelect", label="Destruction Nodes:", cc=selectDestNode)
cmds.menuItem(label="None")
###############################
## Mat Attributes ##
###############################
cmds.frameLayout("Material Attributes (Advanced)", width=475, collapsable=True, cl=False)
cmds.floatSliderGrp("matDensity", label="Density", field=True, min=0.01, max=1, fmn=0.01, fmx=1)
cmds.columnLayout();
cmds.text("Granularity:", font="boldLabelFont")
cmds.floatSliderGrp("matGrainSize", label="Grain Size", field=True, min=0.01, max=5)
cmds.floatSliderGrp("matScatterSize", label="Scatter Size", field=True, min=0, max=1, fmn=0.01, fmx=1)
cmds.setParent("..")
cmds.setParent("..")
###############################
## Deformations ##
###############################
cmds.frameLayout(label="Deformation")
cmds.radioButtonGrp("defType", label="Type", nrb=3, la3=["Fracture", "Chip", "Break"], sl=1, cc=changeDefType)
cmds.text("Attributes:", font="boldLabelFont")
## FRACTURE
cmds.columnLayout("Fracture", width=475)
cmds.floatSliderGrp("fractureForce", label="Magnitude of force:", min=0, max=1, value=0.5, field=True)
cmds.rowLayout("fractureVector", nc=2)
cmds.button(l="Create Vector Control", c="createControlVector()")
cmds.textField("fractureVectorObject", text="--create vector--")
cmds.setParent("..")
cmds.setParent("..")
##CHIP
cmds.columnLayout("Chip", width=475, visible=False)
cmds.checkBoxGrp("chipOn", label="Chip mesh on: ", labelArray3=["Vertices", "Edges", "Faces"], ncb=3, cc=chipOnChecked)
cmds.columnLayout("Vertices", width=475, en=False)
cmds.text("chipVertex", label="\n# of Vertices selected: 0", font="boldLabelFont")
cmds.intSliderGrp("vertexPercentage", label="% of vertices to chip:", min=1, max=100, field=True)
cmds.setParent("..")
cmds.columnLayout("Edges", width=475, en=False)
cmds.text("chipEdge", label="\n# of Edges selected: 0", font="boldLabelFont")
cmds.intSliderGrp("edgePercentage", label="% of edges to chip:", min=1, max=100, field=True)
cmds.setParent("..")
cmds.columnLayout("Faces", width=475, en=False)
cmds.text("chipFaces", label="\n# of Faces selected: 0", font="boldLabelFont")
cmds.intSliderGrp("facePercentage", label="% of faces to chip:", min=1, max=100, field=True)
cmds.setParent("..")
cmds.setParent("..")
##BREAK
cmds.columnLayout("Break", width=475, visible=False)
cmds.radioButtonGrp("breakPieces", label="Pieces Amount: ", labelArray2=["Interactive Split", "Crumble"], nrb=2, sl=2, cc=changeBreakPieces)
cmds.rowLayout("breakPlane", nc=2, visible=False)
cmds.button(l="Create Plane Control", c="createControlPlane()")
cmds.textField("breakPlaneObject", text="--create plane--")
cmds.setParent("..")
cmds.setParent("..")
cmds.button(label="DEFORM", c=("performDeform()"))
cmds.setParent("..")
cmds.setParent("..")
cmds.showWindow(win)
#debug create default shape
changeAttributes( "Concrete" )
#cmds.polyTorus(r=4, sr=1)
#cmds.polyCube(w=5, h=5, d=5)
#populate ui
cmds.scriptJob(event=["SelectionChanged", selectionChange])
findDestNodes()
##############################
## ##
## FRACTURE FUNCTIONS ##
## ##
##############################
##############################
## Main Function ##
##############################
def performDeform():
#open undo Chunk
cmds.undoInfo( ock=True )
#cmds.undoInfo( swf=False )
#get selected meshes
objs = getSelectedMeshes();
if len(objs) == 0:
cmds.warning("Please select a mesh before performing deformation")
return
#progress window
#remove old window to avoid errors
if(cmds.window("destProgress", exists=True)):
cmds.deleteUI("destProgress", window=True)
win = cmds.window("destProgress", title="Destruction Progress", width=350, h=50 )
cmds.columnLayout(w=350)
cmds.text("destProgressText", font="boldLabelFont", label="Getting Ready", rs=False, w=350, align="left")
cmds.progressBar("destProgressBar", min=0, max=1000, w=350, ii=True)
cmds.setParent("..")
cmds.showWindow(win)
#collect variables
type = cmds.radioButtonGrp("defType", q=True, select=True)
properties = []
properties.append( cmds.floatSliderGrp("matDensity", q=True, v=True) )
properties.append( cmds.floatSliderGrp("matGrainSize", q=True, v=True) )
properties.append( cmds.floatSliderGrp("matScatterSize", q=True, v=True) )
vectorPlane = []
vectorPlane.append( cmds.textField("fractureVectorObject", q=True, text=True) )
vectorPlane.append( cmds.textField("breakPlaneObject", q=True, text=True) )
#do fracture
pieces = []
name = "unknown"
if(type == 1):
name = "fractureResult"
pieces = doFracture(objs, properties, vectorPlane)
#do chip
elif(type == 2):
name = "chipResult"
pieces = doChip(objs, properties)
#do break
elif(type == 3):
name = "breakResult"
breakType = cmds.radioButtonGrp("breakPieces", q=True, select=True)
pieces = doBreak(objs, breakType, properties, vectorPlane)
#if successfull create node to store history
if( len(pieces) > 0 ):
destNode = createDestructionNode(objs[0], properties)
#add controls to node if they exist
for ctrl in vectorPlane:
if cmds.objExists(ctrl):
cmds.parent(ctrl, destNode)
#put pieces under destruction node and rename them
for piece in pieces:
cmds.parent(piece, destNode)
cmds.rename(piece, name)
#repopulate ui
findDestNodes()
cmds.select(destNode)
else:
cmds.select(objs[0])
#clean up
#close undo Chunk
cmds.undoInfo( cck=True )
#cmds.undoInfo( swf=True )
if(cmds.window("destProgress", exists=True)):
cmds.deleteUI("destProgress", window=True)
##############################
## Fracture ##
##############################
def doFracture(mesh, matProperties, vectorPlane):
force = cmds.floatSliderGrp("fractureForce", q=True, v=True)
#IMPACT
#check to see vector control exists
if(cmds.objExists(vectorPlane[0]) == False):
if vectorPlane[0] == "--create vector--":
cmds.warning("Please define a control vector")
else:
cmds.warning("The specified vector control does not exist")
cmds.textField("fractureVectorObject", edit=True, bgc=[0.86, 0.81, 0.53] )
return []
#get transformation matrix
matrix = cmds.xform(vectorPlane[0], q=True, ws=True, matrix=True)
#get line from vector by multiplying usual default points through matrix
#first point is just translation
pointA = [matrix[12], matrix[13], matrix[14]]
#second is just [1, 0, 0]
pointB = [ matrix[0] + matrix[12], matrix[1] + matrix[13], matrix[2] + matrix[14] ]
#find impact point
#find all intesecting faces
impact = None
lowestT = None
faceEqn = None
faceVerts = None
numFaces = cmds.polyEvaluate(mesh[0], f=True)
cmds.text("destProgressText", e=True, label="Fracture: Find Collision")
cmds.progressBar("destProgressBar", e=True, pr=0, max=numFaces)
for i in range(numFaces):
face = cmds.select(mesh[0] + ".f[" + str(i) + "]")
vertNums = cmds.polyInfo(faceToVertex=True)
vertNums = string.split(vertNums[0], ":")[1]
vertNums = string.split(vertNums, "Hard")[0]
vertNums = string.split(vertNums)
#make plane equation from vertices
verts = []
for num in vertNums:
verts.append( cmds.xform(mesh[0] + ".vtx[" + num + "]", q=True, ws=True, translation=True) )
planeEqn = getPlaneEquation(verts)
#find intesection point
t = getLinePlaneIntersect(planeEqn, pointA, pointB)
if t is None:
continue
intersection = [ pointA[0] + t * (pointB[0] - pointA[0]), pointA[1] + t * (pointB[1] - pointA[1]), pointA[2] + t * (pointB[2] - pointA[2]) ]
#check if its on the actual mesh
if isPointOnMesh( mesh[0], intersection ):
#keep the one farthest back on the line
if( lowestT is None or t < lowestT ):
impact = intersection
lowestT = t
faceEqn = planeEqn
faceVerts = verts
cmds.progressBar("destProgressBar", e=True, step=1)
if lowestT is None:
cmds.warning("control vector does not intersect the given mesh")
return []
#calc radius by force
radius = force / matProperties[0]
if matProperties[0] > radius:
cmds.warning("Your material is too dense, or force too weak to cause destruction")
return []
#to get transform matrix, need three normal vectors for axis
matrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
#y axis is in plane equation
matrix[4] = faceEqn[0]
matrix[5] = faceEqn[1]
matrix[6] = faceEqn[2]
#z can be any edge vector
zVec = [faceVerts[0][0] - faceVerts[1][0], faceVerts[0][1] - faceVerts[1][1], faceVerts[0][2] - faceVerts[1][2]]
mag = getMag(zVec)
zVec = [ zVec[0] / mag, zVec[1] / mag, zVec[2] / mag ]
matrix[8] = zVec[0]
matrix[9] = zVec[1]
matrix[10] = zVec[2]
#x is the cross product of Y and z vec
xVec = getCrossProduct(faceEqn, zVec)
matrix[0] = xVec[0]
matrix[1] = xVec[1]
matrix[2] = xVec[2]
#Translation is impact point
matrix[12] = impact[0]
matrix[13] = impact[1]
matrix[14] = impact[2]
#starting from center, spiral out making centers
numPiece = int((radius * 2.0) / matProperties[0]) + 2.0
cmds.text("destProgressText", e=True, label="Fracture: Define Pieces")
cmds.progressBar("destProgressBar", e=True, pr=0, max=radius/matProperties[0] + numPiece * numPiece)
centers = []
surroundingCenters = []
for r in rangef( matProperties[0] * 0.5, radius, matProperties[0]):
cmds.progressBar("destProgressBar", e=True, step=1)
circ = 2 * math.pi * r
step = (matProperties[0] / circ) * 360
for deg in rangef( random.uniform(0, step), 360, step ):
p = [ math.cos(deg) * r, 0, math.sin(deg) * r ]
tp = [ p[0]*matrix[0] + p[1]*matrix[4] + p[2]*matrix[8] + matrix[12], p[0]*matrix[1] + p[1]*matrix[5] + p[2]*matrix[9] + matrix[13], p[0]*matrix[2] + p[1]*matrix[6] + p[2]*matrix[10] + matrix[14]]
centers.append( tp )
surroundingCenters.append( tp )
#create outsides by filling
scatterRange = 0.45 * matProperties[1] * matProperties[2]
for x in rangef( -numPiece/2.0, numPiece/2.0 + 0.1, 1.0):
for z in rangef( -numPiece/2.0, numPiece/2.0 + 0.1, 1.0):
cmds.progressBar("destProgressBar", e=True, step=1)
#point above (no y scatter
X = x * matProperties[0] + random.uniform(-scatterRange, scatterRange)
Z = z * matProperties[0] + random.uniform(-scatterRange, scatterRange)
surroundingCenters.append( [ X*matrix[0] + matProperties[0]*matrix[4] + Z*matrix[8] + matrix[12], X*matrix[1] + matProperties[0]*matrix[5] + Z*matrix[9] + matrix[13], X*matrix[2] + matProperties[0]*matrix[6] + Z*matrix[10] + matrix[14] ] )
#below
X = x * matProperties[0] + random.uniform(-scatterRange, scatterRange)
Z = z * matProperties[0] + random.uniform(-scatterRange, scatterRange)
Y = -matProperties[0] + random.uniform(-scatterRange, scatterRange)
surroundingCenters.append( [ X*matrix[0] + Y*matrix[4] + Z*matrix[8] + matrix[12], X*matrix[1] + Y*matrix[5] + Z*matrix[9] + matrix[13], X*matrix[2] + Y*matrix[6] + Z*matrix[10] + matrix[14] ] )
# in center if at edge
if( x == -numPiece/2 or z == -numPiece/2 or x == numPiece/2 or z == numPiece/2 ):
X = x * matProperties[0] + random.uniform(-scatterRange, scatterRange)
Z = z * matProperties[0] + random.uniform(-scatterRange, scatterRange)
Y = random.uniform(-scatterRange, scatterRange)
surroundingCenters.append( [ X*matrix[0] + Y*matrix[4] + Z*matrix[8] + matrix[12], X*matrix[1] + Y*matrix[5] + Z*matrix[9] + matrix[13], X*matrix[2] + Y*matrix[6] + Z*matrix[10] + matrix[14] ] )
pieces = doVoronoi(mesh[0], centers, surroundingCenters )
cmds.text("destProgressText", e=True, label="Fracture: Finalize")
cmds.progressBar("destProgressBar", e=True, pr=0, max=3)
combined = combinePieces(pieces)
cmds.progressBar("destProgressBar", e=True, step=1)
#sutract from original
newMesh = cmds.duplicate(mesh[0])
cmds.progressBar("destProgressBar", e=True, step=1)
cmds.DeleteAllHistory()
newMesh = cmds.polyBoolOp( mesh[0], combined, op=2, ch=False )
cmds.progressBar("destProgressBar", e=True, step=1)
pieces.append(newMesh)
return pieces
##############################
## Chip ##
##############################
def doChip(mesh, matProperties):
allPieces = []
allMeshes = []
#get number of vertices, edges, and faces of selected object
numVertices = cmds.polyEvaluate(mesh, v=True)
numFaces = cmds.polyEvaluate(mesh, f=True)
numEdges = cmds.polyEvaluate(mesh, e=True)
#check what to chip
checked1 = cmds.checkBoxGrp("chipOn", q=True, v1=True)
checked2 = cmds.checkBoxGrp("chipOn", q=True, v2=True)
checked3 = cmds.checkBoxGrp("chipOn", q=True, v3=True)
verticesToChip = 0
edgesToChip = 0
facesToChip = 0
if checked1 == True:
#get number of verts to chip
vPercent = cmds.intSliderGrp("vertexPercentage", q=True, value=True)
verticesToChip = (vPercent*numVertices) / 100
#make sure no divide by zero
if verticesToChip == 0:
verticesToChip = 1
#step for "randomly" choosing what to chip
step = round(numVertices / verticesToChip)
#start vertex so it isn't the same every time
startValue = random.randrange(0, step)
currentVert = startValue
cmds.text("destProgressText", e=True, label="Chip: Creating Vertex Chips")
cmds.progressBar("destProgressBar", e=True, max=verticesToChip, pr=0)
#loop through amount of vertices that are to be chiped
for i in range(verticesToChip):
vertexPos = [] #holds three x,y,z
verticesPosChip = [] #holds vertexPos to pass to voronoi
vertexPos = cmds.xform(str(mesh[0])+".vtx["+str(int(currentVert))+"]", q=True, ws=True, translation=True)
verticesPosChip.append(vertexPos)
scatterRange = 0.45 * matProperties[1] * matProperties[2]
#put start and end values for x, y, z in two lists
start = []
end = []
for coord in vertexPos:
startVal = coord - matProperties[1]
endVal = coord + matProperties[1]*1.1
start.append(startVal)
end.append(endVal)
vertexSeeds = []
#create locator box around vertex to chip
for x in rangef( start[0], end[0], matProperties[1] ):
for y in rangef( start[1], end[1], matProperties[1] ):
for z in rangef( start[2], end[2], matProperties[1] ):
if x==0 and y==0 and z==0:
continue
if(scatterRange > 0):
p = [x + random.uniform(-scatterRange, scatterRange), y + random.uniform(-scatterRange, scatterRange), z + random.uniform(-scatterRange, scatterRange) ]
else:
p = [x, y, z]
vertexSeeds.append(p)
# cmds.spaceLocator(p=p)
if(cmds.progressBar("destProgressBar", q=True, isCancelled=True)):
return
else:
cmds.progressBar("destProgressBar", e=True, step=1)
currentVert += step
if len(vertexSeeds) > 1:
pieces = doVoronoi(mesh[0], verticesPosChip, vertexSeeds)
for piece in pieces:
allPieces.append(piece)
else:
cmds.warning("not enough seeds, try a larger grain size")
if checked2 == True:
#get number of edges to chip
ePercent = cmds.intSliderGrp("edgePercentage", q=True, value=True)
edgesToChip = (ePercent*numEdges) / 100
#make sure no divide by zero
if edgesToChip == 0:
edgesToChip = 1
#step for "randomly" choosing what to chip
step = round(numEdges / edgesToChip)
#start edge so it isn't the same every time
startValue = random.randrange(0, step)
currentEdge = startValue
cmds.text("destProgressText", e=True, label="Chip: Creating Edge Chips")
cmds.progressBar("destProgressBar", e=True, max=edgesToChip, pr=0)
#loop through amount of vertices that are to be chiped
for i in range(edgesToChip):
vPos = [] #holds edge's vertices
point = [] #holds point on edge to chip
edgesPosChip = [] #holds point to pass to voronoi
edge = cmds.select(mesh[0] + ".e[" + str(int(currentEdge)) + "]")
vertNums = cmds.polyInfo(edgeToVertex=True)
vertNums = string.split(vertNums[0], ":")[1]
vertNums = string.split(vertNums, "Hard")[0]
vertNums = string.split(vertNums)
for j in vertNums:
vPos.append( cmds.xform(mesh[0] + ".vtx[" + j + "]", q=True, ws=True, translation=True) )
#get random point on edge using line equation and edge's vertices
t = random.uniform(0.35, 0.55)
for i in range(3):
p = vPos[0][i] + t*(vPos[1][i] - vPos[0][i])
point.append(p)
edgesPosChip.append(point)
scatterRange = 0.45 * matProperties[1] * matProperties[2]
#put start and end values for x, y, z in two lists
start = []
end = []
for coord in point:
startVal = coord - matProperties[1]
endVal = coord + matProperties[1]*1.1
start.append(startVal)
end.append(endVal)
edgeSeeds = []
#create locator box around edge point to chip
for x in rangef( start[0], end[0], matProperties[1] ):
for y in rangef( start[1], end[1], matProperties[1] ):
for z in rangef( start[2], end[2], matProperties[1] ):
if x==0 and y==0 and z==0:
continue
if(scatterRange > 0):
p = [x + random.uniform(-scatterRange, scatterRange), y + random.uniform(-scatterRange, scatterRange), z + random.uniform(-scatterRange, scatterRange) ]
else:
p = [x, y, z]
edgeSeeds.append(p)
#cmds.spaceLocator(p=p)
if(cmds.progressBar("destProgressBar", q=True, isCancelled=True)):
return
else:
cmds.progressBar("destProgressBar", e=True, step=1)
currentEdge += step
if len(edgeSeeds) > 1:
pieces = doVoronoi(mesh[0], edgesPosChip, edgeSeeds)
for piece in pieces:
allPieces.append(piece)
else:
cmds.warning("not enough seeds, try a larger grain size")
if checked3 == True:
#get number of faces to chip
fPercent = cmds.intSliderGrp("facePercentage", q=True, value=True)
facesToChip = (fPercent*numFaces) / 100
#make sure no divide by zero
if facesToChip == 0:
facesToChip = 1
#step for "randomly" choosing what to chip
step = round(numFaces / facesToChip)
#start vertex so it isn't the same every time
startValue = random.randrange(0, step)
currentFace = startValue
cmds.text("destProgressText", e=True, label="Chip: Creating Edge Chips")
cmds.progressBar("destProgressBar", e=True, max=facesToChip, pr=0)
#loop through amount of vertices that are to be chiped
for i in range(facesToChip):
edgePoints = [] # holds points on edge to average
facePosChip = [] #holds point to pass to voronoi
#get face values in to edges
face = cmds.select(mesh[0] + ".f[" + str(int(currentFace)) + "]")
edgeNum = cmds.polyInfo(faceToEdge=True)
edgeNum = string.split(edgeNum[0], ":")[1]
edgeNum = string.split(edgeNum, "Hard")[0]
edgeNum = string.split(edgeNum)
#loop through edges to get random point on edge to average
for edge in edgeNum:
vPos = [] #holds the vertex positions of face
#get edge values in to vertices
edge = cmds.select(mesh[0] + ".e[" + str(int(edge)) + "]")
vertNum = cmds.polyInfo(edgeToVertex=True)
vertNum = string.split(vertNum[0], ":")[1]
vertNum = string.split(vertNum, "Hard")[0]
vertNum = string.split(vertNum)
#get vertex positions to get random point on edge
for vertex in vertNum:
vPos.append( cmds.xform(mesh[0] + ".vtx[" + vertex + "]", q=True, ws=True, translation=True) )
#get random point on edge using line equation and edge's vertices
t = random.uniform(0.2, 0.8)
point = []
for i in range(3):
p = vPos[0][i] + t*(vPos[1][i] - vPos[0][i])
point.append(p)
#print "point: "+str(point)
edgePoints.append(point)
averagedPoint = []
#average random points and append to averagedPoint to send
for i in range(3):
ap = 0
for j in range(len(edgePoints)):
ap += edgePoints[j][i]
ap /= 4
averagedPoint.append( ap )
facePosChip.append(averagedPoint)
scatterRange = 0.45 * matProperties[1] * matProperties[2]
#put start and end values for x, y, z in two lists
start = []
end = []
for coord in averagedPoint:
startVal = coord - matProperties[1]
endVal = coord + matProperties[1]*1.1
start.append(startVal)
end.append(endVal)
faceSeeds = []
#create locator box around edge point to chip
for x in rangef( start[0], end[0], matProperties[1] ):
for y in rangef( start[1], end[1], matProperties[1] ):
for z in rangef( start[2], end[2], matProperties[1] ):
if x==0 and y==0 and z==0:
continue
if(scatterRange > 0):
p = [x + random.uniform(-scatterRange, scatterRange), y + random.uniform(-scatterRange, scatterRange), z + random.uniform(-scatterRange, scatterRange) ]
else:
p = [x, y, z]
faceSeeds.append(p)
#cmds.spaceLocator(p=p)
if(cmds.progressBar("destProgressBar", q=True, isCancelled=True)):
return
else:
cmds.progressBar("destProgressBar", e=True, step=1)
currentFace += step
if len(faceSeeds) > 1:
pieces = doVoronoi(mesh[0], facePosChip, faceSeeds)
for piece in pieces:
allPieces.append(piece)
else:
cmds.warning("not enough seeds, try a larger grain size")
together = None
for piece in allPieces:
if together == None:
together = [piece]
else:
together = cmds.polyBoolOp(together[0], piece, op=1, ch=False)
temp = cmds.duplicate(together)
newMesh = cmds.duplicate(mesh)
newMesh = cmds.polyBoolOp(newMesh[0], temp, op=2, ch=False)
cmds.select(together)
if(cmds.polyEvaluate(shell=True) > 1):
pieces = cmds.polySeparate(together)
pieces.pop(len(pieces)-1)
else:
pieces = together
pieces.append( newMesh[0] )
return pieces
##############################
## Break ##
##############################
def doBreak(mesh, breakType, matProperties, vectorPlane):
if(breakType == 1):
#check to see vector control exists
if(cmds.objExists(vectorPlane[1]) == False):
if vectorPlane[1] == "--create plane--":
cmds.warning("Please define a plane vector")
else:
cmds.warning("The specified plane control does not exist")
cmds.textField("breakPlaneObject", edit=True, bgc=[0.86, 0.81, 0.53] )
return []
cmds.text("destProgressText", e=True, label="Split: Calculating Seeds")
cmds.progressBar("destProgressBar", e=True, max=2, pr=0)
#get bounding box
bBox = cmds.polyEvaluate(mesh[0], boundingBox=True)
#get plane
#get transformation matrix
matrix = cmds.xform(vectorPlane[1], q=True, ws=True, matrix=True)
#get normal from plane by multiplying usual default points through matrix
#first point is just translation
pointA = [matrix[12], matrix[13], matrix[14]]
#second is just [0, 0, 1]
pointB = [ matrix[8] + matrix[12], matrix[9] + matrix[13], matrix[10] + matrix[14] ]
planeEqn = [ pointB[0] - pointA[0], pointB[1] - pointA[1], pointB[2] - pointA[2] ]
planeEqn = normalize(planeEqn)
#Ax + By + Cz + D = 0
#D = -(Ax + By + Cz)
#get point on plane (point A) and multiply to get D
planeEqn.append( -planeEqn[0] * pointA[0] - planeEqn[1] * pointA[1] - planeEqn[2] * pointA[2] )
#decide on 4 edges
#find where plane is most facing
yDot = max( abs( getDotProduct(planeEqn, [0, 1, 0]) ), abs( getDotProduct(planeEqn, [0, -1, 0]) ) )
xDot = max( abs( getDotProduct(planeEqn, [1, 0, 0]) ), abs( getDotProduct(planeEqn, [-1, 0, 0]) ) )
zDot = max( abs( getDotProduct(planeEqn, [0, 0, 1]) ), abs( getDotProduct(planeEqn, [0, 0, -1]) ) )
cmds.progressBar("destProgressBar", e=True, step=1)
#get intersections
#largest dot shows orientation of plane
#then get intersecting bounding box points
inMesh = False
if yDot >= xDot and yDot >= zDot:
edges = [
[ [bBox[0][0], bBox[1][0], bBox[2][0]], [bBox[0][0], bBox[1][1], bBox[2][0]] ],
[ [bBox[0][0], bBox[1][0], bBox[2][1]], [bBox[0][0], bBox[1][1], bBox[2][1]] ],
[ [bBox[0][1], bBox[1][0], bBox[2][0]], [bBox[0][1], bBox[1][1], bBox[2][0]] ],
[ [bBox[0][1], bBox[1][0], bBox[2][1]], [bBox[0][1], bBox[1][1], bBox[2][1]] ]
]
points = []
for edge in edges:
#calculate y, keep x and z
t = getLinePlaneIntersect(planeEqn, edge[0], edge[1])
if t < 1 and t > 0:
inMesh = True
points.append( [ edge[0][0], edge[0][1] + t * (edge[1][1] - edge[0][1]) , edge[0][2] ] )
elif xDot >= yDot and xDot >= zDot:
edges = [
[ [bBox[0][0], bBox[1][0], bBox[2][0]], [bBox[0][1], bBox[1][0], bBox[2][0]] ],
[ [bBox[0][0], bBox[1][0], bBox[2][1]], [bBox[0][1], bBox[1][0], bBox[2][1]] ],
[ [bBox[0][0], bBox[1][1], bBox[2][0]], [bBox[0][1], bBox[1][1], bBox[2][0]] ],
[ [bBox[0][0], bBox[1][1], bBox[2][1]], [bBox[0][1], bBox[1][1], bBox[2][1]] ]
]
points = []
for edge in edges:
#calculate x, keep y and z
t = getLinePlaneIntersect(planeEqn, edge[0], edge[1])
if t < 1 and t > 0:
inMesh = True
points.append( [ edge[0][0] + t * (edge[1][0] - edge[0][0]), edge[0][1], edge[0][2] ] )
else:
edges = [
[ [bBox[0][0], bBox[1][0], bBox[2][0]], [bBox[0][0], bBox[1][0], bBox[2][1]] ],
[ [bBox[0][0], bBox[1][1], bBox[2][0]], [bBox[0][0], bBox[1][1], bBox[2][1]] ],
[ [bBox[0][1], bBox[1][0], bBox[2][0]], [bBox[0][1], bBox[1][0], bBox[2][1]] ],
[ [bBox[0][1], bBox[1][1], bBox[2][0]], [bBox[0][1], bBox[1][1], bBox[2][1]] ]
]
points = []
for edge in edges:
#calculate z, keep x and y
t = getLinePlaneIntersect(planeEqn, edge[0], edge[1])
if t < 1 and t > 0:
inMesh = True
points.append( [ edge[0][0], edge[0][1], edge[0][2] + t * (edge[1][2] - edge[0][2]) ] )
if(inMesh == False):
cmds.warning("Control Plane does not intersect the selected mesh")
return []
#store two edges fro main interpolation
topBottom = [
[points[0], points[1]],
[points[2], points[3]]
]
cmds.progressBar("destProgressBar", e=True, step=1)
#interpolate across edges, creating rows
centers = []
surroundingCenters = []
scatterRange = 0.45 * matProperties[1] * matProperties[2]
uStep = 1.0 / (getMag(topBottom[0][0], topBottom[0][1]) / matProperties[1])
for u in rangef(0, 1, uStep):
top = [
topBottom[0][0][0] + u * (topBottom[0][1][0] - topBottom[0][0][0]),
topBottom[0][0][1] + u * (topBottom[0][1][1] - topBottom[0][0][1]),
topBottom[0][0][2] + u * (topBottom[0][1][2] - topBottom[0][0][2])
]
bottom = [
topBottom[1][0][0] + u * (topBottom[1][1][0] - topBottom[1][0][0]),
topBottom[1][0][1] + u * (topBottom[1][1][1] - topBottom[1][0][1]),
topBottom[1][0][2] + u * (topBottom[1][1][2] - topBottom[1][0][2])
]
vStep = 1.0 / (getMag(top, bottom) / matProperties[1])
for v in rangef(0, 1, vStep):
posX = top[0] + v * (bottom[0] - top[0])
posY = top[1] + v * (bottom[1] - top[1])
posZ = top[2] + v * (bottom[2] - top[2])
main = [
posX + random.uniform(-scatterRange, scatterRange),
posY + random.uniform(-scatterRange, scatterRange),
posZ + random.uniform(-scatterRange, scatterRange)
]
centers.append( main )
surroundingCenters.append( main )
surroundingCenters.append( [posX + planeEqn[0] * matProperties[1] + random.uniform(-scatterRange, scatterRange), posY + planeEqn[1] * matProperties[1] + random.uniform(-scatterRange, scatterRange), posZ + planeEqn[2] * matProperties[1] + random.uniform(-scatterRange, scatterRange)] )
cmds.progressBar("destProgressBar", e=True, step=1)
#get pieces
pieces = doVoronoi(mesh[0], centers, surroundingCenters, False)
cmds.text("destProgressText", e=True, label="Split: Finalizing")
cmds.progressBar("destProgressBar", e=True, max = 3 + len(pieces), pr=0)
#add all pieces together to get one half
combined = combinePieces( pieces )
cmds.progressBar("destProgressBar", e=True, step=1)
#delete pieces
for piece in pieces:
cmds.delete(piece)
cmds.progressBar("destProgressBar", e=True, step=1)
#bool with mesh
half1 = cmds.duplicate(mesh[0])
half1 = cmds.polyBoolOp(half1, combined, op=2, ch=False)
cmds.progressBar("destProgressBar", e=True, step=1)
#bool to get other half
half2 = cmds.duplicate(mesh[0])
temp = cmds.duplicate(half1[0])
half2 = cmds.polyBoolOp(half2, temp, op=2, ch=False)
cmds.progressBar("destProgressBar", e=True, step=1)
cmds.select(half1, add=True)
return [half1, half2]
elif(breakType == 2):
boundingBox = []
seeds = []
#get bounding box
boundingBox = cmds.polyEvaluate(mesh[0], boundingBox=True)
numPoints = ( boundingBox[0][1] - boundingBox[0][0] ) * ( boundingBox[1][1] - boundingBox[1][0] ) * ( boundingBox[2][1] - boundingBox[2][0] )
cmds.text("destProgressText", e=True, label="Voronoi: Calculating Seeds")
cmds.progressBar("destProgressBar", e=True, max=numPoints, pr=0)
#generate seed zones with noise
#only keep if inside mesh
scatterRange = 0.45 * matProperties[1] * matProperties[2]
for x in rangef( boundingBox[0][0], boundingBox[0][1], matProperties[1] ):
for y in rangef( boundingBox[1][0], boundingBox[1][1], matProperties[1] ):
for z in rangef( boundingBox[2][0], boundingBox[2][1], matProperties[1] ):
if(scatterRange > 0):
p = [x + random.uniform(-scatterRange, scatterRange), y + random.uniform(-scatterRange, scatterRange), z + random.uniform(-scatterRange, scatterRange) ]
else:
p = [x, y, z]
if(isPointInMesh(mesh[0], p)):
seeds.append(p)
# cmds.spaceLocator(p=p)
cmds.progressBar("destProgressBar", e=True, step=1)
if len(seeds) > 1:
return doVoronoi(mesh[0], seeds, seeds)
else:
cmds.warning("not enough seeds, try a smaller grain size")
return []
##############################
## Voronoi ##
##############################
def doVoronoi( mesh, pieceCenters, surroundingCenters, boolMesh=True ):
output = []
#get bounding box
boundingBox = cmds.polyEvaluate(mesh, boundingBox=True)
#get mesh offset from 0 and dimensions
offsetX = boundingBox[0][1] - 0.5 * (boundingBox[0][1] - boundingBox[0][0])
offsetY = boundingBox[1][1] - 0.5 * (boundingBox[1][1] - boundingBox[1][0])
offsetZ = boundingBox[2][1] - 0.5 * (boundingBox[2][1] - boundingBox[2][0])
boundW = boundingBox[0][1] - boundingBox[0][0]
boundH = boundingBox[1][1] - boundingBox[1][0]
boundD = boundingBox[2][1] - boundingBox[2][0]
boundMaxDist = math.sqrt( boundW * boundW + boundH * boundH + boundD * boundD )
#create mesh from bounding box
boundingBoxMesh = cmds.polyCube( w=boundW, h=boundH, d=boundD, sx=1, sy=1, sz=1, ch=False)
cmds.move( offsetX, x=True, a=True)
cmds.move( offsetY, y=True, a=True)
cmds.move( offsetZ, z=True, a=True)
cmds.text("destProgressText", e=True, label="Voronoi: Creating Pieces")
cmds.progressBar("destProgressBar", e=True, pr=0, max=len(pieceCenters))
#go through points left over and find close points
for center in pieceCenters:
#create initial piece
#cube the size of mesh bounding box
piece = cmds.duplicate(boundingBoxMesh[0])
for point in surroundingCenters:
if(point == center):
continue
#get vector to point and to dividing plane
centerToPoint = [point[0] - center[0], point[1]-center[1], point[2]-center[2]]
planeCenter = [center[0] + centerToPoint[0] * 0.5, center[1] + centerToPoint[1] * 0.5, center[2] + centerToPoint[2] * 0.5]
#normalize plane normal vector
mag = getMag(centerToPoint)
planeEqn = [centerToPoint[0]/mag, centerToPoint[1]/mag, centerToPoint[2]/mag]
#get local D for plane equation
#planeEqn.append( -( planeEqn[0] * localPlaneCenter[0] + planeEqn[1] * localPlaneCenter[1] + planeEqn[2] * localPlaneCenter[2]) )
planeEqn.append( -( planeEqn[0] * planeCenter[0] + planeEqn[1] * planeCenter[1] + planeEqn[2] * planeCenter[2]) )
#also add magnitude of vector for halfway point
planeCenterDist = getMag(planeCenter)
#if it intersects with any current edges, use it
useFace = False
numEdges = cmds.polyEvaluate(piece[0], e=True)
#go through edges and check for intersection
for i in range(numEdges):
edge = cmds.select(piece[0] + ".e[" + str(i) + "]")
vertNums = cmds.polyInfo(edgeToVertex=True)
vertNums = string.split(vertNums[0], ":")[1]
vertNums = string.split(vertNums, "Hard")[0]
vertNums = string.split(vertNums)
verts = []
for i in vertNums:
verts.append( cmds.xform(piece[0] + ".vtx[" + i + "]", q=True, ws=True, translation=True) )
t = getLinePlaneIntersect(planeEqn, verts[0], verts[1])
if t >= 0 and t <= 1 :
useFace = True
break
if useFace:
#create cube with proper plane and boolean with shape
temp = cmds.polyPlane(axis=[planeEqn[0], planeEqn[1], planeEqn[2]], w=boundMaxDist * 2, h=boundMaxDist * 2, sx = 1, sy = 1, ch=False)
#move to center point
cmds.move(planeCenter[0], moveX=True, ws=True, a=True)
cmds.move(planeCenter[1], moveY=True, ws=True, a=True)
cmds.move(planeCenter[2], moveZ=True, ws=True, a=True)
#extrude face to proper distance
cmds.select(temp[0] + ".f[0]")
cmds.polyExtrudeFacet(ltz = boundMaxDist, ch=False)
cmds.select(temp[0])
cmds.DeleteAllHistory()
#cmds.polyNormal(normalMode=0, ch=False)
#temp =cmds.duplicate()
piece = cmds.polyBoolOp(piece[0], temp[0], op=2, ch=False)
#harden edges
cmds.polySoftEdge(a=0, ch=False)
cmds.DeleteAllHistory()
pieceFaceNum = cmds.polyEvaluate(f=True)
#cmds.polyTriangulate(piece[0]+".f[0:"+str(pieceFaceNum)+"]", ch=False)
#boolean with copy of original mesh
if(boolMesh):
cmds.select(mesh)
temp = cmds.duplicate()
cmds.DeleteAllHistory()
#meshFaceNum = cmds.polyEvaluate(f=True)
#cmds.polyTriangulate(temp[0]+".f[0:"+str(meshFaceNum)+"]", ch=False)
piece = cmds.polyBoolOp(piece[0], temp[0], op=3, ch=False)
#seperate
if( cmds.polyEvaluate(shell=True) > 1):
print "seperate"
pieces = cmds.polySeparate(ch=False)
keep = False
#closestDist = boundMaxDist
#choose closest to original point
for check in pieces:
if keep == False and isPointInMesh(check, center):
keep = check
else:
cmds.delete(check)
output.append( piece[0] )
#center pivot on piece and check for break
cmds.xform(piece[0], cp=True)
cmds.progressBar("destProgressBar", e=True, step=1)
cmds.delete(boundingBoxMesh[0])
return output
##############################
## Destruction Node ##
##############################
def createDestructionNode(mesh, properties):
destNode = cmds.group(empty=True, n="meshDestruction")
#remove defaults
cmds.setAttr( destNode + ".translateX", keyable=False, lock=True )
cmds.setAttr( destNode + ".translateY", keyable=False, lock=True )
cmds.setAttr( destNode + ".translateZ", keyable=False, lock=True )
cmds.setAttr( destNode + ".rotateX", keyable=False, lock=True )
cmds.setAttr( destNode + ".rotateY", keyable=False, lock=True )
cmds.setAttr( destNode + ".rotateZ", keyable=False, lock=True )
cmds.setAttr( destNode + ".scaleX", keyable=False, lock=True )
cmds.setAttr( destNode + ".scaleY", keyable=False, lock=True )
cmds.setAttr( destNode + ".scaleZ", keyable=False, lock=True )
#add custom
#material properties
cmds.addAttr( ln="inputMesh", nn="Mesh", keyable=False, dt="string")
cmds.addAttr( ln="density", nn="Density", keyable=False, at="float", min=0.0, max=1)
cmds.addAttr( ln="grainSize", nn="Grain Size", keyable=False, at="float", min=0.01 )
cmds.addAttr( ln="scatterSize", nn="Grain Size Scatter", keyable=False, at="float", min=0.0, max=1 )
#set values
cmds.setAttr( destNode + ".inputMesh", mesh, type="string", cb=True)
cmds.setAttr( destNode + ".density", properties[0], cb=True)
cmds.setAttr( destNode + ".grainSize", properties[1] ,cb=True)
cmds.setAttr( destNode + ".scatterSize", properties[2], cb=True)
#UI values
cmds.addAttr( ln="destType", keyable=False, at="short")
cmds.addAttr( ln="force", keyable=False, at="float")
cmds.addAttr( ln="vectorControl", keyable=False, dt="string")
cmds.addAttr( ln="chip1", keyable=False, at="bool")
cmds.addAttr( ln="chip2", keyable=False, at="bool")
cmds.addAttr( ln="chip3", keyable=False, at="bool")
cmds.addAttr( ln="chipPerc", keyable=False, dt="short3")
cmds.addAttr( ln="breakType", keyable=False, at="short")
cmds.addAttr( ln="planeControl", keyable=False, dt="string")
#fill ui values
cmds.setAttr(destNode + ".destType", cmds.radioButtonGrp("defType", q=True, select=True))
cmds.setAttr(destNode + ".force", cmds.floatSliderGrp("fractureForce", q=True, v=True))
cmds.setAttr(destNode + ".vectorControl", cmds.textField("fractureVectorObject", q=True, text=True), type="string")
cmds.setAttr(destNode + ".chip1", cmds.checkBoxGrp("chipOn", q=True, v1=True))
cmds.setAttr(destNode + ".chip2", cmds.checkBoxGrp("chipOn", q=True, v2=True))
cmds.setAttr(destNode + ".chip3", cmds.checkBoxGrp("chipOn", q=True, v3=True))
cmds.setAttr(destNode + ".chipPerc", cmds.intSliderGrp("vertexPercentage", q=True, v=True), cmds.intSliderGrp("edgePercentage", q=True, v=True), cmds.intSliderGrp("facePercentage", q=True, v=True), type="short3" )
cmds.setAttr(destNode + ".breakType", cmds.radioButtonGrp("breakPieces", q=True, select=True))
cmds.setAttr(destNode + ".planeControl", cmds.textField("breakPlaneObject", q=True, text=True), type="string")
return destNode
##############################
## fill UI from Node ##
##############################
def populateUI( destNode ):
cmds.floatSliderGrp("matDensity", e=True, v= cmds.getAttr( destNode + ".density") )
cmds.floatSliderGrp("matGrainSize", e=True, v=cmds.getAttr( destNode + ".grainSize") )
cmds.floatSliderGrp("matScatterSize", e=True, v=cmds.getAttr( destNode + ".scatterSize") )
cmds.radioButtonGrp("defType", e=True, select=cmds.getAttr(destNode + ".destType"))
cmds.floatSliderGrp("fractureForce", e=True, v=cmds.getAttr(destNode + ".force"))
cmds.textField("fractureVectorObject", e=True, text=cmds.getAttr(destNode + ".vectorControl"))
cmds.checkBoxGrp("chipOn", e=True, v1=cmds.getAttr(destNode + ".chip1"))
cmds.checkBoxGrp("chipOn", e=True, v2=cmds.getAttr(destNode + ".chip2"))
cmds.checkBoxGrp("chipOn", e=True, v3=cmds.getAttr(destNode + ".chip3"))
arr = cmds.getAttr(destNode + ".chipPerc")
cmds.intSliderGrp("vertexPercentage", e=True, v=arr[0][0])
cmds.intSliderGrp("edgePercentage", e=True, v=arr[0][1])
cmds.intSliderGrp("facePercentage", e=True, v=arr[0][2])
cmds.radioButtonGrp("breakPieces", e=True, select=cmds.getAttr(destNode + ".breakType"))
cmds.textField("breakPlaneObject", e=True, text=cmds.getAttr(destNode + ".planeControl"))
if(cmds.objExists(cmds.getAttr(destNode + ".inputMesh"))):
cmds.select(cmds.getAttr(destNode + ".inputMesh"))
else:
cmds.warning("The mesh associated with this destruction has been renamed or no longer exists")
##############################
## Controle Plane ##
##############################
def createControlPlane(size=1):
grp = cmds.createNode("transform", n="planeControl")
border = cmds.curve(p=[(-0.5, -0.5, 0), (0.5, -0.5, 0), (0.5, 0.5, 0), (-0.5, 0.5, 0), (-0.5, -0.5, 0)], d=1)
borderShape = cmds.listRelatives(border, shapes=True)
cmds.setAttr(borderShape[0] + ".overrideEnabled", True)
cmds.setAttr(borderShape[0] + ".overrideColor", 9)
cmds.parent(borderShape[0], grp, s=True, r=True)
cmds.delete(border)
for i in rangef(-0.5, 0.5, 0.05):
#negative
line = cmds.curve(d=1, p=[(i, -0.5, 0), (-0.5, i, 0)])
lineShape = cmds.listRelatives(line, shapes=True)
cmds.setAttr(lineShape[0] + ".overrideEnabled", True)
cmds.setAttr(lineShape[0] + ".overrideColor", 9)
cmds.parent(lineShape[0], grp, s=True, r=True)
cmds.delete(line)
#positive
line = cmds.curve(d=1, p=[(-i, 0.5, 0), (0.5, -i, 0)])
lineShape = cmds.listRelatives(line, shapes=True)
cmds.setAttr(lineShape[0] + ".overrideEnabled", True)
cmds.setAttr(lineShape[0] + ".overrideColor", 9)
cmds.parent(lineShape[0], grp, s=True, r=True)
cmds.delete(line)
cmds.delete()
cmds.select(grp)
cmds.scale(size, x=True, r=True)
cmds.scale(size, y=True, r=True)
cmds.makeIdentity(a=True, t=True, r=True, s=True)
#connect to UI
#break
cmds.textField("breakPlaneObject", edit=True, text=grp, bgc=[0.47, 1.0, 0.48] )
return grp
##############################
## Control Vector ##
##############################
def createControlVector(size=1):
grp = cmds.createNode("transform", n="vectorControl")
main = cmds.curve(p=[(-0.5, 0, 0), (0.5, 0, 0)], d=1)
mainShape = cmds.listRelatives(main, shapes=True)
cmds.setAttr(mainShape[0] + ".overrideEnabled", True)
cmds.setAttr(mainShape[0] + ".overrideColor", 9)
cmds.parent(mainShape[0], grp, s=True, r=True)
cmds.delete(main)
#outerLines
for i in range(4):
#negative
line = cmds.curve(d=1, p=[(-0.45, 0.05 * (-0.5 + i%2), 0.05*(int(i/2.0)-0.5)), ( 0.45, 0.05*(-0.5 + i%2), 0.05*(int(i/2.0) - 0.5) )])
lineShape = cmds.listRelatives(line, shapes=True)
cmds.setAttr(lineShape[0] + ".overrideEnabled", True)
cmds.setAttr(lineShape[0] + ".overrideColor", 9)
cmds.parent(lineShape[0], grp, s=True, r=True)
cmds.delete(line)
#create directional arrows
for i in rangef(-0.3, 0.5, 0.1):
for j in range(4):
#negative
line = cmds.curve(d=1, p=[(i, 0, 0), ( i-0.08, (size / 10.0)*(-0.5 + j%2), (size/10.0)*(int(j/2.0) - 0.5) )])
lineShape = cmds.listRelatives(line, shapes=True)
cmds.setAttr(lineShape[0] + ".overrideEnabled", True)
cmds.setAttr(lineShape[0] + ".overrideColor", 9)
cmds.parent(lineShape[0], grp, s=True, r=True)
cmds.delete(line)
cmds.select(grp)
cmds.scale(size, x=True, r=True)
cmds.makeIdentity(a=True, t=True, r=True, s=True)
#cmds.lockNode(grp)
#connect to UI
cmds.textField("fractureVectorObject", edit=True, text=grp, bgc=[0.47, 1.0, 0.48] )
return grp
##############################
## ##
## UTILITY FUNCTIONS ##
## ##
##############################
#combines pieces that share faces which fail boolean union
def combinePieces( pieces ):
#combine all pieces
combined = None
for pce in pieces:
dup = cmds.duplicate(pce)
if combined is None:
combined = dup
else:
#print cmds.objectType(dup)
combined = cmds.polyUnite(combined, dup, ch=False)
#merge
cmds.polyMergeVertex(d=0.0001, am=True, ch=False)
#remove lamina faces
toRemove = []
numFaces = cmds.polyEvaluate(combined, f=True)
for i in range(numFaces):
face1 = cmds.select(combined[0] + ".f[" + str(i) + "]")
vertNums1 = cmds.polyInfo(faceToVertex=True)
vertNums1 = string.split(vertNums1[0], ":")[1]
vertNums1 = string.split(vertNums1, "Hard")[0]
vertNums1 = string.split(vertNums1)
for j in range(i + 1, numFaces):
face2 = cmds.select(combined[0] + ".f[" + str(j) + "]")
vertNums2 = cmds.polyInfo(faceToVertex=True)
vertNums2 = string.split(vertNums2[0], ":")[1]
vertNums2 = string.split(vertNums2, "Hard")[0]
vertNums2 = string.split(vertNums2)
dup = True
for vert in vertNums1:
if (vert in vertNums2) is False:
dup = False
if dup:
toRemove.append( i )
toRemove.append( j )
cmds.select(clear=True)
for faceNum in toRemove:
cmds.select(combined[0] + ".f[" + str(faceNum) + "]", add=True)
cmds.delete()
cmds.select( combined[0] )
cmds.polyMergeVertex(d=0.0001, am=True, ch=False)
cmds.select( combined[0] )
cmds.DeleteAllHistory()
return combined[0]
def getSelectedMeshes():
selObjects = cmds.ls(selection=True)
objs = []
for obj in selObjects:
done = False
while(done == False):
objType = cmds.objectType(obj);
if(objType == 'transform'):
#try children shapes
children = cmds.listRelatives(obj, fullPath=True, s=True)
if( children and len(children) != 0 ):
obj = children[0]
else:
done = True
elif(objType == 'mesh'):
#store in list
objs.append(obj)
done = True
else:
done = True
return objs
#checks if a point is within a threshold of a meshes murface
def isPointOnMesh(mesh, point, thresh = 0.0001):
#find closest point
closestPoint = cmds.createNode("nearestPointOnMesh")
cmds.setAttr(closestPoint + ".inPositionX", point[0])
cmds.setAttr(closestPoint + ".inPositionY", point[1])
cmds.setAttr(closestPoint + ".inPositionZ", point[2])
cmds.connectAttr(mesh + ".worldMesh", closestPoint + ".inMesh", l=True)
#get vector from surface
pos = cmds.getAttr(closestPoint + ".position")
vec = [ point[0] - pos[0][0], point[1] - pos[0][1], point[2] - pos[0][2] ]
#distance is magnitude of vector
dist = getMag( vec )
if dist <= thresh:
return True
else:
return False
#checks if a point is inside a mesh or not
def isPointInMesh(mesh, point):
#find closest point
closestPoint = cmds.createNode("nearestPointOnMesh")
cmds.setAttr(closestPoint + ".inPositionX", point[0])
cmds.setAttr(closestPoint + ".inPositionY", point[1])
cmds.setAttr(closestPoint + ".inPositionZ", point[2])
cmds.connectAttr(mesh + ".worldMesh", closestPoint + ".inMesh", l=True)
#get surface normal
norm = cmds.getAttr(closestPoint + ".normal")
#get vector from surface
pos = cmds.getAttr(closestPoint + ".position")
vec = [ point[0] - pos[0][0], point[1] - pos[0][1], point[2] - pos[0][2] ]
#dot product
if getDotProduct(norm[0], vec) >= 0:
return False
else:
return True
#return result
#return plane equation
def getPlaneEquation( verts ):
#get normal vector
vNorm = getNormalVector( verts );
#Ax + By + Cz + D = 0
#D = -(Ax + By + Cz)
#get point on plane (vertex A) and multiply to get D
vNorm.append( -vNorm[0] * verts[0][0] - vNorm[1] * verts[0][1] - vNorm[2] * verts[0][2] )
return vNorm;
#returns float array of normal vector
def getNormalVector( verts ):
#create vectors
vec1 = [ verts[1][0] - verts[0][0], verts[1][1] - verts[0][1], verts[1][2] - verts[0][2] ]
vec2 = [ verts[2][0] - verts[0][0], verts[2][1] - verts[0][1], verts[2][2] - verts[0][2] ]
#cross them to get perpendicular vector
vNorm = getCrossProduct( vec1, vec2 )
#normalize
vNorm = normalize(vNorm)
return vNorm;
#returns intersection t ( 0->1 )
def getLinePlaneIntersect( pln, p1, p2 ):
#get scalar value t
#calculating denomiators tells if there's no intersect or infinite
denom = pln[0] * (p1[0] - p2[0]) + pln[1] * (p1[1] - p2[1]) + pln[2] * (p1[2] - p2[2])
if(denom == 0):# or (denom > -1.0e-5 and denom < 1.0e-5)):
return None
#do division
perc = pln[0] * p1[0] + pln[1] * p1[1] + pln[2] * p1[2] + pln[3]
perc /= denom
return perc
#used to get floatin point ranges with given step
def rangef(x, y, step):
while(x < y):
yield x
x += step
def normalize( vec ):
mag = getMag(vec)
new = []
for i in vec:
new.append( i / mag )
return new
def getMag( pos1, pos2=False ):
vec = []
if(pos2 != False):
for i in range(len(pos1)):
vec.append( pos1[i] - pos2[i] )
else:
vec = pos1
sum = 0
for val in vec:
sum += val * val
return math.sqrt( sum )
def getDotProduct( vec1, vec2 ):
loop = min(len(vec1), len(vec2))
#multiply matching elements and add to sum
sum = 0
for i in range( loop ):
sum += vec1[i] * vec2[i]
return sum
def getCrossProduct( vec1, vec2 ):
cProd = [];
#no great looping process for cross product so just write equations
#order is Y, Z, X or 1, 2, 0
cProd.append( vec1[1] * vec2[2] - vec1[2] * vec2[1] )
cProd.append( vec1[2] * vec2[0] - vec1[0] * vec2[2] )
cProd.append( vec1[0] * vec2[1] - vec1[1] * vec2[0] )
return cProd;