qWrap node from C++ to Python

114 views
Skip to first unread message

jasonosipa

unread,
Feb 10, 2009, 11:58:06 PM2/10/09
to python_inside_maya
I'm working through converting qWrap over to Python. qWrap is a plugin
+source up on highend posted by someone going only by "Qarl"

http://highend3d.com/maya/downloads/plugins/modeling/misc/4208.html

Things are moving along pretty well so far, but I've run into
something I can't untangle.

<snip>

gAttr = om.MFnGenericAttribute()
qWrapNode.aTargetShapes = gAttr.create( 'targetShapes', 'ts' )
gAttr.addAccept( om.MFnData.kMesh )
gAttr.addAccept( om.MFnData.kNurbsSurface )

<\snip>

The addAccept lines aren't taking. Any ideas?

ryant

unread,
Feb 11, 2009, 1:10:11 AM2/11/09
to python_inside_maya

According to the API docs (http://download.autodesk.com/us/maya/
2009help/API/class_m_fn_generic_attribute.html) the version of
MFnGenericAttribute.addAccept() you are trying to call has no Python
script support. Also several commands are Deprecated.
Try .addDataAccept()

jasonosipa

unread,
Feb 11, 2009, 1:58:36 AM2/11/09
to python_inside_maya
Okay, takeaway one is to go to those online docs, they are clearly
more updated. Smacks head.

I'm still curious, though, since there are 3 things on that page that
all say addAccept, and:

MStatus MFnGenericAttribute::addAccept( const MTypeId & id )

Doesn't say it's all dead/dying like the other two. How does one know
how to use it, or to steer clear of these three options:

MStatus MFnGenericAttribute::addAccept( const MTypeId & id )
# fine?
MStatus MFnGenericAttribute::addAccept( MFnData::Type newType )
# bad
MStatus MFnGenericAttribute::addAccept( MFnNumericData::Type newType )
# bad


And even after going to dig through the online docs, I still can't
figure out why this is dying:

def compute( self, plug, data ):

if ( plug != qWrapNode.aOutShape ):
return om.kUnknownParameter

sourceFnMesh = om.MFnMesh ( data.inputValue
( qWrapNode.aSourceShape ).asMesh() )

sources = om.MPointArray() # where to shoot rays from
outPoints = om.MPointArray() # where they hit
directions = om.MFloatVectorArray() # direction to shoot rays

sourceFnMesh.getPoints( sources, om.MSpace.kWorld )
sourceFnMesh.getPoints( outPoints, om.MSpace.kWorld )
sourceFnMesh.getNormals( directions, om.MSpace.kWorld )

# create distances table - assign negative distance
distances = om.MDoubleArray()
distances.setLength( directions.length() )

for i in range( 0, directions.length() ):
distances[ i ] = -1

targetsArrayHandle = data.inputArrayValue( qWrapNode.aTargetShapes )

targetsArrayHandle.jumpToElement(0)

for i in range( 0, targetsArrayHandle.elementCount() ):

targetMesh = targetsArrayHandle.inputValue().asMesh()
targetFnMesh = om.MFnMesh( targetMesh )
intersections = om.MPointArray()

for j in range( 0, directions.length() ):

targetFnMesh.intersect( sources[ j ],
directions[ j ],
intersections,
1.0e-10,
om.MSpace.kWorld,
None )

<rest cut for brevity>


It doesn't like "directions" for some reason, and gives me this:

# TypeError: in method 'MFnMesh_intersect', argument 3 of type
'MVector const &' //

Any ideas? And again, thanks to all who help. I would've given up on
this long ago without this group as a resource.

ryant

unread,
Feb 11, 2009, 3:23:29 AM2/11/09
to python_inside_maya
First question:

MStatus MFnGenericAttribute::addAccept( const MTypeId & id )

MFnGenericAttribute.addAccept() takes a MTypeId() object and you were
passing it om.MFnData.kMesh which is a enum or int.

Second question:

Read each prototype of the function or even click on the function
itself to jump to the description. Two of the
addAccept's state "Obsolete & no script support." and the other one I
answered above.


MStatus addDataAccept (MFnData::Type newType)
MStatus addAccept (MFnData::Type newType)
Obsolete & no script support.
MStatus addAccept (MFnNumericData::Type newType)
Obsolete & no script support.

Third question:

The part you posted does not look like where the error is happening.
Turn on stack trace in your script editor
for better error reports if you dont have it on. It will give you line
numbers. I looked up the qwrap plugin
there are two spots with MVector. I am taking a shot in the dark here
as to your issue since I dont see the code.

import maya.OpenMaya as OpenMaya

