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 # Example Plugin: torusField.py
43 #
44 # Description
45 # The torusField node implements an attraction-and-repel field.
46 #
47 # The field repels all objects between itself and repelDistance attribute
48 # and attracts objects greater than attractDistance attribute from itself.
49 # This will eventually result in the objects clustering
50 # in a torus shape around the field.
51 #
52 # See torusFieldTest.py for usage
53 #
54 
55 import math, sys
56 import maya.OpenMaya as OpenMaya
57 import maya.OpenMayaUI as OpenMayaUI
58 import maya.OpenMayaMPx as OpenMayaMPx
59 import maya.OpenMayaRender as OpenMayaRender
60 
61 kPluginName = "spTorusField"
62 kPluginNodeId = OpenMaya.MTypeId(0x87008)
63 
64 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
65 glFT = glRenderer.glFunctionTable()
66 
67 def statusError(msg):
68  sys.stderr.write("%s\n" % message)
69  raise # called from exception handlers only, reraise exception
70 
71 
72 # Node definition
73 class TorusField(OpenMayaMPx.MPxFieldNode):
74  # Attributes
75  # minimum distance from field at which repel is applied
76  #
77  aMinDistance = OpenMaya.MObject()
78 
79  # min distance from field at which the force attracts
80  #
81  aAttractDistance = OpenMaya.MObject()
82 
83  # max distance from field at which the force repels.
84  #
85  aRepelDistance = OpenMaya.MObject()
86 
87  # drag exerted on the attractRepel force.
88  #
89  aDrag = OpenMaya.MObject()
90 
91  # amplitude/magnitude of the swarm force.
92  #
93  aSwarmAmplitude = OpenMaya.MObject()
94 
95  # frequency of the swarm force.
96  #
97  aSwarmFrequency = OpenMaya.MObject()
98 
99  # phase of the swarm force.
100  #
101  aSwarmPhase = OpenMaya.MObject()
102 
103 
104  def __init__(self):
105  OpenMayaMPx.MPxFieldNode.__init__(self)
106 
107 
108  def compute(self, plug, block):
109  """
110  Compute output force.
111  """
112  outputForce = OpenMayaMPx.cvar.MPxFieldNode_mOutputForce
113  if not (plug == outputForce):
114  return
115 
116  # get the logical index of the element this plug refers to.
117  #
118  try:
119  multiIndex = plug.logicalIndex()
120  except:
121  statusError("ERROR in plug.logicalIndex.")
122 
123  # Get input data handle, use outputArrayValue since we do not
124  # want to evaluate both inputs, only the one related to the
125  # requested multiIndex. Evaluating both inputs at once would cause
126  # a dependency graph loop.
127  #
128  inputData = OpenMayaMPx.cvar.MPxFieldNode_mInputData
129  try:
130  hInputArray = block.outputArrayValue(inputData)
131  except:
132  statusError("ERROR in hInputArray = block.outputArrayValue().")
133 
134  try:
135  hInputArray.jumpToElement(multiIndex)
136  except:
137  statusError("ERROR: hInputArray.jumpToElement failed.")
138 
139  # get children of aInputData.
140  #
141  try:
142  hCompond = hInputArray.inputValue()
143  except:
144  statusError("ERROR in hCompond=hInputArray.inputValue")
145 
146  inputPositions = OpenMayaMPx.cvar.MPxFieldNode_mInputPositions
147  hPosition = hCompond.child(inputPositions)
148  dPosition = hPosition.data()
149  fnPosition = OpenMaya.MFnVectorArrayData(dPosition)
150  try:
151  points = fnPosition.array()
152  except:
153  statusError("ERROR in fnPosition.array(), not find points.")
154 
155  inputVelocities = OpenMayaMPx.cvar.MPxFieldNode_mInputVelocities
156  hVelocity = hCompond.child(inputVelocities)
157  dVelocity = hVelocity.data()
158  fnVelocity = OpenMaya.MFnVectorArrayData(dVelocity)
159  try:
160  velocities = fnVelocity.array()
161  except:
162  statusError("ERROR in fnVelocity.array(), not find velocities.")
163 
164  inputMass = OpenMayaMPx.cvar.MPxFieldNode_mInputMass
165  hMass = hCompond.child(inputMass)
166  dMass = hMass.data()
167  fnMass = OpenMaya.MFnDoubleArrayData(dMass)
168  try:
169  masses = fnMass.array()
170  except:
171  statusError("ERROR in fnMass.array(), not find masses.")
172 
173  # Compute the output force.
174  #
175  forceArray = OpenMaya.MVectorArray()
176  useMaxDistSet = self.__useMaxDistanceValue(block)
177  if useMaxDistSet:
178  self.__applyMaxDist(block, points, velocities, masses, forceArray)
179  else:
180  self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
181 
182  # get output data handle
183  #
184  try:
185  hOutArray = block.outputArrayValue(outputForce)
186  except:
187  statusError("ERROR in hOutArray = block.outputArrayValue.")
188  try:
189  bOutArray = hOutArray.builder()
190  except:
191  statusError("ERROR in bOutArray = hOutArray.builder.")
192 
193  # get output force array from block.
194  #
195  try:
196  hOut = bOutArray.addElement(multiIndex)
197  except:
198  statusError("ERROR in hOut = bOutArray.addElement.")
199 
200  fnOutputForce = OpenMaya.MFnVectorArrayData()
201  try:
202  dOutputForce = fnOutputForce.create(forceArray)
203  except:
204  statusError("ERROR in dOutputForce = fnOutputForce.create")
205 
206  # update data block with new output force data.
207  #
208  hOut.setMObject(dOutputForce)
209  block.setClean(plug)
210 
211 
212  def draw (self, view, path, style, status):
213  """
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
215  """
216  TORUS_PI = 3.14159265
217  TORUS_2PI = 2.0 * TORUS_PI
218  EDGES = 30
219  SEGMENTS = 20
220 
221  view.beginGL()
222  for j in range(SEGMENTS):
223  glFT.glPushMatrix()
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))
232  glFT.glEnd()
233  glFT.glPopMatrix()
234  view.endGL()
235 
236 
237  def getForceAtPoint(self, points, velocities, masses, forceArray, deltaTime):
238  """
239  This method is not required to be overridden, it is only necessary
240  for compatibility with the MFnField function set.
241  """
242  block = forceCache()
243  useMaxDistSet = self.__useMaxDistanceValue(block)
244  if useMaxDistSet:
245  self.__applyMaxDist(block, points, velocities, masses, forceArray)
246  else:
247  self.__applyNoMaxDist(block, points, velocities, masses, forceArray)
248 
249 
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 )
255 
256 
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 )
266 
267 
268  # methods to compute output force.
269  #
270  def __applyNoMaxDist(self, block, points, velocities, masses, outputForce):
271  """
272  Compute output force in the case that the useMaxDistance is not set.
273  """
274  # points and velocities should have the same length. If not return.
275  #
276  if points.length() != velocities.length():
277  return
278 
279  # clear the output force array
280  #
281  outputForce.clear()
282 
283  # get field parameters.
284  #
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)
291 
292  # get owner's data. posArray may have only one point which is the centroid
293  # (if this has owner) or field position(if without owner). Or it may have
294  # a list of points if with owner and applyPerVertex.
295  #
296  posArray = self.__ownerPosition(block)
297 
298  fieldPosCount = posArray.length()
299  receptorSize = points.length()
300 
301  # With this model,if max distance isn't set then we
302  # also don't attenuate, because 1 - dist/maxDist isn't
303  # meaningful. No max distance and no attenuation.
304  #
305  for ptIndex in range(receptorSize):
306  forceV = OpenMaya.MVector(0.0,0.0,0.0)
307  receptorPoint = points[ptIndex]
308 
309  # Apply from every field position to every receptor position.
310  #
311  distance = 0.0
312  for i in range(fieldPosCount-1, -1, -1):
313  difference = receptorPoint - posArray[i]
314  distance = difference.length()
315 
316  if distance < minDist:
317  continue
318 
319  if distance <= repelDist:
320  forceV += difference * magValue
321  elif distance >= attractDist:
322  forceV += -difference * magValue
323 
324  # Apply drag/swarm only if the object is inside the zone
325  # the repulsion-attraction is pushing the object to.
326  #
327  if distance >= repelDist and distance <= attractDist:
328  if dragMag > 0:
329  if fieldPosCount > 0:
330  dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
331  forceV += dragForceV
332 
333  if swarmAmp > 0:
334  frequency = self.__swarmFrequencyValue(block)
335  phase = OpenMaya.MVector(0.0, 0.0, frequency)
336 
337  # Add swarm in here
338  #
339  for i in range(fieldPosCount-1, -1, -1):
340  difference = receptorPoint - posArray[i]
341  difference = (difference + phase) * frequency
342 
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) ):
350  continue
351 
352  noiseOut = self.__noiseFunction(noiseEffect)
353  swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
354  noiseOut[1] * swarmAmp,
355  noiseOut[2] * swarmAmp)
356  forceV += swarmForce
357 
358  outputForce.append(forceV)
359 
360 
361  def __applyMaxDist(self, block, points, velocities, masses, outputForce):
362  """
363  Compute output force in the case that the useMaxDistance is set.
364  """
365  # points and velocities should have the same length. If not return.
366  #
367  if points.length() != velocities.length():
368  return
369 
370  # clear the output force array.
371  #
372  outputForce.clear()
373 
374  # get field parameters.
375  #
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)
384 
385  # get owner's data. posArray may have only one point which is the centroid
386  # (if this has owner) or field position(if without owner). Or it may have
387  # a list of points if with owner and applyPerVertex.
388  #
389  posArray = self.__ownerPosition(block)
390 
391  fieldPosCount = posArray.length()
392  receptorSize = points.length()
393 
394  for ptIndex in range(receptorSize):
395  receptorPoint = points[ptIndex]
396 
397  # Apply from every field position to every receptor position.
398  #
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()
404 
405  if (distance < minDist or distance > maxDist):
406  continue
407 
408  if attenValue > 0.0:
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
415 
416  # Apply drag and swarm if the object is inside
417  # the zone the repulsion-attraction is pushing the
418  # object to, and if they are set.
419  #
420  if distance >= repelDist and distance <= attractDist:
421  if dragMag > 0:
422  if fieldPosCount > 0:
423  dragForceV = velocities[ptIndex] * (-dragMag) * fieldPosCount
424  forceV += dragForceV
425 
426  if swarmAmp > 0:
427  frequency = self.__swarmFrequencyValue(block)
428  phase = OpenMaya.MVector(0.0, 0.0, frequency)
429 
430  # Add swarm in here
431  #
432  for i in range(fieldPosCount-1, -1, -1):
433  difference = receptorPoint - posArray[i]
434  difference = (difference + phase) * frequency
435 
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) ):
443  continue
444 
445  noiseOut = self.__noiseFunction(noiseEffect)
446  swarmForce = OpenMaya.MVector(noiseOut[0] * swarmAmp,
447  noiseOut[1] * swarmAmp,
448  noiseOut[2] * swarmAmp)
449  forceV += swarmForce
450 
451  if (maxDist > 0.0):
452  forceV *= self.falloffCurve(distance/maxDist)
453  sumForceV += forceV
454 
455  outputForce.append(sumForceV)
456 
457 
458  def __ownerPosition(self, block):
459  """
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.
464  """
465  ownerPosArray = OpenMaya.MVectorArray()
466  if self.__applyPerVertexValue(block):
467  ownerPos = OpenMayaMPx.cvar.MPxFieldNode_mOwnerPosData
468  try:
469  hOwnerPos = block.inputValue(ownerPos)
470  except:
471  # get the field position in the world space
472  # and add it into ownerPosArray.
473  #
474  worldPos = self.__getWorldPosition()
475  ownerPosArray.append(worldPos)
476  else:
477  dOwnerPos = hOwnerPos.data()
478  fnOwnerPos = OpenMaya.MFnVectorArrayData(dOwnerPos)
479  try:
480  posArray = fnOwnerPos.array()
481  except:
482  worldPos = self.__getWorldPosition()
483  ownerPosArray.append(worldPos)
484  else:
485  # assign vectors from block to ownerPosArray.
486  #
487  for i in range(posArray.length()):
488  ownerPosArray.append(posArray[i])
489  else:
490  try:
491  centroidV = self.__ownerCentroidValue(block)
492  except:
493  # get the field position in the world space.
494  #
495  worldPos = self.__getWorldPosition()
496  ownerPosArray.append(worldPos)
497  else:
498  ownerPosArray.append(centroidV)
499 
500  return ownerPosArray
501 
502 
503  def __getWorldPosition(self):
504  thisNode = self.thisMObject()
505  fnThisNode = OpenMaya.MFnDependencyNode(thisNode)
506 
507  # get worldMatrix attribute.
508  #
509  worldMatrixAttr = fnThisNode.attribute("worldMatrix")
510 
511  # build worldMatrix plug, and specify which element the plug refers to.
512  # We use the first element(the first dagPath of this field).
513  #
514  matrixPlug = OpenMaya.MPlug(thisNode, worldMatrixAttr)
515  matrixPlug = matrixPlug.elementByLogicalIndex(0)
516 
517  # Get the value of the 'worldMatrix' attribute
518  #
519  try:
520  matrixObject = matrixPlug.asMObject(matrixObject)
521  except:
522  statusError("TorusField.__getWorldPosition: get matrixObject")
523 
524  try:
525  worldMatrixData = OpenMaya.MFnMatrixData(matrixObject)
526  except:
527  statusError("TorusField.__getWorldPosition: get worldMatrixData")
528 
529  try:
530  worldMatrix = worldMatrixData.matrix()
531  except:
532  statusError("TorusField.__getWorldPosition: get worldMatrix")
533 
534  # assign the translate to the given vector.
535  #
536  return OpenMaya.MVector(worldMatrix(3, 0), worldMatrix(3, 1), worldMatrix(3, 2))
537 
538 
539  def __noiseFunction(self, inNoise):
540  """
541  A noise function
542  """
543  # Shared data for noise computation
544  #
545  xlim = [ [0,0], [0,0], [0,0] ] # integer bound for point
546  xarg = [0.0, 0.0, 0.0 ] # fractional part
547 
548  # Helper functions to compute noise
549  #
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))
554 
555  def frand(s):
556  seed = s << 13^s
557  return (1.0 - ((s*(s*s*15731+789221)+1376312589) & 0x7fffffff)/1073741824.0)
558 
559  def hermite(p0, p1, r0, r1, t):
560  t2 = t * t
561  t3 = t2 * t
562  _3t2 = 3.0 * t2
563  _2t3 = 2.0 * t3
564 
565  return (p0*(_2t3-_3t2+1) + p1*(-_2t3+_3t2) + r0*(t3-2.0*t2+t) + r1*(t3-t2))
566 
567  def interpolate(i, n):
568  f = [ 0.0, 0.0, 0.0, 0.0 ]
569  if n == 0: # at 0, return lattice value
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] )
574  else:
575  n -= 1
576  f0 = interpolate(i, n) # compute first half
577  f1 = interpolate(i | 1<<n, n) # compute second half
578 
579  # use linear interpolation for slopes
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]
583 
584  # use hermite interpolation for values
585  f[3] = hermite(f0[3], f1[3], f0[n], f1[n], xarg[n])
586 
587  return f
588 
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
595 
596  xarg[0] = inNoise[0] - xlim[0][0]
597  xarg[1] = inNoise[1] - xlim[1][0]
598  xarg[2] = inNoise[2] - xlim[2][0]
599 
600  return interpolate(0, 3)
601 
602 
603  # methods to get attribute value.
604  #
605  def __magnitudeValue(self, block):
606  magnitude = OpenMayaMPx.cvar.MPxFieldNode_mMagnitude
607  hValue = block.inputValue(magnitude)
608  return hValue.asDouble()
609 
610 
611  def __attenuationValue(self, block):
612  attenuation = OpenMayaMPx.cvar.MPxFieldNode_mAttenuation
613  hValue = block.inputValue(attenuation)
614  return hValue.asDouble()
615 
616 
617  def __maxDistanceValue(self, block):
618  maxDistance = OpenMayaMPx.cvar.MPxFieldNode_mMaxDistance
619  hValue = block.inputValue(maxDistance)
620  return hValue.asDouble()
621 
622 
623  def __useMaxDistanceValue(self, block):
624  useMaxDistance = OpenMayaMPx.cvar.MPxFieldNode_mUseMaxDistance
625  hValue = block.inputValue(useMaxDistance)
626  return hValue.asBool()
627 
628 
629  def __applyPerVertexValue(self, block):
630  applyPerVertex = OpenMayaMPx.cvar.MPxFieldNode_mApplyPerVertex
631  hValue = block.inputValue(applyPerVertex)
632  return hValue.asBool()
633 
634 
635  # methods to get attribute value of local attributes.
636  #
637  def __minDistanceValue(self, block):
638  hValue = block.inputValue(TorusField.aMinDistance)
639  return hValue.asDouble()
640 
641 
642  def __attractDistanceValue(self, block):
643  hValue = block.inputValue(TorusField.aAttractDistance)
644  return hValue.asDouble()
645 
646 
647  def __repelDistanceValue(self, block):
648  hValue = block.inputValue(TorusField.aRepelDistance)
649  return hValue.asDouble()
650 
651 
652  def __dragValue(self, block):
653  hValue = block.inputValue(TorusField.aDrag)
654  return hValue.asDouble()
655 
656 
657  def __swarmAmplitudeValue(self, block):
658  hValue = block.inputValue(TorusField.aSwarmAmplitude)
659  return hValue.asDouble()
660 
661 
662  def __swarmFrequencyValue(self, block):
663  hValue = block.inputValue(TorusField.aSwarmFrequency)
664  return hValue.asDouble()
665 
666 
667  def __swarmPhaseValue(self, block):
668  hValue = block.inputValue(TorusField.aSwarmPhase)
669  return hValue.asDouble()
670 
671 
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(),
680  hValueY.asDouble(),
681  hValueZ.asDouble())
682 
683 
684 ############################################################################
685 
686 
687 # creator
688 def nodeCreator():
689  return OpenMayaMPx.asMPxPtr(TorusField())
690 
691 
692 # initializer
693 def nodeInitializer():
694  numAttr = OpenMaya.MFnNumericAttribute()
695 
696  # create the field basic attributes.
697  #
698  TorusField.aMinDistance = numAttr.create("minDistance", "mnd", OpenMaya.MFnNumericData.kDouble, 0.0)
699  numAttr.setKeyable(True)
700  try:
701  TorusField.addAttribute(TorusField.aMinDistance)
702  except:
703  statusError("ERROR adding aMinDistance attribute.")
704 
705  TorusField.aAttractDistance = numAttr.create("attractDistance", "ad", OpenMaya.MFnNumericData.kDouble, 20.0)
706  numAttr.setKeyable(True)
707  try:
708  TorusField.addAttribute(TorusField.aAttractDistance)
709  except:
710  statusError("ERROR adding aAttractDistance attribute.")
711 
712  TorusField.aRepelDistance = numAttr.create("repelDistance", "rd", OpenMaya.MFnNumericData.kDouble, 10.0)
713  numAttr.setKeyable(True)
714  try:
715  TorusField.addAttribute(TorusField.aRepelDistance)
716  except:
717  statusError("ERROR adding aRepelDistance attribute.")
718 
719  TorusField.aDrag = numAttr.create("drag", "d", OpenMaya.MFnNumericData.kDouble, 0.0)
720  numAttr.setKeyable(True)
721  try:
722  TorusField.addAttribute(TorusField.aDrag)
723  except:
724  statusError("ERROR adding aDrag attribute.")
725 
726  TorusField.aSwarmAmplitude = numAttr.create("swarmAmplitude", "samp", OpenMaya.MFnNumericData.kDouble, 0.0)
727  numAttr.setKeyable(True)
728  try:
729  TorusField.addAttribute(TorusField.aSwarmAmplitude)
730  except:
731  statusError("ERROR adding aSwarmAmplitude attribute.")
732 
733  TorusField.aSwarmFrequency = numAttr.create("swarmFrequency", "sfrq", OpenMaya.MFnNumericData.kDouble, 1.0)
734  numAttr.setKeyable(True)
735  try:
736  TorusField.addAttribute(TorusField.aSwarmFrequency)
737  except:
738  statusError("ERROR adding aSwarmFrequency attribute.")
739 
740  TorusField.aSwarmPhase = numAttr.create("swarmPhase", "sa", OpenMaya.MFnNumericData.kDouble, 0.0)
741  numAttr.setKeyable(True)
742  try:
743  TorusField.addAttribute(TorusField.aSwarmPhase)
744  except:
745  statusError("ERROR adding aSwarmPhase attribute.")
746 
747 
748 # initialize the script plug-in
749 def initializePlugin(mobject):
750  mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
751  try:
752  mplugin.registerNode(kPluginName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kFieldNode)
753  except:
754  statusError("Failed to register node: %s" % kPluginName)
755 
756 
757 # uninitialize the script plug-in
758 def uninitializePlugin(mobject):
759  mplugin = OpenMayaMPx.MFnPlugin(mobject)
760  try:
761  mplugin.deregisterNode(kPluginNodeId)
762  except:
763  statusError("Failed to deregister node: %s" % kPluginName)