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