//226
targetMesh = MObject(targetsArrayHandle.inputValue().asMesh())

//228
targetFnMesh = MFnMesh(targetMesh)

//229
intersections = OpenMaya.MPointArray()

//line 234
kMFnMeshPointTolerance = 1.0 #this is not in the file, I asume he had
a header and did not include it
targetFnMesh.intersect(sources[j], directions[j], intersections,
kMFnMeshPointTolerance, OpenMaya.MSpace.kWorld)

//line 239
del = OpenMaya.MVector(sources[j]) - OpenMaya.MVector(intersections
[0])


//line 291
del = OpenMaya.MVector(sources[i]) - OpenMaya.MVector(outPoints[i])


Hope that helps.

pric...@googlemail.com

unread,
Feb 11, 2009, 9:49:16 AM2/11/09
to python_inside_maya
probably stating the obvious but this is generally an incorrect
variable type. i ve got this calling closest intersection with the
wrong variable type ie Mpoint instead of Mfloatpoint. ? dunno your all
way better at this than i.

oo and a thank you to jasonosipa without your plugin i would nt of
known what a valid call actually looked like !!lol

jasonosipa

unread,
Feb 11, 2009, 12:15:26 PM2/11/09
to python_inside_maya
So, there really was something wrong with the intersection line, (I
did have Echo All and Stack Trace, and Line numbers visible, I only
posted the information that seemed pertinent). I never did figured
out what it was, so I just gave up and went to
MFnMesh.closestIntersection, because I know how to make that work, and
I'll get the same information back. Now, the whole thing seems to be
running great until the very very very end, where I try to "set" the
outMesh just like in qWrap, but it poops... Here's the entire
compute:

def compute( self, plug, data ):

if ( plug != qWrapNode.aOutShape ):
return om.kUnknownParameter

sourceMesh = data.inputValue( qWrapNode.aSourceShape )\
.asMesh()
sourceFnMesh = om.MFnMesh( sourceMesh )

maxParam = data.inputValue ( qWrapNode.aMaxDistance )
maxDist = maxParam.asFloat()

sources = om.MPointArray() # where to shoot rays from
outPoints = om.MPointArray() # where they hit
directions = om.MFloatVectorArray() # direction to shoot rays

sourceFnMesh.getPoints( sources, om.MSpace.kWorld )
sourceFnMesh.getPoints( outPoints, om.MSpace.kWorld )
sourceFnMesh.getNormals( directions, om.MSpace.kWorld )

# create distances table - assign negative distance
distances = om.MDoubleArray()
distances.setLength( directions.length() )

distances = [ -1 for i in range( 0, directions.length() ) ]

targetsArrayHandle = data.inputArrayValue( qWrapNode.aTargetShapes )

for i in range( 0, targetsArrayHandle.elementCount() ):

targetsArrayHandle.jumpToElement( i )

targetMesh = om.MObject( targetsArrayHandle.inputValue()\
.asMesh() )
#targetMesh = targetsArrayHandle.inputValue().asMesh()
targetFnMesh = om.MFnMesh( targetMesh )
intersections = om.MPointArray()

for j in range( 0, directions.length() ):

raySource = om.MFloatPoint( sources[ j ][0],
sources[ j ][1],
sources[ j ][2] )

rayDirection = om.MFloatVector( directions[ j ][0],
directions[ j ][1],
directions[ j ][2] )

hitPoint = om.MFloatPoint()

gotHit = targetFnMesh.closestIntersection(
raySource, rayDirection,
None, None, False,
om.MSpace.kWorld, maxDist,
False, None,
hitPoint,
None, None, None, None, None )

if gotHit:

delly = om.MVector( sources[ j ] - hitPoint )
distance = delly.length().asFloat()

if ( ( distance < distances[ j ] )
or ( distances[ j ] == -1 ) ):
distances[ j ] = distance
outPoints[ j ] = hitPoint

# modify points by "amount" and "maxDistance"
maxDistance = data.inputValue( \
qWrapNode.aMaxDistance )\
.asDouble()

amount = data.inputValue( qWrapNode.aAmount ).asDouble()

for i in range( 0, outPoints.length() ):

delly = om.MVector( sources[ i ] - outPoints[ i ] )
distance = delly.length()

distMix = 1

if ( distance != 0 ):
distMix = min( 1, max( 0, maxDistance / distance ) )

outPoints[ i ] = sources [ i ] \
+ ( outPoints[ i ] - sources[ i ] ) \
* distMix * amount

# create output
outFnMeshData = om.MFnMeshData()
outMeshData = outFnMeshData.create()
outFnMesh = om.MFnMesh()
outFnMesh.copy( sourceMesh, outMeshData )
outFnMesh.setPoints( outPoints )

