import math, sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaUI as OpenMayaUI
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaRender as OpenMayaRender
kPluginName = "spTorusField"
kPluginNodeId = OpenMaya.MTypeId(0x87008)
glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
glFT = glRenderer.glFunctionTable()
def statusError(msg):
sys.stderr.write("%s\n" % message)
raise
class TorusField(OpenMayaMPx.MPxFieldNode):
aMinDistance = OpenMaya.MObject()
aAttractDistance = OpenMaya.MObject()
aRepelDistance = OpenMaya.MObject()
aDrag = OpenMaya.MObject()
aSwarmAmplitude = OpenMaya.MObject()
aSwarmFrequency = OpenMaya.MObject()
aSwarmPhase = OpenMaya.MObject()
def __init__(self):
OpenMayaMPx.MPxFieldNode.__init__(self)
def compute(self, plug, block):
"""
Compute output force.
"""
outputForce = OpenMayaMPx.cvar.MPxFieldNode_mOutputForce
if not (plug == outputForce):
return
try:
multiIndex = plug.logicalIndex()
except:
statusError("ERROR in plug.logicalIndex.")
inputData = OpenMayaMPx.cvar.MPxFieldNode_mInputData
try:
hInputArray = block.outputArrayValue(inputData)
except:
statusError("ERROR in hInputArray = block.outputArrayValue().")
try:
hInputArray.jumpToElement(multiIndex)
except:
statusError("ERROR: hInputArray.jumpToElement failed.")
try:
hCompond = hInputArray.inputValue()
except:
statusError("ERROR in hCompond=hInputArray.inputValue")
inputPositions = OpenMayaMPx.cvar.MPxFieldNode_mInputPositions
hPosition = hCompond.child(inputPositions)
dPosition = hPosition.data()
fnPosition = OpenMaya.MFnVectorArrayData(dPosition)
try:
points = fnPosition.array()
except:
statusError("ERROR in fnPosition.array(), not find points.")
inputVelocities = OpenMayaMPx.cvar.MPxFieldNode_mInputVelocities
hVelocity = hCompond.child(inputVelocities)
dVelocity = hVelocity.data()
fnVelocity = OpenMaya.MFnVectorArrayData(dVelocity)
try:
velocities = fnVelocity.array()
except:
statusError("ERROR in fnVelocity.array(), not find velocities.")
inputMass = OpenMayaMPx.cvar.MPxFieldNode_mInputMass
hMass = hCompond.child(inputMass)
dMass = hMass.data()
fnMass = OpenMaya.MFnDoubleArrayData(dMass)
try:
masses = fnMass.array()
except:
statusError("ERROR in fnMass.array(), not find masses.")
forceArray = OpenMaya.MVectorArray()
useMaxDistSet = self.__useMaxDistanceValue(block)
if useMaxDistSet:
self.__applyMaxDist(block, points, velocities, masses, forceArray)
else:
self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
try:
hOutArray = block.outputArrayValue(outputForce)
except:
statusError("ERROR in hOutArray = block.outputArrayValue.")
try:
bOutArray = hOutArray.builder()
except:
statusError("ERROR in bOutArray = hOutArray.builder.")
try:
hOut = bOutArray.addElement(multiIndex)
except:
statusError("ERROR in hOut = bOutArray.addElement.")
fnOutputForce = OpenMaya.MFnVectorArrayData()
try:
dOutputForce = fnOutputForce.create(forceArray)
except:
statusError("ERROR in dOutputForce = fnOutputForce.create")
hOut.setMObject(dOutputForce)
block.setClean(plug)
def draw (self, view, path, style, status):
"""
Draw a set of rings to symbolie the field. This does not override default icon, you can do that by implementing the iconBitmap() function
"""
TORUS_PI = 3.14159265
TORUS_2PI = 2.0 * TORUS_PI
EDGES = 30
SEGMENTS = 20
view.beginGL()
for j in range(SEGMENTS):
glFT.glPushMatrix()
glFT.glRotatef(360.0 * j / SEGMENTS, 0.0, 1.0, 0.0)
glFT.glTranslatef(1.5, 0.0, 0.0)
for i in range(EDGES):
glFT.glBegin(OpenMayaRender.MGL_LINE_STRIP)
p0 = TORUS_2PI * i / EDGES
p1 = TORUS_2PI * (i+1) / EDGES
glFT.glVertex2f(math.cos(p0), math.sin(p0))
glFT.glVertex2f(math.cos(p1), math.sin(p1))
glFT.glEnd()
glFT.glPopMatrix()
view.endGL()
def getForceAtPoint(self, points, velocities, masses, forceArray, deltaTime):
"""
This method is not required to be overridden, it is only necessary
for compatibility with the MFnField function set.
"""
block = forceCache()
useMaxDistSet = self.__useMaxDistanceValue(block)
if useMaxDistSet:
self.__applyMaxDist(block, points, velocities, masses, forceArray)
else:
self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
def iconSizeAndOrigin(self, width, height, xbo, ybo):
OpenMaya.MScriptUtil.setUint( width, 32 )
OpenMaya.MScriptUtil.setUint( height, 32 )
OpenMaya.MScriptUtil.setUint( xbo, 4 )
OpenMaya.MScriptUtil.setUint( ybo, 4 )
def iconBitmap(self, bitmap):
OpenMaya.MScriptUtil.setUcharArray( bitmap, 0, 0x18 )
OpenMaya.MScriptUtil.setUcharArray( bitmap, 4, 0x66 )
OpenMaya.MScriptUtil.setUcharArray( bitmap, 8, 0xC3 )
OpenMaya.MScriptUtil.setUcharArray( bitmap, 12, 0x81 )
OpenMaya.MScriptUtil.setUcharArray( bitmap, 16, 0x81 )
OpenMaya.MScriptUtil.setUcharArray( bitmap, 20, 0xC3 )
OpenMaya.MScriptUtil.setUcharArray( bitmap, 24, 0x66 )
OpenMaya.MScriptUtil.setUcharArray( bitmap, 28, 0x18 )
def __applyNoMaxDist(self, block, points, velocities, masses, outputForce):
"""
Compute output force in the case that the useMaxDistance is not set.
"""
if points.length() != velocities.length():
return
outputForce.clear()
magValue = self.__magnitudeValue(block)
minDist = self.__minDistanceValue(block)
attractDist = self.__attractDistanceValue(block)
repelDist = self.__repelDistanceValue(block)
dragMag = self.__dragValue(block)
swarmAmp = self.__swarmAmplitudeValue(block)
posArray = self.__ownerPosition(block)
fieldPosCount = posArray.length()
receptorSize = points.length()
for ptIndex in range(receptorSize):
forceV = OpenMaya.MVector(0.0,0.0,0.0)
receptorPoint = points[ptIndex]
distance = 0.0
for i in range(fieldPosCount-1, -1, -1):
difference = receptorPoint - posArray[i]
distance = difference.length()
if distance < minDist:
continue
if distance <= repelDist:
forceV += difference * magValue
elif distance >= attractDist:
forceV += -difference * magValue
if distance >= repelDist and distance <= attractDist:
if dragMag > 0:
if fieldPosCount > 0:
dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
forceV += dragForceV
if swarmAmp > 0:
frequency = self.__swarmFrequencyValue(block)
phase = OpenMaya.MVector(0.0, 0.0, frequency)
for i in range(fieldPosCount-1, -1, -1):
difference = receptorPoint - posArray[i]
difference = (difference + phase) * frequency
noiseEffect = [ difference[i] for i in range(3) ]
if( (noiseEffect[0] < -2147483647.0) or
(noiseEffect[0] > 2147483647.0) or
(noiseEffect[1] < -2147483647.0) or
(noiseEffect[1] > 2147483647.0) or
(noiseEffect[2] < -2147483647.0) or
(noiseEffect[2] > 2147483647.0) ):
continue
noiseOut = self.__noiseFunction(noiseEffect)
swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
noiseOut[1] * swarmAmp,
noiseOut[2] * swarmAmp)
forceV += swarmForce
outputForce.append(forceV)
def __applyMaxDist(self, block, points, velocities, masses, outputForce):
"""
Compute output force in the case that the useMaxDistance is set.
"""
if points.length() != velocities.length():
return
outputForce.clear()
magValue = self.__magnitudeValue(block)
attenValue = self.__attenuationValue(block)
maxDist = self.__maxDistanceValue(block)
minDist = self.__minDistanceValue(block)
attractDist = self.__attractDistanceValue(block)
repelDist = self.__repelDistanceValue(block)
dragMag = self.__dragValue(block)
swarmAmp = self.__swarmAmplitudeValue(block)
posArray = self.__ownerPosition(block)
fieldPosCount = posArray.length()
receptorSize = points.length()
for ptIndex in range(receptorSize):
receptorPoint = points[ptIndex]
forceV = OpenMaya.MVector(0,0,0)
sumForceV = OpenMaya.MVector(0,0,0)
for i in range(fieldPosCount-1, -1, -1):
difference = receptorPoint-posArray[i]
distance = difference.length()
if (distance < minDist or distance > maxDist):
continue
if attenValue > 0.0:
force = magValue * (math.pow((1.0-(distance/maxDist)),attenValue))
forceV = difference * force
elif (distance <= repelDist):
forceV = difference * magValue
elif (distance >= attractDist):
forceV = -difference * magValue
if distance >= repelDist and distance <= attractDist:
if dragMag > 0:
if fieldPosCount > 0:
dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
forceV += dragForceV
if swarmAmp > 0:
frequency = self.__swarmFrequencyValue(block)
phase = OpenMaya.MVector(0.0, 0.0, frequency)
for i in range(fieldPosCount-1, -1, -1):
difference = receptorPoint - posArray[i]
difference = (difference + phase) * frequency
noiseEffect = [ difference[i] for i in range(3) ]
if( (noiseEffect[0] < -2147483647.0) or
(noiseEffect[0] > 2147483647.0) or
(noiseEffect[1] < -2147483647.0) or
(noiseEffect[1] > 2147483647.0) or
(noiseEffect[2] < -2147483647.0) or
(noiseEffect[2] > 2147483647.0) ):
continue
noiseOut = self.__noiseFunction(noiseEffect)
swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
noiseOut[1] * swarmAmp,
noiseOut[2] * swarmAmp)
forceV += swarmForce
if (maxDist > 0.0):
forceV *= self.falloffCurve(distance/maxDist)
sumForceV += forceV
outputForce.append(sumForceV)
def __ownerPosition(self, block):
"""
If this field has an owner, get the owner's position array or
centroid, then assign it to the ownerPosArray.
If it does not have owner, get the field position in the world
space, and assign it to the given array, ownerPosArray.
"""
ownerPosArray = OpenMaya.MVectorArray()
if self.__applyPerVertexValue(block):
ownerPos = OpenMayaMPx.cvar.MPxFieldNode_mOwnerPosData
try:
hOwnerPos = block.inputValue(ownerPos)
except:
worldPos = self.__getWorldPosition()
ownerPosArray.append(worldPos)
else:
dOwnerPos = hOwnerPos.data()
fnOwnerPos = OpenMaya.MFnVectorArrayData(dOwnerPos)
try:
posArray = fnOwnerPos.array()
except:
worldPos = self.__getWorldPosition()
ownerPosArray.append(worldPos)
else:
for i in range(posArray.length()):
ownerPosArray.append(posArray[i])
else:
try:
centroidV = self.__ownerCentroidValue(block)
except:
worldPos = self.__getWorldPosition()
ownerPosArray.append(worldPos)
else:
ownerPosArray.append(centroidV)
return ownerPosArray
def __getWorldPosition(self):
thisNode = self.thisMObject()
fnThisNode = OpenMaya.MFnDependencyNode(thisNode)
worldMatrixAttr = fnThisNode.attribute("worldMatrix")
matrixPlug = OpenMaya.MPlug(thisNode, worldMatrixAttr)
matrixPlug = matrixPlug.elementByLogicalIndex(0)
try:
matrixObject = matrixPlug.asMObject(matrixObject)
except:
statusError("TorusField.__getWorldPosition: get matrixObject")
try:
worldMatrixData = OpenMaya.MFnMatrixData(matrixObject)
except:
statusError("TorusField.__getWorldPosition: get worldMatrixData")
try:
worldMatrix = worldMatrixData.matrix()
except:
statusError("TorusField.__getWorldPosition: get worldMatrix")
return OpenMaya.MVector(worldMatrix(3, 0), worldMatrix(3, 1), worldMatrix(3, 2))
def __noiseFunction(self, inNoise):
"""
A noise function
"""
xlim = [ [0,0], [0,0], [0,0] ]
xarg = [0.0, 0.0, 0.0 ]
rand3a = lambda x,y,z: frand(67*(x)+59*(y)+71*(z))
rand3b = lambda x,y,z: frand(73*(x)+79*(y)+83*(z))
rand3c = lambda x,y,z: frand(89*(x)+97*(y)+101*(z))
rand3d = lambda x,y,z: frand(103*(x)+107*(y)+109*(z))
def frand(s):
seed = s << 13^s
return (1.0 - ((s*(s*s*15731+789221)+1376312589) & 0x7fffffff)/1073741824.0)
def hermite(p0, p1, r0, r1, t):
t2 = t * t
t3 = t2 * t
_3t2 = 3.0 * t2
_2t3 = 2.0 * t3
return (p0*(_2t3-_3t2+1) + p1*(-_2t3+_3t2) + r0*(t3-2.0*t2+t) + r1*(t3-t2))
def interpolate(i, n):
f = [ 0.0, 0.0, 0.0, 0.0 ]
if n == 0:
f[0] = rand3a( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
f[1] = rand3b( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
f[2] = rand3c( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
f[3] = rand3d( xlim[0][i&1], xlim[1][i>>1&1], xlim[2][i>>2] )
else:
n -= 1
f0 = interpolate(i, n)
f1 = interpolate(i | 1<<n, n)
f[0] = (1.0 - xarg[n]) * f0[0] + xarg[n] * f1[0]
f[1] = (1.0 - xarg[n]) * f0[1] + xarg[n] * f1[1]
f[2] = (1.0 - xarg[n]) * f0[2] + xarg[n] * f1[2]
f[3] = hermite(f0[3], f1[3], f0[n], f1[n], xarg[n])
return f
xlim[0][0] = int(math.floor(inNoise[0]))
xlim[0][1] = xlim[0][0] + 1
xlim[1][0] = int(math.floor(inNoise[1]))
xlim[1][1] = xlim[1][0] + 1
xlim[2][0] = int(math.floor(inNoise[2]))
xlim[2][1] = xlim[2][0] + 1
xarg[0] = inNoise[0] - xlim[0][0]
xarg[1] = inNoise[1] - xlim[1][0]
xarg[2] = inNoise[2] - xlim[2][0]
return interpolate(0, 3)
def __magnitudeValue(self, block):
magnitude = OpenMayaMPx.cvar.MPxFieldNode_mMagnitude
hValue = block.inputValue(magnitude)
return hValue.asDouble()
def __attenuationValue(self, block):
attenuation = OpenMayaMPx.cvar.MPxFieldNode_mAttenuation
hValue = block.inputValue(attenuation)
return hValue.asDouble()
def __maxDistanceValue(self, block):
maxDistance = OpenMayaMPx.cvar.MPxFieldNode_mMaxDistance
hValue = block.inputValue(maxDistance)
return hValue.asDouble()
def __useMaxDistanceValue(self, block):
useMaxDistance = OpenMayaMPx.cvar.MPxFieldNode_mUseMaxDistance
hValue = block.inputValue(useMaxDistance)
return hValue.asBool()
def __applyPerVertexValue(self, block):
applyPerVertex = OpenMayaMPx.cvar.MPxFieldNode_mApplyPerVertex
hValue = block.inputValue(applyPerVertex)
return hValue.asBool()
def __minDistanceValue(self, block):
hValue = block.inputValue(TorusField.aMinDistance)
return hValue.asDouble()
def __attractDistanceValue(self, block):
hValue = block.inputValue(TorusField.aAttractDistance)
return hValue.asDouble()
def __repelDistanceValue(self, block):
hValue = block.inputValue(TorusField.aRepelDistance)
return hValue.asDouble()
def __dragValue(self, block):
hValue = block.inputValue(TorusField.aDrag)
return hValue.asDouble()
def __swarmAmplitudeValue(self, block):
hValue = block.inputValue(TorusField.aSwarmAmplitude)
return hValue.asDouble()
def __swarmFrequencyValue(self, block):
hValue = block.inputValue(TorusField.aSwarmFrequency)
return hValue.asDouble()
def __swarmPhaseValue(self, block):
hValue = block.inputValue(TorusField.aSwarmPhase)
return hValue.asDouble()
def __ownerCentroidValue(self, block):
ownerCentroidX = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidX
ownerCentroidY = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidY
ownerCentroidZ = OpenMayaMPx.cvar.MPxFieldNode_mOwnerCentroidZ
hValueX = block.inputValue(ownerCentroidX)
hValueY = block.inputValue(ownerCentroidY)
hValueZ = block.inputValue(ownerCentroidZ)
return OpenMaya.MVector(hValueX.asDouble(),
hValueY.asDouble(),
hValueZ.asDouble())
def nodeCreator():
return OpenMayaMPx.asMPxPtr(TorusField())
def nodeInitializer():
numAttr = OpenMaya.MFnNumericAttribute()
TorusField.aMinDistance = numAttr.create("minDistance", "mnd", OpenMaya.MFnNumericData.kDouble, 0.0)
numAttr.setKeyable(True)
try:
TorusField.addAttribute(TorusField.aMinDistance)
except:
statusError("ERROR adding aMinDistance attribute.")
TorusField.aAttractDistance = numAttr.create("attractDistance", "ad", OpenMaya.MFnNumericData.kDouble, 20.0)
numAttr.setKeyable(True)
try:
TorusField.addAttribute(TorusField.aAttractDistance)
except:
statusError("ERROR adding aAttractDistance attribute.")
TorusField.aRepelDistance = numAttr.create("repelDistance", "rd", OpenMaya.MFnNumericData.kDouble, 10.0)
numAttr.setKeyable(True)
try:
TorusField.addAttribute(TorusField.aRepelDistance)
except:
statusError("ERROR adding aRepelDistance attribute.")
TorusField.aDrag = numAttr.create("drag", "d", OpenMaya.MFnNumericData.kDouble, 0.0)
numAttr.setKeyable(True)
try:
TorusField.addAttribute(TorusField.aDrag)
except:
statusError("ERROR adding aDrag attribute.")
TorusField.aSwarmAmplitude = numAttr.create("swarmAmplitude", "samp", OpenMaya.MFnNumericData.kDouble, 0.0)
numAttr.setKeyable(True)
try:
TorusField.addAttribute(TorusField.aSwarmAmplitude)
except:
statusError("ERROR adding aSwarmAmplitude attribute.")
TorusField.aSwarmFrequency = numAttr.create("swarmFrequency", "sfrq", OpenMaya.MFnNumericData.kDouble, 1.0)
numAttr.setKeyable(True)
try:
TorusField.addAttribute(TorusField.aSwarmFrequency)
except:
statusError("ERROR adding aSwarmFrequency attribute.")
TorusField.aSwarmPhase = numAttr.create("swarmPhase", "sa", OpenMaya.MFnNumericData.kDouble, 0.0)
numAttr.setKeyable(True)
try:
TorusField.addAttribute(TorusField.aSwarmPhase)
except:
statusError("ERROR adding aSwarmPhase attribute.")
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
try:
mplugin.registerNode(kPluginName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kFieldNode)
except:
statusError("Failed to register node: %s" % kPluginName)
def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterNode(kPluginNodeId)
except:
statusError("Failed to deregister node: %s" % kPluginName)