Python API 2.0 Reference
python/api1/py1SimpleEmitter.py
1 from __future__ import division
2 #-
3 # ==========================================================================
4 # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All
5 # rights reserved.
6 #
7 # The coded instructions, statements, computer programs, and/or related
8 # material (collectively the "Data") in these files contain unpublished
9 # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
10 # licensors, which is protected by U.S. and Canadian federal copyright
11 # law and by international treaties.
12 #
13 # The Data is provided for use exclusively by You. You have the right
14 # to use, modify, and incorporate this Data into other products for
15 # purposes authorized by the Autodesk software license agreement,
16 # without fee.
17 #
18 # The copyright notices in the Software and this entire statement,
19 # including the above license grant, this restriction and the
20 # following disclaimer, must be included in all copies of the
21 # Software, in whole or in part, and all derivative works of
22 # the Software, unless such copies or derivative works are solely
23 # in the form of machine-executable object code generated by a
24 # source language processor.
25 #
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
27 # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
28 # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
29 # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
30 # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
31 # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
32 # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
33 # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
34 # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
35 # OR PROBABILITY OF SUCH DAMAGES.
36 #
37 # ==========================================================================
38 #+
39 
40 ########################################################################
41 # DESCRIPTION:
42 #
43 # Produces the dependency graph node "simpleEmitter".
44 #
45 # This node is an example of a particle emitter that emits in a direction
46 # from a single position.
47 #
48 ########################################################################
49 
50 from builtins import range
51 import maya.OpenMaya as OpenMaya
52 import maya.OpenMayaMPx as OpenMayaMPx
53 import maya.OpenMayaRender as OpenMayaRender
54 import math
55 import random
56 import sys
57 
58 kSimpleEmitterNodeName = "py1SimpleEmitter"
59 kSimpleEmitterNodeID = OpenMaya.MTypeId(0x00080056)
60 
61 SEGMENTS = 20
62 EDGES = 30
63 TORUS_PI = 3.14159265
64 TORUS_2PI = 2.0 * TORUS_PI
65 
66 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
67 glFT = glRenderer.glFunctionTable()
68 
69 class simpleEmitter(OpenMayaMPx.MPxEmitterNode):
70  def __init__(self):
71  OpenMayaMPx.MPxEmitterNode.__init__(self)
72 
73  def isFullValue( self, plugIndex, block ):
74  value = 1
75  mIsFull = OpenMayaMPx.cvar.MPxEmitterNode_mIsFull
76 
77  try:
78  mhValue = block.inputArrayValue( mIsFull )
79  mhValue.jumpToElement( plugIndex )
80  hValue = mhValue.inputValue( )
81  value = hValue.asBool()
82  except:
83  sys.stderr.write("Error getting the input array value\n")
84  raise
85  return value
86 
87 
88  def getWorldPosition( self, point ):
89  try:
90  thisNode = simpleEmitter.thisMObject( self )
91  fnThisNode = OpenMaya.MFnDependencyNode(thisNode)
92 
93  worldMatrixAttr = fnThisNode.attribute( "worldMatrix" )
94 
95  matrixPlug = OpenMaya.MPlug( thisNode, worldMatrixAttr )
96  matrixPlug = matrixPlug.elementByLogicalIndex( 0 )
97 
98  matrixObject = OpenMaya.MObject()
99  matrixObject = matrixPlug.asMObject( )
100 
101  worldMatrixData = OpenMaya.MFnMatrixData( matrixObject )
102  worldMatrix = worldMatrixData.matrix( )
103 
104  point.x = worldMatrix( 3, 0 )
105  point.y = worldMatrix( 3, 1 )
106  point.z = worldMatrix( 3, 2 )
107  except:
108  sys.stderr.write( "Error in getWorldPosition\n" )
109  raise
110 
111  def currentTimeValue( self, block ):
112  try:
113  mCurrentTime = OpenMayaMPx.cvar.MPxEmitterNode_mCurrentTime
114  hValue = block.inputValue( mCurrentTime )
115  value = hValue.asTime()
116  except:
117  sys.stderr.write("Error getting current time value, returning 0")
118  value = OpenMaya.MTime(0.0)
119  return value
120 
121  def startTimeValue( self, plugIndex, block ):
122  try:
123  mStartTime = OpenMayaMPx.cvar.MPxEmitterNode_mStartTime
124  mhValue = block.inputArrayValue( mStartTime )
125  mhValue.jumpToElement( plugIndex )
126  hValue = mhValue.inputValue( )
127  value = hValue.asTime( )
128  except:
129  sys.stderr.write("Error getting start time value, setting to 0")
130  value = OpenMaya.MTime(0.0)
131  return value
132 
133  def deltaTimeValue( self, plugIndex, block ):
134  try:
135  mDeltaTime = OpenMayaMPx.cvar.MPxEmitterNode_mDeltaTime
136 
137  mhValue = block.inputArrayValue( mDeltaTime )
138  mhValue.jumpToElement( plugIndex )
139 
140  hValue = mhValue.inputValue()
141  value = hValue.asTime()
142  except:
143  sys.stderr.write("Error getting delta time value, setting to 0\n")
144  value = OpenMaya.MTime(0.0)
145  return value
146 
147  def rateValue( self, block ):
148  try:
149  mRate = OpenMayaMPx.cvar.MPxEmitterNode_mRate
150  hValue = block.inputValue( mRate )
151  value = hValue.asDouble()
152  except:
153  sys.stderr.write("Error getting rate value, setting to 0\n")
154  value = 0.0
155  return value
156 
157  def directionValue( self, block ):
158  try:
159  mDirection = OpenMayaMPx.cvar.MPxEmitterNode_mDirection
160  hValue = block.inputValue( mDirection )
161  value = hValue.asDouble3()
162  dirV = OpenMaya.MVector(value[0], value[1], value[2])
163  except:
164  sys.stderr.write("Error getting direction value, setting to 0,0,0\n")
165  dirV = OpenMaya.MVector(0.0, 0.0, 0.0)
166  return dirV
167 
168  def speedValue( self, block ):
169  try:
170  mSpeed = OpenMayaMPx.cvar.MPxEmitterNode_mSpeed
171  hValue = block.inputValue( mSpeed )
172  value = hValue.asDouble()
173  except:
174  sys.stderr.write("Error getting speed value, setting to 0\n")
175  value = 0.0
176  return value
177 
178  def inheritFactorValue( self, plugIndex, block ):
179  try:
180  mInheritFactor = OpenMayaMPx.cvar.MPxEmitterNode_mInheritFactor
181  mhValue = block.inputArrayValue( mInheritFactor )
182  mhValue.jumpToElement( plugIndex )
183  hValue = mhValue.inputValue( )
184  value = hValue.asDouble()
185  except:
186  sys.stderr.write("Error getting inherit factor value, setting to 0\n")
187  value = 0.0
188  return value
189 
190  def useRotation( self, direction ):
191  try:
192  thisNode = simpleEmitter.thisMObject(self)
193  fnThisNode = OpenMaya.MFnDependencyNode( thisNode )
194 
195  worldMatrixAttr = fnThisNode.attribute( "worldMatrix" )
196 
197  matrixPlug = OpenMaya.MPlug( thisNode, worldMatrixAttr )
198  matrixPlug = matrixPlug.elementByLogicalIndex( 0 )
199 
200  matrixObject = OpenMaya.MObject()
201  matrixObject = matrixPlug.asMObject( )
202 
203  worldMatrixData = OpenMaya.MFnMatrixData( matrixObject )
204  worldMatrix = worldMatrixData.matrix( )
205 
206  rotatedVector = OpenMaya.MVector()
207  rotatedVector = direction * worldMatrix
208  except:
209  sys.stderr.write("Error getting rotation value, setting to 0,0,0\n")
210  rotatedVector = OpenMaya.MVector(0.0, 0.0, 0.0)
211  return rotatedVector
212 
213 
214  def compute(self, plug, block):
215  mOutput = OpenMayaMPx.cvar.MPxEmitterNode_mOutput
216 
217  # Determine if we are requesting the output plug for this emitter node.
218  #
219  if plug == mOutput:
220  # Get the logical index of the element this plug refers to,
221  # because the node can be emitting particles into more than
222  # one particle shape.
223  #
224  try:
225  multiIndex = plug.logicalIndex( )
226 
227  # Get output data arrays (position, velocity, or parentId)
228  # that the particle shape is holding from the previous frame.
229  #
230  hOutArray = block.outputArrayValue ( mOutput )
231 
232  # Create a builder to aid in the array construction efficiently.
233  #
234  bOutArray = hOutArray.builder( )
235 
236  # Get the appropriate data array that is being currently evaluated.
237  #
238  hOut = bOutArray.addElement( multiIndex )
239 
240  # Create the data and apply the function set,
241  # particle array initialized to length zero,
242  # fnOutput.clear()
243  #
244  fnOutput = OpenMaya.MFnArrayAttrsData()
245  dOutput = fnOutput.create( )
246 
247  # Check if the particle object has reached it's maximum,
248  # hence is full. If it is full then just return with zero particles.
249  #
250  beenFull = simpleEmitter.isFullValue( self, multiIndex, block )
251  if beenFull == 1:
252  return
253 
254  # Get input position and velocity arrays where new particles are from,
255  # also known as the owner. An owner is determined if connections exist
256  # to the emitter node from a shape such as nurbs, polymesh, curve,
257  # or a lattice shape.
258  #
259 
260  # Get a single position from world transform
261  #
262  inPosAry = OpenMaya.MVectorArray()
263  inPosAry.clear()
264 
265  worldPos = OpenMaya.MPoint(0.0, 0.0, 0.0)
266  simpleEmitter.getWorldPosition( self, worldPos )
267 
268  worldV = OpenMaya.MVector(worldPos[0], worldPos[1], worldPos[2])
269  inPosAry.append( worldV )
270 
271  # Create a single velocity
272  inVelAry = OpenMaya.MVectorArray()
273  inVelAry.clear()
274  velocity = OpenMaya.MVector(0, 0, 0)
275  inVelAry.append( velocity )
276 
277  # Get deltaTime, currentTime and startTime.
278  # If deltaTime <= 0.0, or currentTime <= startTime,
279  # do not emit new pariticles and return.
280  #
281  cT = simpleEmitter.currentTimeValue( self, block )
282  sT = simpleEmitter.startTimeValue( self, multiIndex, block )
283  dT = simpleEmitter.deltaTimeValue( self, multiIndex, block )
284 
285  dTValue = dT.value()
286 
287  if cT <= sT or dTValue <= 0.0:
288  # We do not emit particles before the start time,
289  # and do not emit particles when moving backwards in time.
290  #
291 
292  # This code is necessary primarily the first time to
293  # establish the new data arrays allocated, and since we have
294  # already set the data array to length zero it does
295  # not generate any new particles.
296  #
297  hOut.setMObject( dOutput )
298  block.setClean( plug )
299  return
300 
301  # Compute and store an emission rate
302  #
303  emitCountPP = OpenMaya.MIntArray()
304  emitCountPP.clear()
305 
306  plugIndex = plug.logicalIndex( )
307 
308  # Get rate and delta time.
309  #
310  rate = simpleEmitter.rateValue( self, block )
311  dtRate = simpleEmitter.deltaTimeValue( self, plugIndex, block )
312  dtRateDbl = dtRate.asUnits( OpenMaya.MTime.kSeconds )
313  dblCount = rate * dtRateDbl
314  intCount = int(dblCount)
315  emitCountPP.append( intCount )
316 
317  # Get speed, direction vector, and inheritFactor attributes.
318  #
319  speed = simpleEmitter.speedValue( self, block )
320  dirV = simpleEmitter.directionValue( self, block )
321  inheritFactor = simpleEmitter.inheritFactorValue( self, multiIndex, block )
322 
323  # Get the position, velocity, and normalized time arrays to append new particle data.
324  #
325  fnOutPos = fnOutput.vectorArray( "position" )
326  fnOutVel = fnOutput.vectorArray( "velocity" )
327  fnOutTime = fnOutput.doubleArray( "timeInStep" )
328 
329  # Convert deltaTime into seconds.
330  #
331  dt = dT.asUnits( OpenMaya.MTime.kSeconds )
332 
333  # Rotate the direction attribute by world transform
334  rotatedV = simpleEmitter.useRotation( self, dirV )
335 
336  # Start emitting particles.
337  #
338  simpleEmitter.emit( self, inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor,\
339  rotatedV, fnOutPos, fnOutVel, fnOutTime)
340 
341  # Update the data block with new dOutput and set plug clean.
342  #
343  # sys.__stdout__.write( " handle: " + str(hOut) + "\n" )
344  hOut.setMObject( dOutput )
345  block.setClean( plug )
346  except:
347  sys.stderr.write("simpleEmitter compute error\n")
348  raise
349  else:
350  return OpenMaya.kUnknownParameter
351 
352  def emit( self, inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor, dirV, outPosAry, outVelAry, outTimeAry):
353  try:
354  posLength = inPosAry.length()
355  velLength = inVelAry.length()
356  countLength = emitCountPP.length()
357 
358  if not posLength == velLength or not posLength == countLength:
359  return
360 
361  totalCount = 0
362  for index in range(countLength):
363  totalCount += emitCountPP[index]
364  if totalCount <= 0:
365  return
366 
367  dirV.normalize()
368 
369  for index in range(posLength):
370  emitCount = emitCountPP[index]
371  if emitCount <= 0:
372  continue
373 
374  sPos = inPosAry[index]
375  sVel = inVelAry[index]
376  prePos = sPos - sVel * dt
377 
378  for i in range(emitCount):
379  alpha = ( float(i) + random.random() ) / float(emitCount)
380  newPos = prePos * (1.0 - alpha) + sPos * alpha
381  newVel = dirV * speed
382 
383  newPos += newVel * ( dt * (1.0 - alpha) )
384  newVel += sVel * inheritFactor
385 
386  # Add new data into output arrays.
387  #
388  outPosAry.append( newPos )
389  outVelAry.append( newVel )
390  outTimeAry.append( alpha )
391  except Exception as e:
392  sys.stderr.write( "Error in simpleEmitter.emit\n" )
393  raise
394 
395  def draw( self, view, path, style, status):
396  view.beginGL()
397 
398  for j in range(0, SEGMENTS):
399  glFT.glPushMatrix()
400  glFT.glRotatef(float(360.0 * j/SEGMENTS), 0.0, 1.0, 0.0)
401  glFT.glTranslatef( 1.5, 0.0, 0.0)
402 
403  for i in range(0, EDGES):
404  glFT.glBegin(OpenMayaRender.MGL_LINE_STRIP)
405 
406  p0 = float(TORUS_2PI * i/EDGES)
407  p1 = float(TORUS_2PI * (i+1)/EDGES)
408  glFT.glVertex2f(math.cos(p0), math.sin(p0))
409  glFT.glVertex2f(math.cos(p1), math.sin(p1))
410 
411  glFT.glEnd()
412 
413  glFT.glPopMatrix()
414 
415  view.endGL()
416 
417 
418 def nodeCreator():
419  return OpenMayaMPx.asMPxPtr( simpleEmitter() )
420 
421 def nodeInitializer():
422  return
423 
424 # initialize the script plug-in
425 def initializePlugin(mobject):
426  mplugin = OpenMayaMPx.MFnPlugin(mobject)
427 
428  try:
429  mplugin.registerNode( kSimpleEmitterNodeName, kSimpleEmitterNodeID, \
430  nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kEmitterNode )
431  except:
432  sys.stderr.write( "Failed to register node: %s\n" % kSimpleEmitterNodeName )
433  raise
434 
435 # uninitialize the script plug-in
436 def uninitializePlugin(mobject):
437  mplugin = OpenMayaMPx.MFnPlugin(mobject)
438 
439  try:
440  mplugin.deregisterNode( kSimpleEmitterNodeID )
441  except:
442  sys.stderr.write( "Failed to unregister node: %s\n" % kSimpleEmitterNodeName )
443  raise