# store it
data.outputValue( qWrapNode.aOutShape ).set( outMeshData )
data.setClean( qWrapNode.aOutShape )



When I run that, I get this:

# Traceback (most recent call last):
# File "C:/Documents and Settings/Jason Osipa/My Documents/
Production/osipaEntertainment/omen/pyScratch/qWrapNode.py", line 116,
in compute
# data.outputValue( qWrapNode.aOutShape ).set( outMeshData )
# File "C:\engserv\rbuild\194\build\wrk\optim\runTime\Python\Lib
\site-packages\maya\OpenMaya.py", line 8308, in <lambda>
# File "C:\engserv\rbuild\194\build\wrk\optim\runTime\Python\Lib
\site-packages\maya\OpenMaya.py", line 34, in _swig_getattr
# AttributeError: set //

Help? I can't seem to find an example of or documentation on
"setting" mesh data anywhere.

ryant

unread,
Feb 11, 2009, 2:23:01 PM2/11/09
to python_inside_maya
Ok your running into a non script support issue again.

data.outputValue( qWrapNode.aOutShape )
outputValue.set( outMeshData )
data.setClean( qWrapNode.aOutShape )

outputValue is a MDataHandle class object and the .set() function is
not available to Python. Here is

try this, im not sure if its the right set object but it should be:

data.outputValue( qWrapNode.aOutShape )
outputValue.setMObject( outMeshData )
data.setClean( qWrapNode.aOutShape )

outMeshData looks like it should be a MObject class object so that is
what you need to set. Check out the MDataHandle document if that
doesnt work and try other functions. There is a big list:

void setBool ( bool )
void setChar ( char )
void setShort ( short )
void setInt ( int )
void setFloat ( float )
void setDouble ( double )
void setMMatrix ( const MMatrix & )
void setMFloatMatrix ( const MFloatMatrix & )
void setMVector ( const MVector & )
void setMFloatVector ( const MFloatVector & )
void setMDistance ( const MDistance & )
void setMAngle ( const MAngle & )
void setMTime ( const MTime & )
void set2Short ( short, short )
void set2Int ( int, int )
void set2Float ( float, float )
void set2Double ( double, double )
void set3Short ( short, short, short )
void set3Int ( int, int, int )
void set3Float ( float, float, float )
void set3Double ( double, double, double )
void setString ( const MString &)
MStatus setMObject ( const MObject & data )
MStatus setMPxData ( MPxData * data )
bool asGenericBool () const
unsigned char asGenericChar () const
double asGenericDouble () const
float asGenericFloat () const
short asGenericShort () const
int asGenericInt () const
void setGenericBool ( bool value, bool force )
void setGenericChar ( unsigned char value, bool force )
void setGenericDouble ( double value, bool force )
void setGenericFloat ( float value, bool force )
void setGenericShort ( short value, bool force )
void setGenericInt ( int value, bool force )

RyanT
Technical Artist
NaughtyDog Inc.
www.rtrowbridge.com/blog

jasonosipa

unread,
Feb 12, 2009, 3:05:23 PM2/12/09
to python_inside_maya
This works, now, thanks to ALL!! I took out some features of the C
qWrap to simplify the debugging process, but I'll add those back in
eventually, and post results here.

import sys
import maya.OpenMaya as om
import maya.OpenMayaMPx as omMPx

nodeName = "qWrap"
nodeId = om.MTypeId( 0x123457 )

class qWrapNode( omMPx.MPxNode ):

aSourceShape = om.MObject()
aCollider = om.MObject()
aMaxDistance = om.MObject()
aOutShape = om.MObject()

def __init__ ( self ) :
omMPx.MPxNode.__init__( self )

def compute( self, plug, data ):

if ( plug != qWrapNode.aOutShape ):
return om.kUnknownParameter

sourceMesh = data.inputValue( qWrapNode.aSourceShape )\
.asMesh()
sourceFnMesh = om.MFnMesh( sourceMesh )

print type( sourceFnMesh )

maxDist = data.inputValue( qWrapNode.aMaxDistance )
maxDist = maxDist.asFloat()

sources = om.MPointArray()
outPoints = om.MPointArray()

sourceFnMesh.getPoints( sources, om.MSpace.kWorld )
sourceFnMesh.getPoints( outPoints, om.MSpace.kWorld )

targetMesh = data.inputValue( qWrapNode.aCollider ).asMesh()
targetFnMesh = om.MFnMesh( targetMesh )

mainCount = sources.length()

getVector = om.MVector()

