1 from __future__
import division
61 from builtins
import range
63 import maya.OpenMaya
as OpenMaya
64 import maya.OpenMayaUI
as OpenMayaUI
65 import maya.OpenMayaMPx
as OpenMayaMPx
66 import maya.OpenMayaRender
as OpenMayaRender
68 kPluginName =
"spTorusField"
71 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
72 glFT = glRenderer.glFunctionTable()
75 sys.stderr.write(
"%s\n" % message)
80 class TorusField(OpenMayaMPx.MPxFieldNode):
112 OpenMayaMPx.MPxFieldNode.__init__(self)
115 def compute(self, plug, block):
117 Compute output force.
119 outputForce = OpenMayaMPx.cvar.MPxFieldNode_mOutputForce
120 if not (plug == outputForce):
126 multiIndex = plug.logicalIndex()
128 statusError(
"ERROR in plug.logicalIndex.")
135 inputData = OpenMayaMPx.cvar.MPxFieldNode_mInputData
137 hInputArray = block.outputArrayValue(inputData)
139 statusError(
"ERROR in hInputArray = block.outputArrayValue().")
142 hInputArray.jumpToElement(multiIndex)
144 statusError(
"ERROR: hInputArray.jumpToElement failed.")
149 hCompond = hInputArray.inputValue()
151 statusError(
"ERROR in hCompond=hInputArray.inputValue")
153 inputPositions = OpenMayaMPx.cvar.MPxFieldNode_mInputPositions
154 hPosition = hCompond.child(inputPositions)
155 dPosition = hPosition.data()
158 points = fnPosition.array()
160 statusError(
"ERROR in fnPosition.array(), not find points.")
162 inputVelocities = OpenMayaMPx.cvar.MPxFieldNode_mInputVelocities
163 hVelocity = hCompond.child(inputVelocities)
164 dVelocity = hVelocity.data()
167 velocities = fnVelocity.array()
169 statusError(
"ERROR in fnVelocity.array(), not find velocities.")
171 inputMass = OpenMayaMPx.cvar.MPxFieldNode_mInputMass
172 hMass = hCompond.child(inputMass)
176 masses = fnMass.array()
178 statusError(
"ERROR in fnMass.array(), not find masses.")
183 useMaxDistSet = self.__useMaxDistanceValue(block)
185 self.__applyMaxDist(block, points, velocities, masses, forceArray)
187 self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
192 hOutArray = block.outputArrayValue(outputForce)
194 statusError(
"ERROR in hOutArray = block.outputArrayValue.")
196 bOutArray = hOutArray.builder()
198 statusError(
"ERROR in bOutArray = hOutArray.builder.")
203 hOut = bOutArray.addElement(multiIndex)
205 statusError(
"ERROR in hOut = bOutArray.addElement.")
209 dOutputForce = fnOutputForce.create(forceArray)
211 statusError(
"ERROR in dOutputForce = fnOutputForce.create")
215 hOut.setMObject(dOutputForce)
219 def draw (self, view, path, style, status):
221 Draw a set of rings to symbolie the field. This does not override default icon, you can do that by implementing the iconBitmap() function
223 TORUS_PI = 3.14159265
224 TORUS_2PI = 2.0 * TORUS_PI
229 for j
in range(SEGMENTS):
231 glFT.glRotatef(360.0 * j / SEGMENTS, 0.0, 1.0, 0.0)
232 glFT.glTranslatef(1.5, 0.0, 0.0)
233 for i
in range(EDGES):
234 glFT.glBegin(OpenMayaRender.MGL_LINE_STRIP)
235 p0 = TORUS_2PI * i / EDGES
236 p1 = TORUS_2PI * (i+1) / EDGES
237 glFT.glVertex2f(math.cos(p0), math.sin(p0))
238 glFT.glVertex2f(math.cos(p1), math.sin(p1))
244 def getForceAtPoint(self, points, velocities, masses, forceArray, deltaTime):
246 This method is not required to be overridden, it is only necessary
247 for compatibility with the MFnField function set.
250 useMaxDistSet = self.__useMaxDistanceValue(block)
252 self.__applyMaxDist(block, points, velocities, masses, forceArray)
254 self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
257 def iconSizeAndOrigin(self, width, height, xbo, ybo):
258 OpenMaya.MScriptUtil.setUint( width, 32 )
259 OpenMaya.MScriptUtil.setUint( height, 32 )
260 OpenMaya.MScriptUtil.setUint( xbo, 4 )
261 OpenMaya.MScriptUtil.setUint( ybo, 4 )
264 def iconBitmap(self, bitmap):
265 OpenMaya.MScriptUtil.setUcharArray( bitmap, 0, 0x18 )
266 OpenMaya.MScriptUtil.setUcharArray( bitmap, 4, 0x66 )
267 OpenMaya.MScriptUtil.setUcharArray( bitmap, 8, 0xC3 )
268 OpenMaya.MScriptUtil.setUcharArray( bitmap, 12, 0x81 )
269 OpenMaya.MScriptUtil.setUcharArray( bitmap, 16, 0x81 )
270 OpenMaya.MScriptUtil.setUcharArray( bitmap, 20, 0xC3 )
271 OpenMaya.MScriptUtil.setUcharArray( bitmap, 24, 0x66 )
272 OpenMaya.MScriptUtil.setUcharArray( bitmap, 28, 0x18 )
277 def __applyNoMaxDist(self, block, points, velocities, masses, outputForce):
279 Compute output force in the case that the useMaxDistance is not set.
283 if points.length() != velocities.length():
292 magValue = self.__magnitudeValue(block)
293 minDist = self.__minDistanceValue(block)
294 attractDist = self.__attractDistanceValue(block)
295 repelDist = self.__repelDistanceValue(block)
296 dragMag = self.__dragValue(block)
297 swarmAmp = self.__swarmAmplitudeValue(block)
303 posArray = self.__ownerPosition(block)
305 fieldPosCount = posArray.length()
306 receptorSize = points.length()
312 for ptIndex
in range(receptorSize):
314 receptorPoint = points[ptIndex]
319 for i
in range(fieldPosCount-1, -1, -1):
320 difference = receptorPoint - posArray[i]
321 distance = difference.length()
323 if distance < minDist:
326 if distance <= repelDist:
327 forceV += difference * magValue
328 elif distance >= attractDist:
329 forceV += -difference * magValue
334 if distance >= repelDist
and distance <= attractDist:
336 if fieldPosCount > 0:
337 dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
341 frequency = self.__swarmFrequencyValue(block)
346 for i
in range(fieldPosCount-1, -1, -1):
347 difference = receptorPoint - posArray[i]
348 difference = (difference + phase) * frequency
350 noiseEffect = [ difference[i]
for i
in range(3) ]
351 if( (noiseEffect[0] < -2147483647.0)
or
352 (noiseEffect[0] > 2147483647.0)
or
353 (noiseEffect[1] < -2147483647.0)
or
354 (noiseEffect[1] > 2147483647.0)
or
355 (noiseEffect[2] < -2147483647.0)
or
356 (noiseEffect[2] > 2147483647.0) ):
359 noiseOut = self.__noiseFunction(noiseEffect)
361 noiseOut[1] * swarmAmp,
362 noiseOut[2] * swarmAmp)
365 outputForce.append(forceV)
368 def __applyMaxDist(self, block, points, velocities, masses, outputForce):
370 Compute output force in the case that the useMaxDistance is set.
374 if points.length() != velocities.length():
383 magValue = self.__magnitudeValue(block)
384 attenValue = self.__attenuationValue(block)
385 maxDist = self.__maxDistanceValue(block)
386 minDist = self.__minDistanceValue(block)
387 attractDist = self.__attractDistanceValue(block)
388 repelDist = self.__repelDistanceValue(block)
389 dragMag = self.__dragValue(block)
390 swarmAmp = self.__swarmAmplitudeValue(block)
396 posArray = self.__ownerPosition(block)
398 fieldPosCount = posArray.length()
399 receptorSize = points.length()
401 for ptIndex
in range(receptorSize):
402 receptorPoint = points[ptIndex]
408 for i
in range(fieldPosCount-1, -1, -1):
409 difference = receptorPoint-posArray[i]
410 distance = difference.length()
412 if (distance < minDist
or distance > maxDist):
416 force = magValue * (math.pow((1.0-(distance/maxDist)),attenValue))
417 forceV = difference * force
418 elif (distance <= repelDist):
419 forceV = difference * magValue
420 elif (distance >= attractDist):
421 forceV = -difference * magValue
427 if distance >= repelDist
and distance <= attractDist:
429 if fieldPosCount > 0:
430 dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
434 frequency = self.__swarmFrequencyValue(block)
439 for i
in range(fieldPosCount-1, -1, -1):
440 difference = receptorPoint - posArray[i]
441 difference = (difference + phase) * frequency
443 noiseEffect = [ difference[i]
for i
in range(3) ]
444 if( (noiseEffect[0] < -2147483647.0)
or
445 (noiseEffect[0] > 2147483647.0)
or
446 (noiseEffect[1] < -2147483647.0)
or
447 (noiseEffect[1] > 2147483647.0)
or
448 (noiseEffect[2] < -2147483647.0)
or
449 (noiseEffect[2] > 2147483647.0) ):
452 noiseOut = self.__noiseFunction(noiseEffect)
454 noiseOut[1] * swarmAmp,
455 noiseOut[2] * swarmAmp)
459 forceV *= self.falloffCurve(distance/maxDist)
462 outputForce.append(sumForceV)
465 def __ownerPosition(self, block):
467 If this field has an owner, get the owner's position array or
468 centroid, then assign it to the ownerPosArray.
469 If it does not have owner, get the field position in the world
470 space, and assign it to the given array, ownerPosArray.
473 if self.__applyPerVertexValue(block):
474 ownerPos = OpenMayaMPx.cvar.MPxFieldNode_mOwnerPosData
476 hOwnerPos = block.inputValue(ownerPos)
481 worldPos = self.__getWorldPosition()
482 ownerPosArray.append(worldPos)
484 dOwnerPos = hOwnerPos.data()
487 posArray = fnOwnerPos.array()
489 worldPos = self.__getWorldPosition()
490 ownerPosArray.append(worldPos)
494 for i
in range(posArray.length()):
495 ownerPosArray.append(posArray[i])
498 centroidV = self.__ownerCentroidValue(block)
502 worldPos = self.__getWorldPosition()
503 ownerPosArray.append(worldPos)
505 ownerPosArray.append(centroidV)
510 def __getWorldPosition(self):
511 thisNode = self.thisMObject()
516 worldMatrixAttr = fnThisNode.attribute(
"worldMatrix")
522 matrixPlug = matrixPlug.elementByLogicalIndex(0)
527 matrixObject = matrixPlug.asMObject(matrixObject)
529 statusError(
"TorusField.__getWorldPosition: get matrixObject")
534 statusError(
"TorusField.__getWorldPosition: get worldMatrixData")
537 worldMatrix = worldMatrixData.matrix()
539 statusError(
"TorusField.__getWorldPosition: get worldMatrix")
543 return OpenMaya.MVector(worldMatrix(3, 0), worldMatrix(3, 1), worldMatrix(3, 2))
546 def __noiseFunction(self, inNoise):
552 xlim = [ [0,0], [0,0], [0,0] ]
553 xarg = [0.0, 0.0, 0.0 ]
557 rand3a =
lambda x,y,z: frand(67*(x)+59*(y)+71*(z))
558 rand3b =
lambda x,y,z: frand(73*(x)+79*(y)+83*(z))
559 rand3c =
lambda x,y,z: frand(89*(x)+97*(y)+101*(z))
560 rand3d =
lambda x,y,z: frand(103*(x)+107*(y)+109*(z))
564 return (1.0 - ((s*(s*s*15731+789221)+1376312589) & 0x7fffffff)/1073741824.0)
566 def hermite(p0, p1, r0, r1, t):
572 return (p0*(_2t3-_3t2+1) + p1*(-_2t3+_3t2) + r0*(t3-2.0*t2+t) + r1*(t3-t2))
574 def interpolate(i, n):
575 f = [ 0.0, 0.0, 0.0, 0.0 ]
577 f[0] = rand3a( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
578 f[1] = rand3b( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
579 f[2] = rand3c( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
580 f[3] = rand3d( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
583 f0 = interpolate(i, n)
584 f1 = interpolate(i | 1<<n, n)
587 f[0] = (1.0 - xarg[n]) * f0[0] + xarg[n] * f1[0]
588 f[1] = (1.0 - xarg[n]) * f0[1] + xarg[n] * f1[1]
589 f[2] = (1.0 - xarg[n]) * f0[2] + xarg[n] * f1[2]
592 f[3] = hermite(f0[3], f1[3], f0[n], f1[n], xarg[n])
596 xlim[0][0] = int(math.floor(inNoise[0]))
597 xlim[0][1] = xlim[0][0] + 1
598 xlim[1][0] = int(math.floor(inNoise[1]))
599 xlim[1][1] = xlim[1][0] + 1
600 xlim[2][0] = int(math.floor(inNoise[2]))
601 xlim[2][1] = xlim[2][0] + 1
603 xarg[0] = inNoise[0] - xlim[0][0]
604 xarg[1] = inNoise[1] - xlim[1][0]
605 xarg[2] = inNoise[2] - xlim[2][0]
607 return interpolate(0, 3)
612 def __magnitudeValue(self, block):
613 magnitude = OpenMayaMPx.cvar.MPxFieldNode_mMagnitude
614 hValue = block.inputValue(magnitude)
615 return hValue.asDouble()
618 def __attenuationValue(self, block):
619 attenuation = OpenMayaMPx.cvar.MPxFieldNode_mAttenuation
620 hValue = block.inputValue(attenuation)
621 return hValue.asDouble()
624 def __maxDistanceValue(self, block):
625 maxDistance = OpenMayaMPx.cvar.MPxFieldNode_mMaxDistance
626 hValue = block.inputValue(maxDistance)
627 return hValue.asDouble()
630 def __useMaxDistanceValue(self, block):
631 useMaxDistance = OpenMayaMPx.cvar.MPxFieldNode_mUseMaxDistance
632 hValue = block.inputValue(useMaxDistance)
633 return hValue.asBool()
636 def __applyPerVertexValue(self, block):
637 applyPerVertex = OpenMayaMPx.cvar.MPxFieldNode_mApplyPerVertex
638 hValue = block.inputValue(applyPerVertex)
639 return hValue.asBool()
644 def __minDistanceValue(self, block):
645 hValue = block.inputValue(TorusField.aMinDistance)
646 return hValue.asDouble()
649 def __attractDistanceValue(self, block):
650 hValue = block.inputValue(TorusField.aAttractDistance)
651 return hValue.asDouble()
654 def __repelDistanceValue(self, block):
655 hValue = block.inputValue(TorusField.aRepelDistance)
656 return hValue.asDouble()
659 def __dragValue(self, block):
660 hValue = block.inputValue(TorusField.aDrag)
661 return hValue.asDouble()
664 def __swarmAmplitudeValue(self, block):
665 hValue = block.inputValue(TorusField.aSwarmAmplitude)
666 return hValue.asDouble()
669 def __swarmFrequencyValue(self, block):
670 hValue = block.inputValue(TorusField.aSwarmFrequency)
671 return hValue.asDouble()
674 def __swarmPhaseValue(self, block):
675 hValue = block.inputValue(TorusField.aSwarmPhase)
676 return hValue.asDouble()
679 def __ownerCentroidValue(self, block):
680 ownerCentroidX = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidX
681 ownerCentroidY = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidY
682 ownerCentroidZ = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidZ
683 hValueX = block.inputValue(ownerCentroidX)
684 hValueY = block.inputValue(ownerCentroidY)
685 hValueZ = block.inputValue(ownerCentroidZ)
696 return OpenMayaMPx.asMPxPtr(TorusField())
700 def nodeInitializer():
705 TorusField.aMinDistance = numAttr.create(
"minDistance",
"mnd", OpenMaya.MFnNumericData.kDouble, 0.0)
706 numAttr.setKeyable(
True)
708 TorusField.addAttribute(TorusField.aMinDistance)
710 statusError(
"ERROR adding aMinDistance attribute.")
712 TorusField.aAttractDistance = numAttr.create(
"attractDistance",
"ad", OpenMaya.MFnNumericData.kDouble, 20.0)
713 numAttr.setKeyable(
True)
715 TorusField.addAttribute(TorusField.aAttractDistance)
717 statusError(
"ERROR adding aAttractDistance attribute.")
719 TorusField.aRepelDistance = numAttr.create(
"repelDistance",
"rd", OpenMaya.MFnNumericData.kDouble, 10.0)
720 numAttr.setKeyable(
True)
722 TorusField.addAttribute(TorusField.aRepelDistance)
724 statusError(
"ERROR adding aRepelDistance attribute.")
726 TorusField.aDrag = numAttr.create(
"drag",
"d", OpenMaya.MFnNumericData.kDouble, 0.0)
727 numAttr.setKeyable(
True)
729 TorusField.addAttribute(TorusField.aDrag)
731 statusError(
"ERROR adding aDrag attribute.")
733 TorusField.aSwarmAmplitude = numAttr.create(
"swarmAmplitude",
"samp", OpenMaya.MFnNumericData.kDouble, 0.0)
734 numAttr.setKeyable(
True)
736 TorusField.addAttribute(TorusField.aSwarmAmplitude)
738 statusError(
"ERROR adding aSwarmAmplitude attribute.")
740 TorusField.aSwarmFrequency = numAttr.create(
"swarmFrequency",
"sfrq", OpenMaya.MFnNumericData.kDouble, 1.0)
741 numAttr.setKeyable(
True)
743 TorusField.addAttribute(TorusField.aSwarmFrequency)
745 statusError(
"ERROR adding aSwarmFrequency attribute.")
747 TorusField.aSwarmPhase = numAttr.create(
"swarmPhase",
"sa", OpenMaya.MFnNumericData.kDouble, 0.0)
748 numAttr.setKeyable(
True)
750 TorusField.addAttribute(TorusField.aSwarmPhase)
752 statusError(
"ERROR adding aSwarmPhase attribute.")
756 def initializePlugin(mobject):
757 mplugin = OpenMayaMPx.MFnPlugin(mobject,
"Autodesk",
"1.0",
"Any")
759 mplugin.registerNode(kPluginName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kFieldNode)
761 statusError(
"Failed to register node: %s" % kPluginName)
765 def uninitializePlugin(mobject):
766 mplugin = OpenMayaMPx.MFnPlugin(mobject)
768 mplugin.deregisterNode(kPluginNodeId)
770 statusError(
"Failed to deregister node: %s" % kPluginName)