61 import maya.OpenMaya 
as OpenMaya
 
   62 import maya.OpenMayaUI 
as OpenMayaUI
 
   63 import maya.OpenMayaMPx 
as OpenMayaMPx
 
   64 import maya.OpenMayaRender 
as OpenMayaRender
 
   66 kPluginName = 
"spTorusField" 
   69 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
 
   70 glFT = glRenderer.glFunctionTable()
 
   73     sys.stderr.write(
"%s\n" % message)
 
   78 class TorusField(OpenMayaMPx.MPxFieldNode):
 
  110         OpenMayaMPx.MPxFieldNode.__init__(self)
 
  113     def compute(self, plug, block):
 
  115         Compute output force. 
  117         outputForce = OpenMayaMPx.cvar.MPxFieldNode_mOutputForce
 
  118         if not (plug == outputForce):
 
  124             multiIndex = plug.logicalIndex()
 
  126             statusError(
"ERROR in plug.logicalIndex.")
 
  133         inputData = OpenMayaMPx.cvar.MPxFieldNode_mInputData
 
  135             hInputArray = block.outputArrayValue(inputData)
 
  137             statusError(
"ERROR in hInputArray = block.outputArrayValue().")
 
  140             hInputArray.jumpToElement(multiIndex)
 
  142             statusError(
"ERROR: hInputArray.jumpToElement failed.")
 
  147             hCompond = hInputArray.inputValue()
 
  149             statusError(
"ERROR in hCompond=hInputArray.inputValue")
 
  151         inputPositions = OpenMayaMPx.cvar.MPxFieldNode_mInputPositions
 
  152         hPosition = hCompond.child(inputPositions)
 
  153         dPosition = hPosition.data()
 
  156             points = fnPosition.array()
 
  158             statusError(
"ERROR in fnPosition.array(), not find points.")
 
  160         inputVelocities = OpenMayaMPx.cvar.MPxFieldNode_mInputVelocities
 
  161         hVelocity = hCompond.child(inputVelocities)
 
  162         dVelocity = hVelocity.data()
 
  165             velocities = fnVelocity.array()
 
  167             statusError(
"ERROR in fnVelocity.array(), not find velocities.")
 
  169         inputMass = OpenMayaMPx.cvar.MPxFieldNode_mInputMass
 
  170         hMass = hCompond.child(inputMass)
 
  174             masses = fnMass.array()
 
  176             statusError(
"ERROR in fnMass.array(), not find masses.")
 
  181         useMaxDistSet = self.__useMaxDistanceValue(block)
 
  183             self.__applyMaxDist(block, points, velocities, masses, forceArray)
 
  185             self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
 
  190             hOutArray = block.outputArrayValue(outputForce)
 
  192             statusError(
"ERROR in hOutArray = block.outputArrayValue.")
 
  194             bOutArray = hOutArray.builder()
 
  196             statusError(
"ERROR in bOutArray = hOutArray.builder.")
 
  201             hOut = bOutArray.addElement(multiIndex)
 
  203             statusError(
"ERROR in hOut = bOutArray.addElement.")
 
  207             dOutputForce = fnOutputForce.create(forceArray)
 
  209             statusError(
"ERROR in dOutputForce = fnOutputForce.create")
 
  213         hOut.setMObject(dOutputForce)
 
  217     def draw (self, view, path, style, status):
 
  219         Draw a set of rings to symbolie the field. This does not override default icon, you can do that by implementing the iconBitmap() function 
  221         TORUS_PI = 3.14159265
 
  222         TORUS_2PI = 2.0 * TORUS_PI
 
  227         for j 
in range(SEGMENTS):
 
  229             glFT.glRotatef(360.0 * j / SEGMENTS, 0.0, 1.0, 0.0)
 
  230             glFT.glTranslatef(1.5, 0.0, 0.0)
 
  231             for i 
