Here is an update to the previous visibility node. As discussed in the last post the intention with this version was to test out a quicker alternative in changing the visibility for individual faces in a mesh for the purpose of visualising internal meshes and details.
This time however rather than adjusting the vertex face alpha values the node would make use of the component modifiers already available in Maya.These modifiers are created and maintained as historical entities to your mesh. If you apply a poly smooth then a modifier will be created for the purpose into which will hook your mesh 'orig shape', the output passing into your mesh itself. It is possible to chain the modifiers together each one maintaining a list of edited components that pass down the chain until they reach the actual mesh where the result can be seen.
It is the fact that these modifiers allow the input of a component list that allows them to be used for the purpose of dynamically adjusting a mesh based on fluctuating input data.
In the case of this example we want to see inside a mesh when a collider intersects. For this we use a deleteComponent modifier which allows us to input the original mesh shape and a list of components to be considered for deletion. These components are derived by using the ever useful MFnMesh::allIntersections method which based on casting rays can define if a mesh is colliding with another before returning the vertices that are inside.
By querying the faces that are made up of these vertices it is possible to pass a complete list over to the modifier to be 'modified'.
Below is an example of how the nodes hook into one another to achieve the desired effect.
I had initially created the node in such a way that it was able to accept a continually changing face count from the orig shape without the requirement of updating class variables. However this was at a sacrifice to speed as I was rebuilding the vertex lists for every update. Fevsy of Constrain n' Bake suggested that I only read in the vertex list once on the initialisation and then maintain it as required which is why I trigger an update by switching the update channel on the node. It speeds up the evaluation at the cost of making the node less fluid to interact with. Which is better? You decide.
Thanks also go to Hans Goddard for inspiring me with a demonstration of his implementation in this video.
..and here is mine
Visibility Node v2.0 from SBGrover on Vimeo.
If you want to have a go yourself below is a basic Python Plugin to get started with. This is an example from the first version of the node and does not support the component modifiers. Note that this plugin is also an MPxDeformer rather than an MPxNode.
import maya.cmds as cmds
import maya.OpenMaya as OpenMaya
import maya.OpenMayaAnim as OpenMayaAnim
import maya.OpenMayaMPx as OpenMayaMPx
class visibilityDeformer(OpenMayaMPx.MPxDeformerNode):
kPluginNodeId = OpenMaya.MTypeId(0x00000013)
kPluginNodeTypeName = "visibilityDeformer"
accelParams = OpenMaya.MMeshIsectAccelParams() #speeds up intersect calculation
intersector = OpenMaya.MMeshIntersector() #contains methods for efficiently finding the closest point to a mesh, required for collider
def __init__(self):
OpenMayaMPx.MPxDeformerNode.__init__( self )
def deform( self, block, geoItr, matrix, index ):
#get ENVELOPE
envelope = OpenMayaMPx.cvar.MPxGeometryFilter_envelope
envelopeHandle = block.inputValue(envelope)
envelopeVal = envelopeHandle.asFloat()
if envelopeVal!=0:
#get DEFORMED MESH
inMesh = self.get_input_geom(block, index)
#get COLLIDER MESH (as worldMesh)
colliderHandle = block.inputValue(self.collider)
inColliderMesh = colliderHandle.asMesh()
if not inColliderMesh.isNull():
inColliderFn = OpenMaya.MFnMesh(inColliderMesh)
#get COLLIDER WORLD MATRIX
colliderMatrixHandle = block.inputValue(self.colliderMatrix)
colliderMatrixVal = colliderMatrixHandle.asMatrix()
#get BOUNDING BOX MIN VALUES
colliderBoundingBoxMinHandle = block.inputValue(self.colliderBoundingBoxMin)
colliderBoundingBoxMinValDouble = colliderBoundingBoxMinHandle.asFloat3()
#get BOUNDING BOX MAX VALUES
colliderBoundingBoxMaxHandle = block.inputValue(self.colliderBoundingBoxMax)
colliderBoundingBoxMaxValDouble = colliderBoundingBoxMaxHandle.asFloat3()
colliderBoundingBoxMinVal = OpenMaya.MPoint(colliderBoundingBoxMinValDouble[0], colliderBoundingBoxMinValDouble[1], colliderBoundingBoxMinValDouble[2])
colliderBoundingBoxMaxVal = OpenMaya.MPoint(colliderBoundingBoxMaxValDouble[0], colliderBoundingBoxMaxValDouble[1], colliderBoundingBoxMaxValDouble[2])
#build new bounding box based on given values
bbox = OpenMaya.MBoundingBox()
bbox.expand(colliderBoundingBoxMinVal)
bbox.expand(colliderBoundingBoxMaxVal)
self.accelParams = inColliderFn.autoUniformGridParams()
#deal with main mesh
inMeshFn = OpenMaya.MFnMesh(inMesh)
inPointArray = OpenMaya.MPointArray()
inMeshFn.getPoints(inPointArray, OpenMaya.MSpace.kWorld)
##BEGIN DIRECT COLLISION##
deformed_list = []
col = OpenMaya.MColor(1,1,1,0)
for num in range(inPointArray.length()):
point = OpenMaya.MPoint(inPointArray[num])
bbox_flag = False
if bbox.contains(point):
bbox_flag = True
vec = OpenMaya.MVector()
inMeshFn.getVertexNormal(num, vec, OpenMaya.MSpace.kWorld)
##-- allIntersections arguments --##
floatPoint = OpenMaya.MFloatPoint(point)
floatVec = OpenMaya.MFloatVector(vec)
faceIds = None
triIds = None
idsSorted = False
space = OpenMaya.MSpace.kWorld
maxParam = 100000
testBothDirs = False
accelParams = None#self.accelParams
sortHits = False
hitPoints = OpenMaya.MFloatPointArray()
hitRayParams = None
hitFaces = OpenMaya.MIntArray()
hitTriangles = OpenMaya.MIntArray()
hitBary1 = None
hitBary2 = None
tolerance = 0.0001
####################################
inColliderFn.allIntersections( floatPoint, floatVec, faceIds, triIds, idsSorted, space, maxParam, testBothDirs, accelParams, sortHits, hitPoints, hitRayParams, hitFaces, hitTriangles, hitBary1, hitBary2, tolerance )
#for all hits of length of 1 do the following
if hitPoints.length()%2 == 1:
iterator = OpenMaya.MItMeshVertex(inMesh)
util = OpenMaya.MScriptUtil()
util.createFromInt(0)
pInt = util.asIntPtr()
iterator.setIndex(num, pInt)
faceArray = OpenMaya.MIntArray()
iterator.getConnectedFaces(faceArray)
for f_idx in range(faceArray.length()):
polyVertArray = OpenMaya.MIntArray()
inMeshFn.getPolygonVertices(faceArray[f_idx], polyVertArray)
for v_idx in range(polyVertArray.length()):
inMeshFn.setFaceVertexColor(col, faceArray[f_idx], polyVertArray[v_idx])
string = "dgdirty %s;"%self.name()
OpenMaya.MGlobal.executeCommand(string, False, False)
def get_input_geom(self, block, index):
input_attr = OpenMayaMPx.cvar.MPxGeometryFilter_input
input_geom_attr = OpenMayaMPx.cvar.MPxGeometryFilter_inputGeom
input_handle = block.outputArrayValue(input_attr)
input_handle.jumpToElement(index)
input_geom_obj = input_handle.outputValue().child(input_geom_attr).asMesh()
return input_geom_obj
def creator():
return OpenMayaMPx.asMPxPtr(visibilityDeformer())
def initialize():
gAttr = OpenMaya.MFnGenericAttribute()
mAttr = OpenMaya.MFnMatrixAttribute()
nAttr = OpenMaya.MFnNumericAttribute()
visibilityDeformer.collider = gAttr.create( "colliderTarget", "col")
gAttr.addDataAccept( OpenMaya.MFnData.kMesh )
visibilityDeformer.colliderBoundingBoxMin = nAttr.createPoint( "colliderBoundingBoxMin", "cbbmin")
visibilityDeformer.colliderBoundingBoxMax = nAttr.createPoint( "colliderBoundingBoxMax", "cbbmax")
visibilityDeformer.colliderMatrix = mAttr.create("colliderMatrix", "collMatr")
visibilityDeformer.multiplier = nAttr.create("multiplier", "mult", OpenMaya.MFnNumericData.kFloat, 1)
visibilityDeformer.addAttribute( visibilityDeformer.collider )
visibilityDeformer.addAttribute( visibilityDeformer.colliderMatrix )
visibilityDeformer.addAttribute( visibilityDeformer.colliderBoundingBoxMin )
visibilityDeformer.addAttribute( visibilityDeformer.colliderBoundingBoxMax )
visibilityDeformer.addAttribute( visibilityDeformer.multiplier )
outMesh = OpenMayaMPx.cvar.MPxGeometryFilter_outputGeom
visibilityDeformer.attributeAffects( visibilityDeformer.collider, outMesh )
visibilityDeformer.attributeAffects( visibilityDeformer.colliderBoundingBoxMin, outMesh )
visibilityDeformer.attributeAffects( visibilityDeformer.colliderBoundingBoxMax, outMesh )
visibilityDeformer.attributeAffects( visibilityDeformer.colliderMatrix, outMesh )
visibilityDeformer.attributeAffects( visibilityDeformer.multiplier, outMesh )
def initializePlugin(obj):
plugin = OpenMayaMPx.MFnPlugin(obj, 'Grover', '1.0', 'Any')
try:
plugin.registerNode('visibilityDeformer', visibilityDeformer.kPluginNodeId, creator, initialize, OpenMayaMPx.MPxNode.kDeformerNode)
except:
raise RuntimeError, 'Failed to register node'
def uninitializePlugin(obj):
plugin = OpenMayaMPx.MFnPlugin(obj)
try:
plugin.deregisterNode(visibilityDeformer.kPluginNodeId)
except:
raise RuntimeError, 'Failed to deregister node'
The support code. Run this to build a quick scene which supports the plugin above. import maya.cmds as cmds
cmds.polySphere()
cmds.polySphere()
cmds.move(3,0,0)
cmds.select('pSphere1', 'pSphere2')
cmds.polyColorPerVertex(r=0.5, g=0.5, b=0.5 ,a=1 , cdo=True)
cmds.delete(ch=True)
cmds.delete(cmds.ls(type='visibilityDeformer'))
cmds.flushUndo()
cmds.unloadPlugin('visibilityDeformer')
cmds.loadPlugin('visibilityDeformer')
cmds.select('pSphere1')
deform = cmds.deformer(type='visibilityDeformer')[0]
cmds.connectAttr('pSphere2.worldMesh',deform + ".colliderTarget", f=True)
cmds.connectAttr('pSphere2.worldMatrix',deform + ".colliderMatrix", f=True)
cmds.connectAttr('pSphere2.boundingBoxMin',deform + ".colliderBoundingBoxMin", f=True)
cmds.connectAttr('pSphere2.boundingBoxMax',deform + ".colliderBoundingBoxMax", f=True)
0 comments:
Post a Comment