Hi. I want to create cusrom node that replicate the original mesh partially and that update its point position in real-time if the original mesh changed as well. So here is the code of the custom node:
createCsPoly Command and csPoly node
import sys
import maya.api.OpenMaya as om2
def maya_useNewAPI():
pass
class csPoly( om2.MPxNode ):
kPluginNodeName = "csPoly"
kPluginNodeClassify = "utility/general"
kPluginNodeId = om2.MTypeId( 0x86027 )
@staticmethod
def nodeCreator():
return csPoly()
@staticmethod
def nodeInitializer():
tAttr = om2.MFnTypedAttribute()
nAttr = om2.MFnNumericAttribute()
cAttr = om2.MFnCompoundAttribute()
csPoly.inMesh = tAttr.create("inMesh", "inm", om2.MFnMeshData.kMesh)
tAttr.readable = False
tAttr.keyable = False
csPoly.addAttribute(csPoly.inMesh)
csPoly.faceIDs = tAttr.create("faceIDs", "fid", om2.MFnData.kString)
csPoly.addAttribute(csPoly.faceIDs)
csPoly.controlPts = nAttr.create("controlPts", "cpt", om2.MFnNumericData.k3Double )
nAttr.array = True
csPoly.addAttribute( csPoly.controlPts )
csPoly.controlVts = nAttr.create("controlVts", "cvt", om2.MFnNumericData.k3Double )
nAttr.array = True
csPoly.addAttribute( csPoly.controlVts )
csPoly.outMesh = tAttr.create("outMesh", "onm", om2.MFnMeshData.kMesh)
tAttr.writable = True
tAttr.storable = True
tAttr.keyable = False
csPoly.addAttribute(csPoly.outMesh)
csPoly.attributeAffects( csPoly.inMesh, csPoly.outMesh )
csPoly.attributeAffects( csPoly.controlPts, csPoly.outMesh )
csPoly.attributeAffects( csPoly.controlVts, csPoly.outMesh )
def __init__( self ):
om2.MPxNode.__init__( self )
self.collectFlag = 1
self.faceVertsId = None
def collectData( self ):
self.inFaceIDs = om2.MPlug( self.thisMObject(), self.faceIDs ).asString()
listStrIDs = self.inFaceIDs.split(",")
listIDs = [ int(x) for x in listStrIDs ]
self.mFnMeshSrc = om2.MFnMesh( om2.MPlug( self.thisMObject(), self.inMesh ).asMDataHandle().asMesh() )
outVertConects = [ x for p in listIDs for x in list(self.mFnMeshSrc.getPolygonVertices(p)) ]
self.faceVertsId = set(outVertConects)
listSorted = list(set(outVertConects))
listSequental = range(len(listSorted))
self.outVertConectsOrdered = [ x + ( listSequental[listSorted.index(x)] - listSorted[listSorted.index(x)] ) for x in outVertConects]
self.outVertCounts = [ self.mFnMeshSrc.getPolygonVertices(p).__len__() for p in listIDs]
self.vValues = [ 0.0 ] * len(listIDs)
self.resultMesh = om2.MFnMeshData().create()
self.outFnMesh = om2.MFnMesh()
inMeshPointArray = self.mFnMeshSrc.getPoints(om2.MSpace.kWorld )
outPoints = [ inMeshPointArray[k] for k in self.faceVertsId ]
self.outFnMesh.create( outPoints, self.outVertCounts, self.outVertConectsOrdered, self.vValues, self.vValues, self.resultMesh)
def compute( self, plug, data ):
pntsInArray = data.inputArrayValue( self.controlPts )
vrtsInArray = data.inputArrayValue( self.controlVts )
outMeshPlug = data.outputValue( self.outMesh )
if self.collectFlag:
self.collectData()
self.collectFlag = 0
elif self.collectFlag == 0:
self.inMeshPoint = []
self.resultMeshData = om2.MFnMesh( self.resultMesh )
for p in xrange(pntsInArray.__len__()):
pntsInArray.jumpToLogicalElement(p)
vrtsInArray.jumpToLogicalElement(p)
pts = pntsInArray.inputValue().asDouble3()
vts = vrtsInArray.inputValue().asDouble3()
vec = om2.MVector(pts) + om2.MVector(vts)
self.inMeshPoint.append( om2.MPoint( vec ) )
self.resultMeshData.setPoints( self.inMeshPoint, om2.MSpace.kObject )
outMeshPlug.setMObject(self.resultMesh)
data.setClean( self.outMesh )
pntsInArray.setAllClean()
outMeshPlug.setClean()
controller_flag_short = "-c"
controller_flag_long = "-controller"
name = "createCsPoly"
class createcsPolyPoly(om2.MPxCommand):
def __init__(self):
om2.MPxCommand.__init__(self)
self.nameCsPoints = "csPoints"
self.namecsPoly = "csPoly"
self.csPoints = om2.MObject()
self.csPoly = om2.MObject()
self.dgmod = om2.MDGModifier()
self.dagmod = om2.MDagModifier()
self.connectedNodes = None
self.optimized = 1
def doIt(self, args):
sel = om2.MGlobal.getActiveSelectionList()
if sel.length() == 0:
return
argData = om2.MArgParser(self.syntax(), args)
dp = om2.MDagPath()
comp = om2.MObject()
dp, comp = sel.getComponent( 0 )
argData.getObjectStrings()
if argData.isFlagSet(controller_flag_short):
self.controller = argData.flagArgumentString(controller_flag_short, 0)
selLength = sel.length()
if selLength == 2:
try:
mItVtx = om2.MItMeshPolygon( dp, comp )
parentController = sel.getDagPath( 1 )
parentCntMobject = sel.getDependNode(1)
mesh = dp.extendToShape(0)
except:
dp, comp = sel.getComponent( 1 )
mItVtx = om2.MItMeshPolygon( dp, comp )
parentController = sel.getDagPath( 0 )
parentCntMobject = sel.getDependNode(0)
mesh = dp.extendToShape(0)
else:
mItVtx = om2.MItMeshPolygon( dp, comp )
mesh = dp.extendToShape(0)
selList = om2.MSelectionList()
selList.add(self.controller)
parentController = selList.getDagPath(0)
parentCntMobject = selList.getDependNode(0)
idx = []
while not mItVtx.isDone():
point = mItVtx.index()
idx.append(int(point))
mItVtx.next(0)
componentList = list( sorted( set( idx ) ) )
if len( componentList ) > 0 and len( str( parentController ) ) > 0:
outMeshPlig = om2.MFnDependencyNode( mesh.node() ).findPlug( "outMesh", 1 )
self.connectedNodes = [ x for x in outMeshPlig.connectedTo( 1, 1 ) if self.nameCsPoints in x.name() ]
csPointsNodes = [ x.node() for x in outMeshPlig.connectedTo( 1, 1 ) if "csPoints" in x.name() ]
fnTrGeo = om2.MFnDependencyNode()
fnTrGeo_node = fnTrGeo.create("transformGeometry")
self.csPoly = self.dgmod.createNode( om2.MTypeId(0x86027 ) )
fn_Poly_node = om2.MFnDependencyNode( self.csPoly )
fn_Poly_node.findPlug( "faceIDs", 0 ).setString( (", ".join(str(i) for i in componentList) ) )
outVertConects = [ x for p in componentList for x in list(om2.MFnMesh(mesh.node()).getPolygonVertices(p)) ]
sortedOutVerts = list(set(outVertConects))
for o in xrange(len(sortedOutVerts)):
meshPnts = om2.MFnDependencyNode( mesh.node() ).findPlug( "pnts", 0 ).elementByLogicalIndex(sortedOutVerts[o])
polyPoints = fn_Poly_node.findPlug( "controlPts", 0 ).elementByLogicalIndex(o)
meshVnts = om2.MFnDependencyNode( mesh.node() ).findPlug( "vrts", 0 ).elementByLogicalIndex(sortedOutVerts[o])
polyVerts = fn_Poly_node.findPlug( "controlVts", 0 ).elementByLogicalIndex(o)
self.dgmod.connect( meshPnts, polyPoints )
self.dgmod.connect( meshVnts, polyVerts )
self.dgmod.connect( outMeshPlig, fn_Poly_node.findPlug( "inMesh", 0 ) )
objPolyShp = om2.MFnDagNode().create("mesh", "polyCube", parentCntMobject)
self.dgmod.connect( fn_Poly_node.findPlug( "outMesh", 0 ), om2.MFnDependencyNode(fnTrGeo_node).findPlug( "inputGeometry", 0 ) )
self.dgmod.connect( om2.MFnDependencyNode(parentCntMobject).findPlug( "worldInverseMatrix", 0 ).elementByLogicalIndex(0), om2.MFnDependencyNode(fnTrGeo_node).findPlug( "transform", 0 ) )
self.dgmod.connect( om2.MFnDependencyNode(fnTrGeo_node).findPlug( "outputGeometry", 0 ), om2.MFnDependencyNode(objPolyShp).findPlug( "inMesh", 0 ) )
return self.redoIt()
def redoIt(self):
self.clearResult()
self.dgmod.doIt()
fn_csPoints_node = om2.MFnDependencyNode(self.csPoints)
if self.nameCsPoints and self.nameCsPoints != "csPoints#":
pass
else:
name = fn_csPoints_node.name()
self.setResult(name)
fn_csPoly_node = om2.MFnDependencyNode(self.csPoly)
if self.namecsPoly and self.namecsPoly != "csPoly#":
name2 = fn_csPoly_node.setName(self.namecsPoly)
else:
name2 = fn_csPoly_node.name()
self.setResult(name2)
def undoIt(self):
if not self.connectedNodes:
self.dgmod.undoIt()
self.dagmod.doIt()
def isUndoable(self):
return True
def hasSyntax(self):
return True
def creator():
return createcsPolyPoly()
def syntaxCreator():
syntax = om2.MSyntax()
syntax.setObjectType( om2.MSyntax.kStringObjects )
syntax.addFlag( controller_flag_short, controller_flag_long, om2.MSyntax.kString )
return syntax
def initializePlugin( mobject ):
mplugin = om2.MFnPlugin( mobject, "Alex Smirnov", "1.0", "Any" )
try:
mplugin.registerCommand( name, creator, syntaxCreator )
except:
sys.stderr.write( "Failed to register command: " + name )
raise
try:
mplugin.registerNode( csPoly.kPluginNodeName, csPoly.kPluginNodeId, csPoly.nodeCreator, csPoly.nodeInitializer )
except:
sys.stderr.write( "Failed to register node: " + csPoly.kPluginNodeName )
raise
def uninitializePlugin( mobject ):
mplugin = om2.MFnPlugin( mobject )
try:
mplugin.deregisterCommand( name )
except:
sys.stderr.write( "Failed to deregister command: " + name )
try:
mplugin.deregisterNode( csPoly.kPluginNodeId )
except:
sys.stderr.write( "Failed to deregister node: " + csPoly.kPluginNodeName )
raise
The main trouble in 90-94 lines, where I can’t get correct poins position
Here is the code that quickly setup the scene, just run in script editor:
Quick setup scene code - creates all nodes automatically
mc.file( new=1, f=1 )
mc.unloadPlugin( 'createCsPoly' )
mc.loadPlugin( 'createCsPoly' )
sphereObj = mc.polySphere()
cir = mc.circle()
mc.delete( sphereObj[0], ch=1 )
mc.select( sphereObj[0] + '.f[10:50]', r=1 )
mc.select( cir[0], add=1 )
mc.createCsPoly()
This node returned very small values of the points positions, however if I repeat it manually, it works ok.
Here is the manual check code:
Points positions qiuick test
mc.file( new=1, f=1 )
sphereObj = mc.polySphere( sx=6,sy=6)
mc.delete( sphereObj[0], ch=1 )
count = mc.polyEvaluate( sphereObj[0], vertex=1 )
shape = mc.listRelatives( sphereObj[0], s=1 )[0]
for x in xrange(count):
initPos = mc.getAttr( shape + '.vrts[%1.0f]' %x)[0]
relPos = mc.getAttr( shape + '.pnts[%1.0f]' %x)[0]
finPos = initPos[0]+relPos[0], initPos[1]+relPos[1], initPos[2]+relPos[2]
loc = mc.spaceLocator()
mc.xform( loc, ws=1, t=finPos)
I know that I should add world matrix to the points positions, but even without that it doesn’t work properly