in range(EDGES):
 
  232                 glFT.glBegin(OpenMayaRender.MGL_LINE_STRIP)
 
  233                 p0 = TORUS_2PI * i / EDGES
 
  234                 p1 = TORUS_2PI * (i+1) / EDGES
 
  235                 glFT.glVertex2f(math.cos(p0), math.sin(p0))
 
  236                 glFT.glVertex2f(math.cos(p1), math.sin(p1))
 
  242     def getForceAtPoint(self, points, velocities, masses, forceArray, deltaTime):
 
  244         This method is not required to be overridden, it is only necessary 
  245         for compatibility with the MFnField function set. 
  248         useMaxDistSet = self.__useMaxDistanceValue(block)
 
  250             self.__applyMaxDist(block, points, velocities, masses, forceArray)
 
  252             self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
 
  255     def iconSizeAndOrigin(self, width, height, xbo, ybo):
 
  256         OpenMaya.MScriptUtil.setUint( width, 32 )
 
  257         OpenMaya.MScriptUtil.setUint( height, 32 )
 
  258         OpenMaya.MScriptUtil.setUint( xbo, 4 )
 
  259         OpenMaya.MScriptUtil.setUint( ybo, 4 )
 
  262     def iconBitmap(self, bitmap):
 
  263         OpenMaya.MScriptUtil.setUcharArray( bitmap, 0, 0x18 )
 
  264         OpenMaya.MScriptUtil.setUcharArray( bitmap, 4, 0x66 )
 
  265         OpenMaya.MScriptUtil.setUcharArray( bitmap, 8, 0xC3 )
 
  266         OpenMaya.MScriptUtil.setUcharArray( bitmap, 12, 0x81 )
 
  267         OpenMaya.MScriptUtil.setUcharArray( bitmap, 16, 0x81 )
 
  268         OpenMaya.MScriptUtil.setUcharArray( bitmap, 20, 0xC3 )
 
  269         OpenMaya.MScriptUtil.setUcharArray( bitmap, 24, 0x66 )
 
  270         OpenMaya.MScriptUtil.setUcharArray( bitmap, 28, 0x18 )
 
  275     def __applyNoMaxDist(self, block, points, velocities, masses, outputForce):
 
  277         Compute output force in the case that the useMaxDistance is not set. 
  281         if points.length() != velocities.length():
 
  290         magValue = self.__magnitudeValue(block)
 
  291         minDist = self.__minDistanceValue(block)
 
  292         attractDist = self.__attractDistanceValue(block)
 
  293         repelDist = self.__repelDistanceValue(block)
 
  294         dragMag = self.__dragValue(block)
 
  295         swarmAmp = self.__swarmAmplitudeValue(block)
 
  301         posArray = self.__ownerPosition(block)
 
  303         fieldPosCount = posArray.length()
 
  304         receptorSize = points.length()
 
  310         for ptIndex 
in range(receptorSize):
 
  312             receptorPoint = points[ptIndex]
 
  317             for i 
in range(fieldPosCount-1, -1, -1):
 
  318                 difference = receptorPoint - posArray[i]
 
  319                 distance = difference.length()
 
  321                 if distance < minDist:
 
  324                 if distance <= repelDist:
 
  325                     forceV += difference * magValue
 
  326                 elif distance >= attractDist:
 
  327                     forceV += -difference * magValue
 
  332             if distance >= repelDist 
and distance <= attractDist:
 
  334                     if fieldPosCount > 0:
 
  335                         dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
 
  339                     frequency = self.__swarmFrequencyValue(block)
 
  344                     for i 
in range(fieldPosCount-1, -1, -1):
 
  345                         difference = receptorPoint - posArray[i]
 
  346                         difference = (difference + phase) * frequency
 
  348                         noiseEffect = [ difference[i] 
for i 
in range(3) ]
 
  349                         if( (noiseEffect[0] < -2147483647.0) 
or 
  350                             (noiseEffect[0] >  2147483647.0) 
or 
  351                             (noiseEffect[1] < -2147483647.0) 
or 
  352                             (noiseEffect[1] >  2147483647.0) 
or 
  353                             (noiseEffect[2] < -2147483647.0) 
or 
  354                             (noiseEffect[2] >  2147483647.0) ):
 
  357                         noiseOut = self.__noiseFunction(noiseEffect)
 
  359                                                         noiseOut[1] * swarmAmp,
 
  360                                                         noiseOut[2] * swarmAmp)
 
  363             outputForce.append(forceV)
 
  366     def __applyMaxDist(self, block, points, velocities, masses, outputForce):
 
  368         Compute output force in the case that the useMaxDistance is set. 
  372         if points.length() != velocities.length():
 
  381         magValue = self.__magnitudeValue(block)
 
  382         attenValue = self.__attenuationValue(block)
 
  383         maxDist = self.__maxDistanceValue(block)
 
  384         minDist = self.__minDistanceValue(block)
 
  385         attractDist = self.__attractDistanceValue(block)
 
  386         repelDist = self.__repelDistanceValue(block)
 
  387         dragMag = self.__dragValue(block)
 
  388         swarmAmp = self.__swarmAmplitudeValue(block)
 
  394         posArray = self.__ownerPosition(block)
 
  396         fieldPosCount = posArray.length()
 
  397         receptorSize = points.length()
 
  399         for ptIndex 
