scripted/torusField.py

scripted/torusField.py
1 #-
2 # ==========================================================================
3 # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All
4 # rights reserved.
5 #
6 # The coded instructions, statements, computer programs, and/or related
7 # material (collectively the "Data") in these files contain unpublished
8 # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
9 # licensors, which is protected by U.S. and Canadian federal copyright
10 # law and by international treaties.
11 #
12 # The Data is provided for use exclusively by You. You have the right
13 # to use, modify, and incorporate this Data into other products for
14 # purposes authorized by the Autodesk software license agreement,
15 # without fee.
16 #
17 # The copyright notices in the Software and this entire statement,
18 # including the above license grant, this restriction and the
19 # following disclaimer, must be included in all copies of the
20 # Software, in whole or in part, and all derivative works of
21 # the Software, unless such copies or derivative works are solely
22 # in the form of machine-executable object code generated by a
23 # source language processor.
24 #
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
26 # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
27 # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
28 # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
29 # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
30 # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
31 # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
32 # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
33 # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
34 # OR PROBABILITY OF SUCH DAMAGES.
35 #
36 # ==========================================================================
37 #+
38 
39 #
40 # Creation Date: 3 October 2006
41 #
42 
43 ########################################################################
44 # DESCRIPTION:
45 #
46 # Produces the dependency graph node "spTorusField".
47 #
48 # The spTorusField node implements an attraction-and-repel field.
49 #
50 # The field repels all objects between itself and repelDistance attribute
51 # and attracts objects greater than attractDistance attribute from itself.
52 # This will eventually result in the objects clustering in a torus shape
53 # around the field.
54 #
55 # See the torusFieldTest.py script for an example on using this node.
56 # The example creates a new field node and a particle object, and connects them together.
57 #
58 ########################################################################
59 
60 import math, sys
61 import maya.OpenMaya as OpenMaya
62 import maya.OpenMayaUI as OpenMayaUI
63 import maya.OpenMayaMPx as OpenMayaMPx
64 import maya.OpenMayaRender as OpenMayaRender
65 
66 kPluginName = "spTorusField"
67 kPluginNodeId = OpenMaya.MTypeId(0x87008)
68 
69 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
70 glFT = glRenderer.glFunctionTable()
71 
72 def statusError(msg):
73  sys.stderr.write("%s\n" % message)
74  raise # called from exception handlers only, reraise exception
75 
76 
77 # Node definition
78 class TorusField(OpenMayaMPx.MPxFieldNode):
79  # Attributes
80  # minimum distance from field at which repel is applied
81  #
82  aMinDistance = OpenMaya.MObject()
83 
84  # min distance from field at which the force attracts
85  #
86  aAttractDistance = OpenMaya.MObject()
87 
88  # max distance from field at which the force repels.
89  #
90  aRepelDistance = OpenMaya.MObject()
91 
92  # drag exerted on the attractRepel force.
93  #
94  aDrag = OpenMaya.MObject()
95 
96  # amplitude/magnitude of the swarm force.
97  #
98  aSwarmAmplitude = OpenMaya.MObject()
99 
100  # frequency of the swarm force.
101  #
102  aSwarmFrequency = OpenMaya.MObject()
103 
104  # phase of the swarm force.
105  #
106  aSwarmPhase = OpenMaya.MObject()
107 
108 
109  def __init__(self):
110  OpenMayaMPx.MPxFieldNode.__init__(self)
111 
112 
113  def compute(self, plug, block):
114  """
115  Compute output force.
116  """
117  outputForce = OpenMayaMPx.cvar.MPxFieldNode_mOutputForce
118  if not (plug == outputForce):
119  return
120 
121  # get the logical index of the element this plug refers to.
122  #
123  try:
124  multiIndex = plug.logicalIndex()
125  except:
126  statusError("ERROR in plug.logicalIndex.")
127 
128  # Get input data handle, use outputArrayValue since we do not
129  # want to evaluate both inputs, only the one related to the
130  # requested multiIndex. Evaluating both inputs at once would cause
131  # a dependency graph loop.
132  #
133  inputData = OpenMayaMPx.cvar.MPxFieldNode_mInputData
134  try:
135  hInputArray = block.outputArrayValue(inputData)
136  except:
137  statusError("ERROR in hInputArray = block.outputArrayValue().")
138 
139  try:
140  hInputArray.jumpToElement(multiIndex)
141  except:
142  statusError("ERROR: hInputArray.jumpToElement failed.")
143 
144  # get children of aInputData.
145  #
146  try:
147  hCompond = hInputArray.inputValue()
148  except:
149  statusError("ERROR in hCompond=hInputArray.inputValue")
150 
151  inputPositions = OpenMayaMPx.cvar.MPxFieldNode_mInputPositions
152  hPosition = hCompond.child(inputPositions)
153  dPosition = hPosition.data()
154  fnPosition = OpenMaya.MFnVectorArrayData(dPosition)
155  try:
156  points = fnPosition.array()
157  except:
158  statusError("ERROR in fnPosition.array(), not find points.")
159 
160  inputVelocities = OpenMayaMPx.cvar.MPxFieldNode_mInputVelocities
161  hVelocity = hCompond.child(inputVelocities)
162  dVelocity = hVelocity.data()
163  fnVelocity = OpenMaya.MFnVectorArrayData(dVelocity)
164  try:
165  velocities = fnVelocity.array()
166  except:
167  statusError("ERROR in fnVelocity.array(), not find velocities.")
168 
169  inputMass = OpenMayaMPx.cvar.MPxFieldNode_mInputMass
170  hMass = hCompond.child(inputMass)
171  dMass = hMass.data()
172  fnMass = OpenMaya.MFnDoubleArrayData(dMass)
173  try:
174  masses = fnMass.array()
175  except:
176  statusError("ERROR in fnMass.array(), not find masses.")
177 
178  # Compute the output force.
179  #
180  forceArray = OpenMaya.MVectorArray()
181  useMaxDistSet = self.__useMaxDistanceValue(block)
182  if useMaxDistSet:
183  self.__applyMaxDist(block, points, velocities, masses, forceArray)
184  else:
185  self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
186 
187  # get output data handle
188  #
189  try:
190  hOutArray = block.outputArrayValue(outputForce)
191  except:
192  statusError("ERROR in hOutArray = block.outputArrayValue.")
193  try:
194  bOutArray = hOutArray.builder()
195  except:
196  statusError("ERROR in bOutArray = hOutArray.builder.")
197 
198  # get output force array from block.
199  #
200  try:
201  hOut = bOutArray.addElement(multiIndex)
202  except:
203  statusError("ERROR in hOut = bOutArray.addElement.")
204 
205  fnOutputForce = OpenMaya.MFnVectorArrayData()
206  try:
207  dOutputForce = fnOutputForce.create(forceArray)
208  except:
209  statusError("ERROR in dOutputForce = fnOutputForce.create")
210 
211  # update data block with new output force data.
212  #
213  hOut.setMObject(dOutputForce)
214  block.setClean(plug)
215 
216 
217  def draw (self, view, path, style, status):
218  """
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
220  """
221  TORUS_PI = 3.14159265
222  TORUS_2PI = 2.0 * TORUS_PI
223  EDGES = 30
224  SEGMENTS = 20
225 
226  view.beginGL()
227  for j in range(SEGMENTS):
228  glFT.glPushMatrix()
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))
237  glFT.glEnd()
238  glFT.glPopMatrix()
239  view.endGL()
240 
241 
242  def getForceAtPoint(self, points, velocities, masses, forceArray, deltaTime):
243  """
244  This method is not required to be overridden, it is only necessary
245  for compatibility with the MFnField function set.
246  """
247  block = forceCache()
248  useMaxDistSet = self.__useMaxDistanceValue(block)
249  if useMaxDistSet:
250  self.__applyMaxDist(block, points, velocities, masses, forceArray)
251  else:
252  self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
253 
254 
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 )
260 
261 
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 )
271 
272 
273  # methods to compute output force.
274  #
275  def __applyNoMaxDist(self, block, points, velocities, masses, outputForce):
276  """
277  Compute output force in the case that the useMaxDistance is not set.
278  """
279  # points and velocities should have the same length. If not return.
280  #
281  if points.length() != velocities.length():
282  return
283 
284  # clear the output force array
285  #
286  outputForce.clear()
287 
288  # get field parameters.
289  #
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)
296 
297  # get owner's data. posArray may have only one point which is the centroid
298  # (if this has owner) or field position(if without owner). Or it may have
299  # a list of points if with owner and applyPerVertex.
300  #
301  posArray = self.__ownerPosition(block)
302 
303  fieldPosCount = posArray.length()
304  receptorSize = points.length()
305 
306  # With this model,if max distance isn't set then we
307  # also don't attenuate, because 1 - dist/maxDist isn't
308  # meaningful. No max distance and no attenuation.
309  #
310  for ptIndex in range(receptorSize):
311  forceV = OpenMaya.MVector(0.0,0.0,0.0)
312  receptorPoint = points[ptIndex]
313 
314  # Apply from every field position to every receptor position.
315  #
316  distance = 0.0
317  for i in range(fieldPosCount-1, -1, -1):
318  difference = receptorPoint - posArray[i]
319  distance = difference.length()
320 
321  if distance < minDist:
322  continue
323 
324  if distance <= repelDist:
325  forceV += difference * magValue
326  elif distance >= attractDist:
327  forceV += -difference * magValue
328 
329  # Apply drag/swarm only if the object is inside the zone
330  # the repulsion-attraction is pushing the object to.
331  #
332  if distance >= repelDist and distance <= attractDist:
333  if dragMag > 0:
334  if fieldPosCount > 0:
335  dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
336  forceV += dragForceV
337 
338  if swarmAmp > 0:
339  frequency = self.__swarmFrequencyValue(block)
340  phase = OpenMaya.MVector(0.0, 0.0, frequency)
341 
342  # Add swarm in here
343  #
344  for i in range(fieldPosCount-1, -1, -1):
345  difference = receptorPoint - posArray[i]
346  difference = (difference + phase) * frequency
347 
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) ):
355  continue
356 
357  noiseOut = self.__noiseFunction(noiseEffect)
358  swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
359  noiseOut[1] * swarmAmp,
360  noiseOut[2] * swarmAmp)
361  forceV += swarmForce
362 
363  outputForce.append(forceV)
364 
365 
366  def __applyMaxDist(self, block, points, velocities, masses, outputForce):
367  """
368  Compute output force in the case that the useMaxDistance is set.
369  """
370  # points and velocities should have the same length. If not return.
371  #
372  if points.length() != velocities.length():
373  return
374 
375  # clear the output force array.
376  #
377  outputForce.clear()
378 
379  # get field parameters.
380  #
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)
389 
390  # get owner's data. posArray may have only one point which is the centroid
391  # (if this has owner) or field position(if without owner). Or it may have
392  # a list of points if with owner and applyPerVertex.
393  #
394  posArray = self.__ownerPosition(block)
395 
396  fieldPosCount = posArray.length()
397  receptorSize = points.length()
398 
399  for ptIndex in range(receptorSize):
400  receptorPoint = points[ptIndex]
401 
402  # Apply from every field position to every receptor position.
403  #
404  forceV = OpenMaya.MVector(0,0,0)
405  sumForceV = OpenMaya.MVector(0,0,0)
406  for i in range(fieldPosCount-1, -1, -1):
407  difference = receptorPoint-posArray[i]
408  distance = difference.length()
409 
410  if (distance < minDist or distance > maxDist):
411  continue
412 
413  if attenValue > 0.0:
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
420 
421  # Apply drag and swarm if the object is inside
422  # the zone the repulsion-attraction is pushing the
423  # object to, and if they are set.
424  #
425  if distance >= repelDist and distance <= attractDist:
426  if dragMag > 0:
427  if fieldPosCount > 0:
428  dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
429  forceV += dragForceV
430 
431  if swarmAmp > 0:
432  frequency = self.__swarmFrequencyValue(block)
433  phase = OpenMaya.MVector(0.0, 0.0, frequency)
434 
435  # Add swarm in here
436  #
437  for i in range(fieldPosCount-1, -1, -1):
438  difference = receptorPoint - posArray[i]
439  difference = (difference + phase) * frequency
440 
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) ):
448  continue
449 
450  noiseOut = self.__noiseFunction(noiseEffect)
451  swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
452  noiseOut[1] * swarmAmp,
453  noiseOut[2] * swarmAmp)
454  forceV += swarmForce
455 
456  if (maxDist > 0.0):
457  forceV *= self.falloffCurve(distance/maxDist)
458  sumForceV += forceV
459 
460  outputForce.append(sumForceV)
461 
462 
463  def __ownerPosition(self, block):
464  """
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.
469  """
470  ownerPosArray = OpenMaya.MVectorArray()
471  if self.__applyPerVertexValue(block):
472  ownerPos = OpenMayaMPx.cvar.MPxFieldNode_mOwnerPosData
473  try:
474  hOwnerPos = block.inputValue(ownerPos)
475  except:
476  # get the field position in the world space
477  # and add it into ownerPosArray.
478  #
479  worldPos = self.__getWorldPosition()
480  ownerPosArray.append(worldPos)
481  else:
482  dOwnerPos = hOwnerPos.data()
483  fnOwnerPos = OpenMaya.MFnVectorArrayData(dOwnerPos)
484  try:
485  posArray = fnOwnerPos.array()
486  except:
487  worldPos = self.__getWorldPosition()
488  ownerPosArray.append(worldPos)
489  else:
490  # assign vectors from block to ownerPosArray.
491  #
492  for i in range(posArray.length()):
493  ownerPosArray.append(posArray[i])
494  else:
495  try:
496  centroidV = self.__ownerCentroidValue(block)
497  except:
498  # get the field position in the world space.
499  #
500  worldPos = self.__getWorldPosition()
501  ownerPosArray.append(worldPos)
502  else:
503  ownerPosArray.append(centroidV)
504 
505  return ownerPosArray
506 
507 
508  def __getWorldPosition(self):
509  thisNode = self.thisMObject()
510  fnThisNode = OpenMaya.MFnDependencyNode(thisNode)
511 
512  # get worldMatrix attribute.
513  #
514  worldMatrixAttr = fnThisNode.attribute("worldMatrix")
515 
516  # build worldMatrix plug, and specify which element the plug refers to.
517  # We use the first element(the first dagPath of this field).
518  #
519  matrixPlug = OpenMaya.MPlug(thisNode, worldMatrixAttr)
520  matrixPlug = matrixPlug.elementByLogicalIndex(0)
521 
522  # Get the value of the 'worldMatrix' attribute
523  #
524  try:
525  matrixObject = matrixPlug.asMObject(matrixObject)
526  except:
527  statusError("TorusField.__getWorldPosition: get matrixObject")
528 
529  try:
530  worldMatrixData = OpenMaya.MFnMatrixData(matrixObject)
531  except:
532  statusError("TorusField.__getWorldPosition: get worldMatrixData")
533 
534  try:
535  worldMatrix = worldMatrixData.matrix()
536  except:
537  statusError("TorusField.__getWorldPosition: get worldMatrix")
538 
539  # assign the translate to the given vector.
540  #
541  return OpenMaya.MVector(worldMatrix(3, 0), worldMatrix(3, 1), worldMatrix(3, 2))
542 
543 
544  def __noiseFunction(self, inNoise):
545  """
546  A noise function
547  """
548  # Shared data for noise computation
549  #
550  xlim = [ [0,0], [0,0], [0,0] ] # integer bound for point
551  xarg = [0.0, 0.0, 0.0 ] # fractional part
552 
553  # Helper functions to compute noise
554  #
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))
559 
560  def frand(s):
561  seed = s << 13^s
562  return (1.0 - ((s*(s*s*15731+789221)+1376312589) & 0x7fffffff)/1073741824.0)
563 
564  def hermite(p0, p1, r0, r1, t):
565  t2 = t * t
566  t3 = t2 * t
567  _3t2 = 3.0 * t2
568  _2t3 = 2.0 * t3
569 
570  return (p0*(_2t3-_3t2+1) + p1*(-_2t3+_3t2) + r0*(t3-2.0*t2+t) + r1*(t3-t2))
571 
572  def interpolate(i, n):
573  f = [ 0.0, 0.0, 0.0, 0.0 ]
574  if n == 0: # at 0, return lattice value
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] )
579  else:
580  n -= 1
581  f0 = interpolate(i, n) # compute first half
582  f1 = interpolate(i | 1<<n, n) # compute second half
583 
584  # use linear interpolation for slopes
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]
588 
589  # use hermite interpolation for values
590  f[3] = hermite(f0[3], f1[3], f0[n], f1[n], xarg[n])
591 
592  return f
593 
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
600 
601  xarg[0] = inNoise[0] - xlim[0][0]
602  xarg[1] = inNoise[1] - xlim[1][0]
603  xarg[2] = inNoise[2] - xlim[2][0]
604 
605  return interpolate(0, 3)
606 
607 
608  # methods to get attribute value.
609  #
610  def __magnitudeValue(self, block):
611  magnitude = OpenMayaMPx.cvar.MPxFieldNode_mMagnitude
612  hValue = block.inputValue(magnitude)
613  return hValue.asDouble()
614 
615 
616  def __attenuationValue(self, block):
617  attenuation = OpenMayaMPx.cvar.MPxFieldNode_mAttenuation
618  hValue = block.inputValue(attenuation)
619  return hValue.asDouble()
620 
621 
622  def __maxDistanceValue(self, block):
623  maxDistance = OpenMayaMPx.cvar.MPxFieldNode_mMaxDistance
624  hValue = block.inputValue(maxDistance)
625  return hValue.asDouble()
626 
627 
628  def __useMaxDistanceValue(self, block):
629  useMaxDistance = OpenMayaMPx.cvar.MPxFieldNode_mUseMaxDistance
630  hValue = block.inputValue(useMaxDistance)
631  return hValue.asBool()
632 
633 
634  def __applyPerVertexValue(self, block):
635  applyPerVertex = OpenMayaMPx.cvar.MPxFieldNode_mApplyPerVertex
636  hValue = block.inputValue(applyPerVertex)
637  return hValue.asBool()
638 
639 
640  # methods to get attribute value of local attributes.
641  #
642  def __minDistanceValue(self, block):
643  hValue = block.inputValue(TorusField.aMinDistance)
644  return hValue.asDouble()
645 
646 
647  def __attractDistanceValue(self, block):
648  hValue = block.inputValue(TorusField.aAttractDistance)
649  return hValue.asDouble()
650 
651 
652  def __repelDistanceValue(self, block):
653  hValue = block.inputValue(TorusField.aRepelDistance)
654  return hValue.asDouble()
655 
656 
657  def __dragValue(self, block):
658  hValue = block.inputValue(TorusField.aDrag)
659  return hValue.asDouble()
660 
661 
662  def __swarmAmplitudeValue(self, block):
663  hValue = block.inputValue(TorusField.aSwarmAmplitude)
664  return hValue.asDouble()
665 
666 
667  def __swarmFrequencyValue(self, block):
668  hValue = block.inputValue(TorusField.aSwarmFrequency)
669  return hValue.asDouble()
670 
671 
672  def __swarmPhaseValue(self, block):
673  hValue = block.inputValue(TorusField.aSwarmPhase)
674  return hValue.asDouble()
675 
676 
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)
684  return OpenMaya.MVector(hValueX.asDouble(),
685  hValueY.asDouble(),
686  hValueZ.asDouble())
687 
688 
689 ############################################################################
690 
691 
692 # creator
693 def nodeCreator():
694  return OpenMayaMPx.asMPxPtr(TorusField())
695 
696 
697 # initializer
698 def nodeInitializer():
699  numAttr = OpenMaya.MFnNumericAttribute()
700 
701  # create the field basic attributes.
702  #
703  TorusField.aMinDistance = numAttr.create("minDistance", "mnd", OpenMaya.MFnNumericData.kDouble, 0.0)
704  numAttr.setKeyable(True)
705  try:
706  TorusField.addAttribute(TorusField.aMinDistance)
707  except:
708  statusError("ERROR adding aMinDistance attribute.")
709 
710  TorusField.aAttractDistance = numAttr.create("attractDistance", "ad", OpenMaya.MFnNumericData.kDouble, 20.0)
711  numAttr.setKeyable(True)
712  try:
713  TorusField.addAttribute(TorusField.aAttractDistance)
714  except:
715  statusError("ERROR adding aAttractDistance attribute.")
716 
717  TorusField.aRepelDistance = numAttr.create("repelDistance", "rd", OpenMaya.MFnNumericData.kDouble, 10.0)
718  numAttr.setKeyable(True)
719  try:
720  TorusField.addAttribute(TorusField.aRepelDistance)
721  except:
722  statusError("ERROR adding aRepelDistance attribute.")
723 
724  TorusField.aDrag = numAttr.create("drag", "d", OpenMaya.MFnNumericData.kDouble, 0.0)
725  numAttr.setKeyable(True)
726  try:
727  TorusField.addAttribute(TorusField.aDrag)
728  except:
729  statusError("ERROR adding aDrag attribute.")
730 
731  TorusField.aSwarmAmplitude = numAttr.create("swarmAmplitude", "samp", OpenMaya.MFnNumericData.kDouble, 0.0)
732  numAttr.setKeyable(True)
733  try:
734  TorusField.addAttribute(TorusField.aSwarmAmplitude)
735  except:
736  statusError("ERROR adding aSwarmAmplitude attribute.")
737 
738  TorusField.aSwarmFrequency = numAttr.create("swarmFrequency", "sfrq", OpenMaya.MFnNumericData.kDouble, 1.0)
739  numAttr.setKeyable(True)
740  try:
741  TorusField.addAttribute(TorusField.aSwarmFrequency)
742  except:
743  statusError("ERROR adding aSwarmFrequency attribute.")
744 
745  TorusField.aSwarmPhase = numAttr.create("swarmPhase", "sa", OpenMaya.MFnNumericData.kDouble, 0.0)
746  numAttr.setKeyable(True)
747  try:
748  TorusField.addAttribute(TorusField.aSwarmPhase)
749  except:
750  statusError("ERROR adding aSwarmPhase attribute.")
751 
752 
753 # initialize the script plug-in
754 def initializePlugin(mobject):
755  mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
756  try:
757  mplugin.registerNode(kPluginName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kFieldNode)
758  except:
759  statusError("Failed to register node: %s" % kPluginName)
760 
761 
762 # uninitialize the script plug-in
763 def uninitializePlugin(mobject):
764  mplugin = OpenMayaMPx.MFnPlugin(mobject)
765  try:
766  mplugin.deregisterNode(kPluginNodeId)
767  except:
768  statusError("Failed to deregister node: %s" % kPluginName)