scripted/simpleEmitter.py

scripted/simpleEmitter.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 # DESCRIPTION:
41 #
42 # Produces the dependency graph node "simpleEmitter".
43 #
44 # This node is an example of a particle emitter that emits in a direction
45 # from a single position.
46 #
47 ########################################################################
48 
49 import maya.OpenMaya as OpenMaya
50 import maya.OpenMayaMPx as OpenMayaMPx
51 import maya.OpenMayaRender as OpenMayaRender
52 import math
53 import random
54 import sys
55 
56 kSimpleEmitterNodeName = "spSimpleEmitter"
57 kSimpleEmitterNodeID = OpenMaya.MTypeId(0x8700F)
58 
59 SEGMENTS = 20
60 EDGES = 30
61 TORUS_PI = 3.14159265
62 TORUS_2PI = 2.0 * TORUS_PI
63 
64 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
65 glFT = glRenderer.glFunctionTable()
66 
67 class simpleEmitter(OpenMayaMPx.MPxEmitterNode):
68  def __init__(self):
69  OpenMayaMPx.MPxEmitterNode.__init__(self)
70 
71  def isFullValue( self, plugIndex, block ):
72  value = 1
73  mIsFull = OpenMayaMPx.cvar.MPxEmitterNode_mIsFull
74 
75  try:
76  mhValue = block.inputArrayValue( mIsFull )
77  mhValue.jumpToElement( plugIndex )
78  hValue = mhValue.inputValue( )
79  value = hValue.asBool()
80  except:
81  sys.stderr.write("Error getting the input array value\n")
82  raise
83  return value
84 
85 
86  def getWorldPosition( self, point ):
87  try:
88  thisNode = simpleEmitter.thisMObject( self )
89  fnThisNode = OpenMaya.MFnDependencyNode(thisNode)
90 
91  worldMatrixAttr = fnThisNode.attribute( "worldMatrix" )
92 
93  matrixPlug = OpenMaya.MPlug( thisNode, worldMatrixAttr )
94  matrixPlug = matrixPlug.elementByLogicalIndex( 0 )
95 
96  matrixObject = OpenMaya.MObject()
97  matrixObject = matrixPlug.asMObject( )
98 
99  worldMatrixData = OpenMaya.MFnMatrixData( matrixObject )
100  worldMatrix = worldMatrixData.matrix( )
101 
102  point.x = worldMatrix( 3, 0 )
103  point.y = worldMatrix( 3, 1 )
104  point.z = worldMatrix( 3, 2 )
105  except:
106  sys.stderr.write( "Error in getWorldPosition\n" )
107  raise
108 
109  def currentTimeValue( self, block ):
110  try:
111  mCurrentTime = OpenMayaMPx.cvar.MPxEmitterNode_mCurrentTime
112  hValue = block.inputValue( mCurrentTime )
113  value = hValue.asTime()
114  except:
115  sys.stderr.write("Error getting current time value, returning 0")
116  value = OpenMaya.MTime(0.0)
117  return value
118 
119  def startTimeValue( self, plugIndex, block ):
120  try:
121  mStartTime = OpenMayaMPx.cvar.MPxEmitterNode_mStartTime
122  mhValue = block.inputArrayValue( mStartTime )
123  mhValue.jumpToElement( plugIndex )
124  hValue = mhValue.inputValue( )
125  value = hValue.asTime( )
126  except:
127  sys.stderr.write("Error getting start time value, setting to 0")
128  value = OpenMaya.MTime(0.0)
129  return value
130 
131  def deltaTimeValue( self, plugIndex, block ):
132  try:
133  mDeltaTime = OpenMayaMPx.cvar.MPxEmitterNode_mDeltaTime
134 
135  mhValue = block.inputArrayValue( mDeltaTime )
136  mhValue.jumpToElement( plugIndex )
137 
138  hValue = mhValue.inputValue()
139  value = hValue.asTime()
140  except:
141  sys.stderr.write("Error getting delta time value, setting to 0\n")
142  value = OpenMaya.MTime(0.0)
143  return value
144 
145  def rateValue( self, block ):
146  try:
147  mRate = OpenMayaMPx.cvar.MPxEmitterNode_mRate
148  hValue = block.inputValue( mRate )
149  value = hValue.asDouble()
150  except:
151  sys.stderr.write("Error getting rate value, setting to 0\n")
152  value = 0.0
153  return value
154 
155  def directionValue( self, block ):
156  try:
157  mDirection = OpenMayaMPx.cvar.MPxEmitterNode_mDirection
158  hValue = block.inputValue( mDirection )
159  value = hValue.asDouble3()
160  dirV = OpenMaya.MVector(value[0], value[1], value[2])
161  except:
162  sys.stderr.write("Error getting direction value, setting to 0,0,0\n")
163  dirV = OpenMaya.MVector(0.0, 0.0, 0.0)
164  return dirV
165 
166  def speedValue( self, block ):
167  try:
168  mSpeed = OpenMayaMPx.cvar.MPxEmitterNode_mSpeed
169  hValue = block.inputValue( mSpeed )
170  value = hValue.asDouble()
171  except:
172  sys.stderr.write("Error getting speed value, setting to 0\n")
173  value = 0.0
174  return value
175 
176  def inheritFactorValue( self, plugIndex, block ):
177  try:
178  mInheritFactor = OpenMayaMPx.cvar.MPxEmitterNode_mInheritFactor
179  mhValue = block.inputArrayValue( mInheritFactor )
180  mhValue.jumpToElement( plugIndex )
181  hValue = mhValue.inputValue( )
182  value = hValue.asDouble()
183  except:
184  sys.stderr.write("Error getting inherit factor value, setting to 0\n")
185  value = 0.0
186  return value
187 
188  def useRotation( self, direction ):
189  try:
190  thisNode = simpleEmitter.thisMObject(self)
191  fnThisNode = OpenMaya.MFnDependencyNode( thisNode )
192 
193  worldMatrixAttr = fnThisNode.attribute( "worldMatrix" )
194 
195  matrixPlug = OpenMaya.MPlug( thisNode, worldMatrixAttr )
196  matrixPlug = matrixPlug.elementByLogicalIndex( 0 )
197 
198  matrixObject = OpenMaya.MObject()
199  matrixObject = matrixPlug.asMObject( )
200 
201  worldMatrixData = OpenMaya.MFnMatrixData( matrixObject )
202  worldMatrix = worldMatrixData.matrix( )
203 
204  rotatedVector = OpenMaya.MVector()
205  rotatedVector = direction * worldMatrix
206  except:
207  sys.stderr.write("Error getting rotation value, setting to 0,0,0\n")
208  rotatedVector = OpenMaya.MVector(0.0, 0.0, 0.0)
209  return rotatedVector
210 
211 
212  def compute(self, plug, block):
213  mOutput = OpenMayaMPx.cvar.MPxEmitterNode_mOutput
214 
215  # Determine if we are requesting the output plug for this emitter node.
216  #
217  if plug == mOutput:
218  # Get the logical index of the element this plug refers to,
219  # because the node can be emitting particles into more than
220  # one particle shape.
221  #
222  try:
223  multiIndex = plug.logicalIndex( )
224 
225  # Get output data arrays (position, velocity, or parentId)
226  # that the particle shape is holding from the previous frame.
227  #
228  hOutArray = block.outputArrayValue ( mOutput )
229 
230  # Create a builder to aid in the array construction efficiently.
231  #
232  bOutArray = hOutArray.builder( )
233 
234  # Get the appropriate data array that is being currently evaluated.
235  #
236  hOut = bOutArray.addElement( multiIndex )
237 
238  # Create the data and apply the function set,
239  # particle array initialized to length zero,
240  # fnOutput.clear()
241  #
242  fnOutput = OpenMaya.MFnArrayAttrsData()
243  dOutput = fnOutput.create( )
244 
245  # Check if the particle object has reached it's maximum,
246  # hence is full. If it is full then just return with zero particles.
247  #
248  beenFull = simpleEmitter.isFullValue( self, multiIndex, block )
249  if beenFull == 1:
250  return
251 
252  # Get input position and velocity arrays where new particles are from,
253  # also known as the owner. An owner is determined if connections exist
254  # to the emitter node from a shape such as nurbs, polymesh, curve,
255  # or a lattice shape.
256  #
257 
258  # Get a single position from world transform
259  #
260  inPosAry = OpenMaya.MVectorArray()
261  inPosAry.clear()
262 
263  worldPos = OpenMaya.MPoint(0.0, 0.0, 0.0)
264  simpleEmitter.getWorldPosition( self, worldPos )
265 
266  worldV = OpenMaya.MVector(worldPos[0], worldPos[1], worldPos[2])
267  inPosAry.append( worldV )
268 
269  # Create a single velocity
270  inVelAry = OpenMaya.MVectorArray()
271  inVelAry.clear()
272  velocity = OpenMaya.MVector(0, 0, 0)
273  inVelAry.append( velocity )
274 
275  # Get deltaTime, currentTime and startTime.
276  # If deltaTime <= 0.0, or currentTime <= startTime,
277  # do not emit new pariticles and return.
278  #
279  cT = simpleEmitter.currentTimeValue( self, block )
280  sT = simpleEmitter.startTimeValue( self, multiIndex, block )
281  dT = simpleEmitter.deltaTimeValue( self, multiIndex, block )
282 
283  dTValue = dT.value()
284 
285  if cT <= sT or dTValue <= 0.0:
286  # We do not emit particles before the start time,
287  # and do not emit particles when moving backwards in time.
288  #
289 
290  # This code is necessary primarily the first time to
291  # establish the new data arrays allocated, and since we have
292  # already set the data array to length zero it does
293  # not generate any new particles.
294  #
295  hOut.setMObject( dOutput )
296  block.setClean( plug )
297  return
298 
299  # Compute and store an emission rate
300  #
301  emitCountPP = OpenMaya.MIntArray()
302  emitCountPP.clear()
303 
304  plugIndex = plug.logicalIndex( )
305 
306  # Get rate and delta time.
307  #
308  rate = simpleEmitter.rateValue( self, block )
309  dtRate = simpleEmitter.deltaTimeValue( self, plugIndex, block )
310  dtRateDbl = dtRate.asUnits( OpenMaya.MTime.kSeconds )
311  dblCount = rate * dtRateDbl
312  intCount = int(dblCount)
313  emitCountPP.append( intCount )
314 
315  # Get speed, direction vector, and inheritFactor attributes.
316  #
317  speed = simpleEmitter.speedValue( self, block )
318  dirV = simpleEmitter.directionValue( self, block )
319  inheritFactor = simpleEmitter.inheritFactorValue( self, multiIndex, block )
320 
321  # Get the position, velocity, and normalized time arrays to append new particle data.
322  #
323  fnOutPos = fnOutput.vectorArray( "position" )
324  fnOutVel = fnOutput.vectorArray( "velocity" )
325  fnOutTime = fnOutput.doubleArray( "timeInStep" )
326 
327  # Convert deltaTime into seconds.
328  #
329  dt = dT.asUnits( OpenMaya.MTime.kSeconds )
330 
331  # Rotate the direction attribute by world transform
332  rotatedV = simpleEmitter.useRotation( self, dirV )
333 
334  # Start emitting particles.
335  #
336  simpleEmitter.emit( self, inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor,\
337  rotatedV, fnOutPos, fnOutVel, fnOutTime)
338 
339  # Update the data block with new dOutput and set plug clean.
340  #
341  # sys.__stdout__.write( " handle: " + str(hOut) + "\n" )
342  hOut.setMObject( dOutput )
343  block.setClean( plug )
344  except:
345  sys.stderr.write("simpleEmitter compute error\n")
346  raise
347  else:
348  return OpenMaya.kUnknownParameter
349 
350  def emit( self, inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor, dirV, outPosAry, outVelAry, outTimeAry):
351  try:
352  posLength = inPosAry.length()
353  velLength = inVelAry.length()
354  countLength = emitCountPP.length()
355 
356  if not posLength == velLength or not posLength == countLength:
357  return
358 
359  totalCount = 0
360  for index in range(countLength):
361  totalCount += emitCountPP[index]
362  if totalCount <= 0:
363  return
364 
365  dirV.normalize()
366 
367  for index in range(posLength):
368  emitCount = emitCountPP[index]
369  if emitCount <= 0:
370  continue
371 
372  sPos = inPosAry[index]
373  sVel = inVelAry[index]
374  prePos = sPos - sVel * dt
375 
376  for i in range(emitCount):
377  alpha = ( float(i) + random.random() ) / float(emitCount)
378  newPos = prePos * (1.0 - alpha) + sPos * alpha
379  newVel = dirV * speed
380 
381  newPos += newVel * ( dt * (1.0 - alpha) )
382  newVel += sVel * inheritFactor
383 
384  # Add new data into output arrays.
385  #
386  outPosAry.append( newPos )
387  outVelAry.append( newVel )
388  outTimeAry.append( alpha )
389  except Exception, e:
390  sys.stderr.write( "Error in simpleEmitter.emit\n" )
391  raise
392 
393  def draw( self, view, path, style, status):
394  view.beginGL()
395 
396  for j in range(0, SEGMENTS):
397  glFT.glPushMatrix()
398  glFT.glRotatef(float(360.0 * j/SEGMENTS), 0.0, 1.0, 0.0)
399  glFT.glTranslatef( 1.5, 0.0, 0.0)
400 
401  for i in range(0, EDGES):
402  glFT.glBegin(OpenMayaRender.MGL_LINE_STRIP)
403 
404  p0 = float(TORUS_2PI * i/EDGES)
405  p1 = float(TORUS_2PI * (i+1)/EDGES)
406  glFT.glVertex2f(math.cos(p0), math.sin(p0))
407  glFT.glVertex2f(math.cos(p1), math.sin(p1))
408 
409  glFT.glEnd()
410 
411  glFT.glPopMatrix()
412 
413  view.endGL()
414 
415 
416 def nodeCreator():
417  return OpenMayaMPx.asMPxPtr( simpleEmitter() )
418 
419 def nodeInitializer():
420  return
421 
422 # initialize the script plug-in
423 def initializePlugin(mobject):
424  mplugin = OpenMayaMPx.MFnPlugin(mobject)
425 
426  try:
427  mplugin.registerNode( kSimpleEmitterNodeName, kSimpleEmitterNodeID, \
428  nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kEmitterNode )
429  except:
430  sys.stderr.write( "Failed to register node: %s\n" % kSimpleEmitterNodeName )
431  raise
432 
433 # uninitialize the script plug-in
434 def uninitializePlugin(mobject):
435  mplugin = OpenMayaMPx.MFnPlugin(mobject)
436 
437  try:
438  mplugin.deregisterNode( kSimpleEmitterNodeID )
439  except:
440  sys.stderr.write( "Failed to unregister node: %s\n" % kSimpleEmitterNodeName )
441  raise