in range(receptorSize):
 
  400             receptorPoint = points[ptIndex]
 
  406             for i 
in range(fieldPosCount-1, -1, -1):
 
  407                 difference = receptorPoint-posArray[i]
 
  408                 distance  = difference.length()
 
  410                 if (distance < minDist 
or distance > maxDist):
 
  414                     force = magValue * (math.pow((1.0-(distance/maxDist)),attenValue))
 
  415                     forceV = difference * force
 
  416                 elif (distance <= repelDist):
 
  417                     forceV = difference * magValue
 
  418                 elif (distance >= attractDist):
 
  419                     forceV = -difference * magValue                     
 
  425                 if distance >= repelDist 
and distance <= attractDist:
 
  427                         if fieldPosCount > 0:
 
  428                             dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
 
  432                         frequency = self.__swarmFrequencyValue(block)
 
  437                         for i 
in range(fieldPosCount-1, -1, -1):
 
  438                             difference = receptorPoint - posArray[i]
 
  439                             difference = (difference + phase) * frequency
 
  441                             noiseEffect = [ difference[i] 
for i 
in range(3) ]
 
  442                             if( (noiseEffect[0] < -2147483647.0) 
or 
  443                                 (noiseEffect[0] >  2147483647.0) 
or 
  444                                 (noiseEffect[1] < -2147483647.0) 
or 
  445                                 (noiseEffect[1] >  2147483647.0) 
or 
  446                                 (noiseEffect[2] < -2147483647.0) 
or 
  447                                 (noiseEffect[2] >  2147483647.0) ):
 
  450                             noiseOut = self.__noiseFunction(noiseEffect)
 
  452                                                             noiseOut[1] * swarmAmp,
 
  453                                                             noiseOut[2] * swarmAmp)
 
  457                     forceV *= self.falloffCurve(distance/maxDist)
 
  460             outputForce.append(sumForceV)
 
  463     def __ownerPosition(self, block):
 
  465         If this field has an owner, get the owner's position array or 
  466         centroid, then assign it to the ownerPosArray. 
  467         If it does not have owner, get the field position in the world 
  468         space, and assign it to the given array, ownerPosArray. 
  471         if self.__applyPerVertexValue(block):
 
  472             ownerPos = OpenMayaMPx.cvar.MPxFieldNode_mOwnerPosData
 
  474                 hOwnerPos = block.inputValue(ownerPos)
 
  479                 worldPos = self.__getWorldPosition()
 
  480                 ownerPosArray.append(worldPos)
 
  482                 dOwnerPos = hOwnerPos.data()
 
  485                     posArray = fnOwnerPos.array()
 
  487                     worldPos = self.__getWorldPosition()
 
  488                     ownerPosArray.append(worldPos)
 
  492                     for i 
in range(posArray.length()):
 
  493                         ownerPosArray.append(posArray[i])
 
  496                 centroidV = self.__ownerCentroidValue(block)
 
  500                 worldPos = self.__getWorldPosition()
 
  501                 ownerPosArray.append(worldPos)
 
  503                 ownerPosArray.append(centroidV) 
 
  508     def __getWorldPosition(self):
 
  509         thisNode = self.thisMObject()
 
  514         worldMatrixAttr = fnThisNode.attribute(
"worldMatrix")
 
  520         matrixPlug = matrixPlug.elementByLogicalIndex(0)
 
  525             matrixObject = matrixPlug.asMObject(matrixObject)
 
  527             statusError(
"TorusField.__getWorldPosition: get matrixObject")
 
  532             statusError(
"TorusField.__getWorldPosition: get worldMatrixData")
 
  535             worldMatrix = worldMatrixData.matrix()
 
  537             statusError(
"TorusField.__getWorldPosition: get worldMatrix")
 
  541         return OpenMaya.MVector(worldMatrix(3, 0), worldMatrix(3, 1), worldMatrix(3, 2))
 
  544     def __noiseFunction(self, inNoise):
 
  550         xlim = [ [0,0], [0,0], [0,0] ] 
 
  551         xarg = [0.0, 0.0, 0.0 ] 
 
  555         rand3a = 
