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)