56 import maya.OpenMaya
as OpenMaya
57 import maya.OpenMayaUI
as OpenMayaUI
58 import maya.OpenMayaMPx
as OpenMayaMPx
59 import maya.OpenMayaRender
as OpenMayaRender
61 kPluginName =
"spTorusField"
62 kPluginNodeId = OpenMaya.MTypeId(0x87008)
64 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
65 glFT = glRenderer.glFunctionTable()
68 sys.stderr.write(
"%s\n" % message)
73 class TorusField(OpenMayaMPx.MPxFieldNode):
77 aMinDistance = OpenMaya.MObject()
81 aAttractDistance = OpenMaya.MObject()
85 aRepelDistance = OpenMaya.MObject()
89 aDrag = OpenMaya.MObject()
93 aSwarmAmplitude = OpenMaya.MObject()
97 aSwarmFrequency = OpenMaya.MObject()
101 aSwarmPhase = OpenMaya.MObject()
105 OpenMayaMPx.MPxFieldNode.__init__(self)
108 def compute(self, plug, block):
110 Compute output force.
112 outputForce = OpenMayaMPx.cvar.MPxFieldNode_mOutputForce
113 if not (plug == outputForce):
119 multiIndex = plug.logicalIndex()
121 statusError(
"ERROR in plug.logicalIndex.")
128 inputData = OpenMayaMPx.cvar.MPxFieldNode_mInputData
130 hInputArray = block.outputArrayValue(inputData)
132 statusError(
"ERROR in hInputArray = block.outputArrayValue().")
135 hInputArray.jumpToElement(multiIndex)
137 statusError(
"ERROR: hInputArray.jumpToElement failed.")
142 hCompond = hInputArray.inputValue()
144 statusError(
"ERROR in hCompond=hInputArray.inputValue")
146 inputPositions = OpenMayaMPx.cvar.MPxFieldNode_mInputPositions
147 hPosition = hCompond.child(inputPositions)
148 dPosition = hPosition.data()
149 fnPosition = OpenMaya.MFnVectorArrayData(dPosition)
151 points = fnPosition.array()
153 statusError(
"ERROR in fnPosition.array(), not find points.")
155 inputVelocities = OpenMayaMPx.cvar.MPxFieldNode_mInputVelocities
156 hVelocity = hCompond.child(inputVelocities)
157 dVelocity = hVelocity.data()
158 fnVelocity = OpenMaya.MFnVectorArrayData(dVelocity)
160 velocities = fnVelocity.array()
162 statusError(
"ERROR in fnVelocity.array(), not find velocities.")
164 inputMass = OpenMayaMPx.cvar.MPxFieldNode_mInputMass
165 hMass = hCompond.child(inputMass)
167 fnMass = OpenMaya.MFnDoubleArrayData(dMass)
169 masses = fnMass.array()
171 statusError(
"ERROR in fnMass.array(), not find masses.")
175 forceArray = OpenMaya.MVectorArray()
176 useMaxDistSet = self.__useMaxDistanceValue(block)
178 self.__applyMaxDist(block, points, velocities, masses, forceArray)
180 self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
185 hOutArray = block.outputArrayValue(outputForce)
187 statusError(
"ERROR in hOutArray = block.outputArrayValue.")
189 bOutArray = hOutArray.builder()
191 statusError(
"ERROR in bOutArray = hOutArray.builder.")
196 hOut = bOutArray.addElement(multiIndex)
198 statusError(
"ERROR in hOut = bOutArray.addElement.")
200 fnOutputForce = OpenMaya.MFnVectorArrayData()
202 dOutputForce = fnOutputForce.create(forceArray)
204 statusError(
"ERROR in dOutputForce = fnOutputForce.create")
208 hOut.setMObject(dOutputForce)
212 def draw (self, view, path, style, status):
214 Draw a set of rings to symbolie the field. This does not override default icon, you can do that by implementing the iconBitmap() function
216 TORUS_PI = 3.14159265
217 TORUS_2PI = 2.0 * TORUS_PI
222 for j
in range(SEGMENTS):
224 glFT.glRotatef(360.0 * j / SEGMENTS, 0.0, 1.0, 0.0)
225 glFT.glTranslatef(1.5, 0.0, 0.0)
226 for i
in range(EDGES):
227 glFT.glBegin(OpenMayaRender.MGL_LINE_STRIP)
228 p0 = TORUS_2PI * i / EDGES
229 p1 = TORUS_2PI * (i+1) / EDGES
230 glFT.glVertex2f(math.cos(p0), math.sin(p0))
231 glFT.glVertex2f(math.cos(p1), math.sin(p1))
237 def getForceAtPoint(self, points, velocities, masses, forceArray, deltaTime):
239 This method is not required to be overridden, it is only necessary
240 for compatibility with the MFnField function set.
243 useMaxDistSet = self.__useMaxDistanceValue(block)
245 self.__applyMaxDist(block, points, velocities, masses, forceArray)
247 self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
250 def iconSizeAndOrigin(self, width, height, xbo, ybo):
251 OpenMaya.MScriptUtil.setUint( width, 32 )
252 OpenMaya.MScriptUtil.setUint( height, 32 )
253 OpenMaya.MScriptUtil.setUint( xbo, 4 )
254 OpenMaya.MScriptUtil.setUint( ybo, 4 )
257 def iconBitmap(self, bitmap):
258 OpenMaya.MScriptUtil.setUcharArray( bitmap, 0, 0x18 )
259 OpenMaya.MScriptUtil.setUcharArray( bitmap, 4, 0x66 )
260 OpenMaya.MScriptUtil.setUcharArray( bitmap, 8, 0xC3 )
261 OpenMaya.MScriptUtil.setUcharArray( bitmap, 12, 0x81 )
262 OpenMaya.MScriptUtil.setUcharArray( bitmap, 16, 0x81 )
263 OpenMaya.MScriptUtil.setUcharArray( bitmap, 20, 0xC3 )
264 OpenMaya.MScriptUtil.setUcharArray( bitmap, 24, 0x66 )
265 OpenMaya.MScriptUtil.setUcharArray( bitmap, 28, 0x18 )
270 def __applyNoMaxDist(self, block, points, velocities, masses, outputForce):
272 Compute output force in the case that the useMaxDistance is not set.
276 if points.length() != velocities.length():
285 magValue = self.__magnitudeValue(block)
286 minDist = self.__minDistanceValue(block)
287 attractDist = self.__attractDistanceValue(block)
288 repelDist = self.__repelDistanceValue(block)
289 dragMag = self.__dragValue(block)
290 swarmAmp = self.__swarmAmplitudeValue(block)
296 posArray = self.__ownerPosition(block)
298 fieldPosCount = posArray.length()
299 receptorSize = points.length()
305 for ptIndex
in range(receptorSize):
306 forceV = OpenMaya.MVector(0.0,0.0,0.0)
307 receptorPoint = points[ptIndex]
312 for i
in range(fieldPosCount-1, -1, -1):
313 difference = receptorPoint - posArray[i]
314 distance = difference.length()
316 if distance < minDist:
319 if distance <= repelDist:
320 forceV += difference * magValue
321 elif distance >= attractDist:
322 forceV += -difference * magValue
327 if distance >= repelDist
and distance <= attractDist:
329 if fieldPosCount > 0:
330 dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
334 frequency = self.__swarmFrequencyValue(block)
335 phase = OpenMaya.MVector(0.0, 0.0, frequency)
339 for i
in range(fieldPosCount-1, -1, -1):
340 difference = receptorPoint - posArray[i]
341 difference = (difference + phase) * frequency
343 noiseEffect = [ difference[i]
for i
in range(3) ]
344 if( (noiseEffect[0] < -2147483647.0)
or
345 (noiseEffect[0] > 2147483647.0)
or
346 (noiseEffect[1] < -2147483647.0)
or
347 (noiseEffect[1] > 2147483647.0)
or
348 (noiseEffect[2] < -2147483647.0)
or
349 (noiseEffect[2] > 2147483647.0) ):
352 noiseOut = self.__noiseFunction(noiseEffect)
353 swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
354 noiseOut[1] * swarmAmp,
355 noiseOut[2] * swarmAmp)
358 outputForce.append(forceV)
361 def __applyMaxDist(self, block, points, velocities, masses, outputForce):
363 Compute output force in the case that the useMaxDistance is set.
367 if points.length() != velocities.length():
376 magValue = self.__magnitudeValue(block)
377 attenValue = self.__attenuationValue(block)
378 maxDist = self.__maxDistanceValue(block)
379 minDist = self.__minDistanceValue(block)
380 attractDist = self.__attractDistanceValue(block)
381 repelDist = self.__repelDistanceValue(block)
382 dragMag = self.__dragValue(block)
383 swarmAmp = self.__swarmAmplitudeValue(block)
389 posArray = self.__ownerPosition(block)
391 fieldPosCount = posArray.length()
392 receptorSize = points.length()
394 for ptIndex
in range(receptorSize):
395 receptorPoint = points[ptIndex]
399 forceV = OpenMaya.MVector(0,0,0)
400 sumForceV = OpenMaya.MVector(0,0,0)
401 for i
in range(fieldPosCount-1, -1, -1):
402 difference = receptorPoint-posArray[i]
403 distance = difference.length()
405 if (distance < minDist
or distance > maxDist):
409 force = magValue * (math.pow((1.0-(distance/maxDist)),attenValue))
410 forceV = difference * force
411 elif (distance <= repelDist):
412 forceV = difference * magValue
413 elif (distance >= attractDist):
414 forceV = -difference * magValue
420 if distance >= repelDist
and distance <= attractDist:
422 if fieldPosCount > 0:
423 dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
427 frequency = self.__swarmFrequencyValue(block)
428 phase = OpenMaya.MVector(0.0, 0.0, frequency)
432 for i
in range(fieldPosCount-1, -1, -1):
433 difference = receptorPoint - posArray[i]
434 difference = (difference + phase) * frequency
436 noiseEffect = [ difference[i]
for i
in range(3) ]
437 if( (noiseEffect[0] < -2147483647.0)
or
438 (noiseEffect[0] > 2147483647.0)
or
439 (noiseEffect[1] < -2147483647.0)
or
440 (noiseEffect[1] > 2147483647.0)
or
441 (noiseEffect[2] < -2147483647.0)
or
442 (noiseEffect[2] > 2147483647.0) ):
445 noiseOut = self.__noiseFunction(noiseEffect)
446 swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
447 noiseOut[1] * swarmAmp,
448 noiseOut[2] * swarmAmp)
452 forceV *= self.falloffCurve(distance/maxDist)
455 outputForce.append(sumForceV)
458 def __ownerPosition(self, block):
460 If this field has an owner, get the owner's position array or
461 centroid, then assign it to the ownerPosArray.
462 If it does not have owner, get the field position in the world
463 space, and assign it to the given array, ownerPosArray.
465 ownerPosArray = OpenMaya.MVectorArray()
466 if self.__applyPerVertexValue(block):
467 ownerPos = OpenMayaMPx.cvar.MPxFieldNode_mOwnerPosData
469 hOwnerPos = block.inputValue(ownerPos)
474 worldPos = self.__getWorldPosition()
475 ownerPosArray.append(worldPos)
477 dOwnerPos = hOwnerPos.data()
478 fnOwnerPos = OpenMaya.MFnVectorArrayData(dOwnerPos)
480 posArray = fnOwnerPos.array()
482 worldPos = self.__getWorldPosition()
483 ownerPosArray.append(worldPos)
487 for i
in range(posArray.length()):
488 ownerPosArray.append(posArray[i])
491 centroidV = self.__ownerCentroidValue(block)
495 worldPos = self.__getWorldPosition()
496 ownerPosArray.append(worldPos)
498 ownerPosArray.append(centroidV)
503 def __getWorldPosition(self):
504 thisNode = self.thisMObject()
505 fnThisNode = OpenMaya.MFnDependencyNode(thisNode)
509 worldMatrixAttr = fnThisNode.attribute(
"worldMatrix")
514 matrixPlug = OpenMaya.MPlug(thisNode, worldMatrixAttr)
515 matrixPlug = matrixPlug.elementByLogicalIndex(0)
520 matrixObject = matrixPlug.asMObject(matrixObject)
522 statusError(
"TorusField.__getWorldPosition: get matrixObject")
525 worldMatrixData = OpenMaya.MFnMatrixData(matrixObject)
527 statusError(
"TorusField.__getWorldPosition: get worldMatrixData")
530 worldMatrix = worldMatrixData.matrix()
532 statusError(
"TorusField.__getWorldPosition: get worldMatrix")
536 return OpenMaya.MVector(worldMatrix(3, 0), worldMatrix(3, 1), worldMatrix(3, 2))
539 def __noiseFunction(self, inNoise):
545 xlim = [ [0,0], [0,0], [0,0] ]
546 xarg = [0.0, 0.0, 0.0 ]
550 rand3a =
lambda x,y,z: frand(67*(x)+59*(y)+71*(z))
551 rand3b =
lambda x,y,z: frand(73*(x)+79*(y)+83*(z))
552 rand3c =
lambda x,y,z: frand(89*(x)+97*(y)+101*(z))
553 rand3d =
lambda x,y,z: frand(103*(x)+107*(y)+109*(z))
557 return (1.0 - ((s*(s*s*15731+789221)+1376312589) & 0x7fffffff)/1073741824.0)
559 def hermite(p0, p1, r0, r1, t):
565 return (p0*(_2t3-_3t2+1) + p1*(-_2t3+_3t2) + r0*(t3-2.0*t2+t) + r1*(t3-t2))
567 def interpolate(i, n):
568 f = [ 0.0, 0.0, 0.0, 0.0 ]
570 f[0] = rand3a( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
571 f[1] = rand3b( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
572 f[2] = rand3c( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
573 f[3] = rand3d( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
576 f0 = interpolate(i, n)
577 f1 = interpolate(i | 1<<n, n)
580 f[0] = (1.0 - xarg[n]) * f0[0] + xarg[n] * f1[0]
581 f[1] = (1.0 - xarg[n]) * f0[1] + xarg[n] * f1[1]
582 f[2] = (1.0 - xarg[n]) * f0[2] + xarg[n] * f1[2]
585 f[3] = hermite(f0[3], f1[3], f0[n], f1[n], xarg[n])
589 xlim[0][0] = int(math.floor(inNoise[0]))
590 xlim[0][1] = xlim[0][0] + 1
591 xlim[1][0] = int(math.floor(inNoise[1]))
592 xlim[1][1] = xlim[1][0] + 1
593 xlim[2][0] = int(math.floor(inNoise[2]))
594 xlim[2][1] = xlim[2][0] + 1
596 xarg[0] = inNoise[0] - xlim[0][0]
597 xarg[1] = inNoise[1] - xlim[1][0]
598 xarg[2] = inNoise[2] - xlim[2][0]
600 return interpolate(0, 3)
605 def __magnitudeValue(self, block):
606 magnitude = OpenMayaMPx.cvar.MPxFieldNode_mMagnitude
607 hValue = block.inputValue(magnitude)
608 return hValue.asDouble()
611 def __attenuationValue(self, block):
612 attenuation = OpenMayaMPx.cvar.MPxFieldNode_mAttenuation
613 hValue = block.inputValue(attenuation)
614 return hValue.asDouble()
617 def __maxDistanceValue(self, block):
618 maxDistance = OpenMayaMPx.cvar.MPxFieldNode_mMaxDistance
619 hValue = block.inputValue(maxDistance)
620 return hValue.asDouble()
623 def __useMaxDistanceValue(self, block):
624 useMaxDistance = OpenMayaMPx.cvar.MPxFieldNode_mUseMaxDistance
625 hValue = block.inputValue(useMaxDistance)
626 return hValue.asBool()
629 def __applyPerVertexValue(self, block):
630 applyPerVertex = OpenMayaMPx.cvar.MPxFieldNode_mApplyPerVertex
631 hValue = block.inputValue(applyPerVertex)
632 return hValue.asBool()
637 def __minDistanceValue(self, block):
638 hValue = block.inputValue(TorusField.aMinDistance)
639 return hValue.asDouble()
642 def __attractDistanceValue(self, block):
643 hValue = block.inputValue(TorusField.aAttractDistance)
644 return hValue.asDouble()
647 def __repelDistanceValue(self, block):
648 hValue = block.inputValue(TorusField.aRepelDistance)
649 return hValue.asDouble()
652 def __dragValue(self, block):
653 hValue = block.inputValue(TorusField.aDrag)
654 return hValue.asDouble()
657 def __swarmAmplitudeValue(self, block):
658 hValue = block.inputValue(TorusField.aSwarmAmplitude)
659 return hValue.asDouble()
662 def __swarmFrequencyValue(self, block):
663 hValue = block.inputValue(TorusField.aSwarmFrequency)
664 return hValue.asDouble()
667 def __swarmPhaseValue(self, block):
668 hValue = block.inputValue(TorusField.aSwarmPhase)
669 return hValue.asDouble()
672 def __ownerCentroidValue(self, block):
673 ownerCentroidX = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidX
674 ownerCentroidY = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidY
675 ownerCentroidZ = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidZ
676 hValueX = block.inputValue(ownerCentroidX)
677 hValueY = block.inputValue(ownerCentroidY)
678 hValueZ = block.inputValue(ownerCentroidZ)
679 return OpenMaya.MVector(hValueX.asDouble(),
689 return OpenMayaMPx.asMPxPtr(TorusField())
693 def nodeInitializer():
694 numAttr = OpenMaya.MFnNumericAttribute()
698 TorusField.aMinDistance = numAttr.create(
"minDistance",
"mnd", OpenMaya.MFnNumericData.kDouble, 0.0)
699 numAttr.setKeyable(
True)
701 TorusField.addAttribute(TorusField.aMinDistance)
703 statusError(
"ERROR adding aMinDistance attribute.")
705 TorusField.aAttractDistance = numAttr.create(
"attractDistance",
"ad", OpenMaya.MFnNumericData.kDouble, 20.0)
706 numAttr.setKeyable(
True)
708 TorusField.addAttribute(TorusField.aAttractDistance)
710 statusError(
"ERROR adding aAttractDistance attribute.")
712 TorusField.aRepelDistance = numAttr.create(
"repelDistance",
"rd", OpenMaya.MFnNumericData.kDouble, 10.0)
713 numAttr.setKeyable(
True)
715 TorusField.addAttribute(TorusField.aRepelDistance)
717 statusError(
"ERROR adding aRepelDistance attribute.")
719 TorusField.aDrag = numAttr.create(
"drag",
"d", OpenMaya.MFnNumericData.kDouble, 0.0)
720 numAttr.setKeyable(
True)
722 TorusField.addAttribute(TorusField.aDrag)
724 statusError(
"ERROR adding aDrag attribute.")
726 TorusField.aSwarmAmplitude = numAttr.create(
"swarmAmplitude",
"samp", OpenMaya.MFnNumericData.kDouble, 0.0)
727 numAttr.setKeyable(
True)
729 TorusField.addAttribute(TorusField.aSwarmAmplitude)
731 statusError(
"ERROR adding aSwarmAmplitude attribute.")
733 TorusField.aSwarmFrequency = numAttr.create(
"swarmFrequency",
"sfrq", OpenMaya.MFnNumericData.kDouble, 1.0)
734 numAttr.setKeyable(
True)
736 TorusField.addAttribute(TorusField.aSwarmFrequency)
738 statusError(
"ERROR adding aSwarmFrequency attribute.")
740 TorusField.aSwarmPhase = numAttr.create(
"swarmPhase",
"sa", OpenMaya.MFnNumericData.kDouble, 0.0)
741 numAttr.setKeyable(
True)
743 TorusField.addAttribute(TorusField.aSwarmPhase)
745 statusError(
"ERROR adding aSwarmPhase attribute.")
749 def initializePlugin(mobject):
750 mplugin = OpenMayaMPx.MFnPlugin(mobject,
"Autodesk",
"1.0",
"Any")
752 mplugin.registerNode(kPluginName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kFieldNode)
754 statusError(
"Failed to register node: %s" % kPluginName)
758 def uninitializePlugin(mobject):
759 mplugin = OpenMayaMPx.MFnPlugin(mobject)
761 mplugin.deregisterNode(kPluginNodeId)
763 statusError(
"Failed to deregister node: %s" % kPluginName)