for j in range( 0, mainCount ):

raySource = om.MFloatPoint( sources[ j ][0],
sources[ j ][1],
sources[ j ][2] )

sourceFnMesh.getVertexNormal( j,
False,
getVector,
om.MSpace.kWorld )

rayDirection = om.MFloatVector( getVector )
rayDirection = rayDirection.normal()
hitPoint = om.MFloatPoint()
hitFacePtr = None
idsSorted = False
testBothDirs = False
faceIds = None
triIds = None
accelParams = None
hitRayParam = None
hitTriangle = None
hitBary1 = None
hitBary2 = None

gotHit = targetFnMesh.closestIntersection(
raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
om.MSpace.kWorld,
maxDist,
testBothDirs,
accelParams,
hitPoint,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2 )

if gotHit:
outPoints.set( j, hitPoint[0], hitPoint[1], hitPoint[2] )

else:
outPoints.set( j,
raySource.x + ( rayDirection.x * maxDist ),
raySource.y + ( rayDirection.y * maxDist ),
raySource.z + ( rayDirection.z * maxDist ) )

# create output
outFnMeshData = om.MFnMeshData()
outMeshData = outFnMeshData.create()
outFnMesh = om.MFnMesh()
outFnMesh.copy( sourceMesh, outMeshData )
outFnMesh.setPoints( outPoints )

# store it
data.outputValue( qWrapNode.aOutShape ).setMObject( outMeshData )
data.setClean( qWrapNode.aOutShape )

def nodeCreator () :
return omMPx.asMPxPtr ( qWrapNode() )

def nodeInit():

nAttr = om.MFnNumericAttribute()
qWrapNode.aMaxDistance = nAttr.create( 'maxDistance', 'md',
om.MFnNumericData.kFloat, 99.9 )
nAttr.setKeyable (1)
nAttr.setWritable(1)
nAttr.setReadable(1)
nAttr.setStorable(1)
nAttr.setSoftMin (0)
qWrapNode.addAttribute( qWrapNode.aMaxDistance )

tAttr = om.MFnTypedAttribute()
qWrapNode.aSourceShape = tAttr.create( 'sourceShape', 'ss',
om.MFnData.kMesh )
tAttr.setStorable(0)
qWrapNode.addAttribute( qWrapNode.aSourceShape )

tAttr = om.MFnTypedAttribute()
qWrapNode.aCollider = tAttr.create ( 'collider', 'c',
om.MFnData.kMesh )
tAttr.setStorable(1)
tAttr.setStorable(1)
qWrapNode.addAttribute( qWrapNode.aCollider )

tAttr = om.MFnTypedAttribute()
qWrapNode.aOutShape = tAttr.create( 'outShape', 'os',
om.MFnData.kMesh )
tAttr.setStorable(0)
tAttr.setWritable(0)
qWrapNode.addAttribute( qWrapNode.aOutShape )

qWrapNode.attributeAffects( qWrapNode.aSourceShape,
qWrapNode.aOutShape )

qWrapNode.attributeAffects( qWrapNode.aCollider,
qWrapNode.aOutShape )

qWrapNode.attributeAffects( qWrapNode.aMaxDistance,
qWrapNode.aOutShape )

def initializePlugin( mobject ):
mplugin = omMPx.MFnPlugin ( mobject )
try:
mplugin.registerNode( nodeName, nodeId, nodeCreator, nodeInit )
except:
sys.stderr.write( 'Error loading' )
raise

def uninitializePlugin ( mobject ):
mplugin = omMPx.MFnPlugin ( mobject )
try:
mplugin.deregisterNode ( nodeId )
except:
sys.stderr.write( 'Error removing' )
raise


'''
#Eg Mel to use this

file -f -new;
polyCube -w 3 -h 3 -d 3 -sx 1 -sy 1 -sz 1 -ax 0 1 0 -cuv 4 -ch 0;
polySphere -r 1 -sx 35 -sy 35 -ax 0 1 0 -cuv 2 -ch 0;
polySphere -r 1 -sx 35 -sy 35 -ax 0 1 0 -cuv 2 -ch 0;
createNode qWrap;
connectAttr -f pCubeShape1.worldMesh qWrap1.collider;
connectAttr -f pSphereShape1.worldMesh qWrap1.sourceShape;
connectAttr -f qWrap1.outShape pSphereShape2.inMesh;
'''

jasonosipa

unread,
Feb 12, 2009, 3:09:18 PM2/12/09
to python_inside_maya
There's a print statement in the compute I forgot to nuke before
posting - please do so, it slows things down. :)
Reply all
Reply to author
Forward
0 new messages