lambda x,y,z: frand(67*(x)+59*(y)+71*(z))
 
  556         rand3b = 
lambda x,y,z: frand(73*(x)+79*(y)+83*(z))
 
  557         rand3c = 
lambda x,y,z: frand(89*(x)+97*(y)+101*(z))
 
  558         rand3d = 
lambda x,y,z: frand(103*(x)+107*(y)+109*(z))
 
  562             return (1.0 - ((s*(s*s*15731+789221)+1376312589) & 0x7fffffff)/1073741824.0)
 
  564         def hermite(p0, p1, r0, r1, t):
 
  570             return (p0*(_2t3-_3t2+1) + p1*(-_2t3+_3t2) + r0*(t3-2.0*t2+t) + r1*(t3-t2))
 
  572         def interpolate(i, n):
 
  573             f = [ 0.0, 0.0, 0.0, 0.0 ]
 
  575                 f[0] = rand3a( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
 
  576                 f[1] = rand3b( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
 
  577                 f[2] = rand3c( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
 
  578                 f[3] = rand3d( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
 
  581                 f0 = interpolate(i, n) 
 
  582                 f1 = interpolate(i | 1<<n, n) 
 
  585                 f[0] = (1.0 - xarg[n]) * f0[0] + xarg[n] * f1[0]
 
  586                 f[1] = (1.0 - xarg[n]) * f0[1] + xarg[n] * f1[1]
 
  587                 f[2] = (1.0 - xarg[n]) * f0[2] + xarg[n] * f1[2]
 
  590                 f[3] = hermite(f0[3], f1[3], f0[n], f1[n], xarg[n])
 
  594         xlim[0][0] = int(math.floor(inNoise[0]))
 
  595         xlim[0][1] = xlim[0][0] + 1
 
  596         xlim[1][0] = int(math.floor(inNoise[1]))
 
  597         xlim[1][1] = xlim[1][0] + 1
 
  598         xlim[2][0] = int(math.floor(inNoise[2]))
 
  599         xlim[2][1] = xlim[2][0] + 1
 
  601         xarg[0] = inNoise[0] - xlim[0][0]
 
  602         xarg[1] = inNoise[1] - xlim[1][0]
 
  603         xarg[2] = inNoise[2] - xlim[2][0]
 
  605         return interpolate(0, 3)
 
  610     def __magnitudeValue(self, block):
 
  611         magnitude = OpenMayaMPx.cvar.MPxFieldNode_mMagnitude
 
  612         hValue = block.inputValue(magnitude)
 
  613         return hValue.asDouble()
 
  616     def __attenuationValue(self, block):
 
  617         attenuation = OpenMayaMPx.cvar.MPxFieldNode_mAttenuation
 
  618         hValue = block.inputValue(attenuation)
 
  619         return hValue.asDouble()
 
  622     def __maxDistanceValue(self, block):
 
  623         maxDistance = OpenMayaMPx.cvar.MPxFieldNode_mMaxDistance
 
  624         hValue = block.inputValue(maxDistance)
 
  625         return hValue.asDouble()
 
  628     def __useMaxDistanceValue(self, block):
 
  629         useMaxDistance = OpenMayaMPx.cvar.MPxFieldNode_mUseMaxDistance
 
  630         hValue = block.inputValue(useMaxDistance)
 
  631         return hValue.asBool()
 
  634     def __applyPerVertexValue(self, block):
 
  635         applyPerVertex = OpenMayaMPx.cvar.MPxFieldNode_mApplyPerVertex
 
  636         hValue = block.inputValue(applyPerVertex)
 
  637         return hValue.asBool()
 
  642     def __minDistanceValue(self, block):
 
  643         hValue = block.inputValue(TorusField.aMinDistance)
 
  644         return hValue.asDouble()
 
  647     def __attractDistanceValue(self, block):
 
  648         hValue = block.inputValue(TorusField.aAttractDistance)
 
  649         return hValue.asDouble()
 
  652     def __repelDistanceValue(self, block):
 
  653         hValue = block.inputValue(TorusField.aRepelDistance)
 
  654         return hValue.asDouble()
 
  657     def __dragValue(self, block):
 
  658         hValue = block.inputValue(TorusField.aDrag)
 
  659         return hValue.asDouble()
 
  662     def __swarmAmplitudeValue(self, block):
 
  663         hValue = block.inputValue(TorusField.aSwarmAmplitude)
 
  664         return hValue.asDouble()
 
  667     def __swarmFrequencyValue(self, block):
 
  668         hValue = block.inputValue(TorusField.aSwarmFrequency)
 
  669         return hValue.asDouble()
 
  672     def __swarmPhaseValue(self, block):
 
  673         hValue = block.inputValue(TorusField.aSwarmPhase)
 
  674         return hValue.asDouble()
 
  677     def __ownerCentroidValue(self, block):
 
  678         ownerCentroidX = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidX
 
  679         ownerCentroidY = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidY
 
  680         ownerCentroidZ = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidZ
 
  681         hValueX = block.inputValue(ownerCentroidX)
 
  682         hValueY = block.inputValue(ownerCentroidY)
 
  683         hValueZ = block.inputValue(ownerCentroidZ)
 
  694     return OpenMayaMPx.asMPxPtr(TorusField())
 
  698 def nodeInitializer():
 
  703     TorusField.aMinDistance = numAttr.create(
"minDistance", 
"mnd", OpenMaya.MFnNumericData.kDouble, 0.0)
 
  704     numAttr.setKeyable(
True)
 
  706         TorusField.addAttribute(TorusField.aMinDistance)
 
  708         statusError(
"ERROR adding aMinDistance attribute.")
 
  710     TorusField.aAttractDistance = numAttr.create(
"attractDistance", 
"ad", OpenMaya.MFnNumericData.kDouble, 20.0)
 
  711     numAttr.setKeyable(
True)
 
  713         TorusField.addAttribute(TorusField.aAttractDistance)
 
  715         statusError(
"ERROR adding aAttractDistance attribute.")
 
  717     TorusField.aRepelDistance = numAttr.create(
"repelDistance", 
"rd", OpenMaya.MFnNumericData.kDouble, 10.0)
 
  718     numAttr.setKeyable(
True)
 
  720         TorusField.addAttribute(TorusField.aRepelDistance)
 
  722         statusError(
"ERROR adding aRepelDistance attribute.")
 
  724     TorusField.aDrag = numAttr.create(
"drag", 
"d", OpenMaya.MFnNumericData.kDouble, 0.0)
 
  725     numAttr.setKeyable(
True)
 
  727         TorusField.addAttribute(TorusField.aDrag)
 
  729         statusError(
"ERROR adding aDrag attribute.")
 
  731     TorusField.aSwarmAmplitude = numAttr.create(
"swarmAmplitude", 
"samp", OpenMaya.MFnNumericData.kDouble, 0.0)
 
  732     numAttr.setKeyable(
True)
 
  734         TorusField.addAttribute(TorusField.aSwarmAmplitude)
 
  736         statusError(
"ERROR adding aSwarmAmplitude attribute.")
 
  738     TorusField.aSwarmFrequency = numAttr.create(
"swarmFrequency", 
"sfrq", OpenMaya.MFnNumericData.kDouble, 1.0)
 
  739     numAttr.setKeyable(
True)
 
  741         TorusField.addAttribute(TorusField.aSwarmFrequency)
 
  743         statusError(
"ERROR adding aSwarmFrequency attribute.")
 
  745     TorusField.aSwarmPhase = numAttr.create(
"swarmPhase", 
"sa", OpenMaya.MFnNumericData.kDouble, 0.0)
 
  746     numAttr.setKeyable(
True)
 
  748         TorusField.addAttribute(TorusField.aSwarmPhase)
 
  750         statusError(
"ERROR adding aSwarmPhase attribute.")
 
  754 def initializePlugin(mobject):
 
  755     mplugin = OpenMayaMPx.MFnPlugin(mobject, 
"Autodesk", 
"1.0", 
"Any")
 
  757         mplugin.registerNode(kPluginName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kFieldNode)
 
  759         statusError(
"Failed to register node: %s" % kPluginName)
 
  763 def uninitializePlugin(mobject):
 
  764     mplugin = OpenMayaMPx.MFnPlugin(mobject)
 
  766         mplugin.deregisterNode(kPluginNodeId)
 
  768         statusError(
"Failed to deregister node: %s" % kPluginName)