simpleEmitter.py

#-
# ==========================================================================
# Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
# rights reserved.
#
# The coded instructions, statements, computer programs, and/or related 
# material (collectively the "Data") in these files contain unpublished 
# information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
# licensors, which is protected by U.S. and Canadian federal copyright 
# law and by international treaties.
#
# The Data is provided for use exclusively by You. You have the right 
# to use, modify, and incorporate this Data into other products for 
# purposes authorized by the Autodesk software license agreement, 
# without fee.
#
# The copyright notices in the Software and this entire statement, 
# including the above license grant, this restriction and the 
# following disclaimer, must be included in all copies of the 
# Software, in whole or in part, and all derivative works of 
# the Software, unless such copies or derivative works are solely 
# in the form of machine-executable object code generated by a 
# source language processor.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
# AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
# WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
# NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
# PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
# TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
# BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
# DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
# AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
# OR PROBABILITY OF SUCH DAMAGES.
#
# ==========================================================================
#+


import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaRender as OpenMayaRender
import math
import random
import sys

kSimpleEmitterNodeName = "spSimpleEmitter"
kSimpleEmitterNodeID = OpenMaya.MTypeId(0x8700F)

SEGMENTS = 20
EDGES = 30
TORUS_PI = 3.14159265
TORUS_2PI = 2.0 * TORUS_PI

glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
glFT = glRenderer.glFunctionTable()

class simpleEmitter(OpenMayaMPx.MPxEmitterNode):
        def __init__(self):
                OpenMayaMPx.MPxEmitterNode.__init__(self)

        def isFullValue( self, plugIndex, block ):
                value = 1
                mIsFull = OpenMayaMPx.cvar.MPxEmitterNode_mIsFull

                try:
                        mhValue = block.inputArrayValue( mIsFull )
                        mhValue.jumpToElement( plugIndex )
                        hValue = mhValue.inputValue( )
                        value = hValue.asBool()
                except:
                        sys.stderr.write("Error getting the input array value\n")
                        raise
                return value
                                        
        
        def getWorldPosition( self, point ):
                try:
                        thisNode = simpleEmitter.thisMObject( self )
                        fnThisNode = OpenMaya.MFnDependencyNode(thisNode)

                        worldMatrixAttr = fnThisNode.attribute( "worldMatrix" )

                        matrixPlug = OpenMaya.MPlug( thisNode, worldMatrixAttr )
                        matrixPlug = matrixPlug.elementByLogicalIndex( 0 )

                        matrixObject = OpenMaya.MObject()
                        matrixObject = matrixPlug.asMObject(  )

                        worldMatrixData = OpenMaya.MFnMatrixData( matrixObject )
                        worldMatrix = worldMatrixData.matrix( )

                        point.x = worldMatrix( 3, 0 )
                        point.y = worldMatrix( 3, 1 )
                        point.z = worldMatrix( 3, 2 )
                except:
                        sys.stderr.write( "Error in getWorldPosition\n" )
                        raise

        def currentTimeValue( self, block ):
                try:
                        mCurrentTime = OpenMayaMPx.cvar.MPxEmitterNode_mCurrentTime
                        hValue = block.inputValue( mCurrentTime )
                        value = hValue.asTime()
                except:
                        sys.stderr.write("Error getting current time value, returning 0")
                        value = OpenMaya.MTime(0.0)
                return value

        def startTimeValue( self, plugIndex, block ):
                try:
                        mStartTime = OpenMayaMPx.cvar.MPxEmitterNode_mStartTime
                        mhValue = block.inputArrayValue( mStartTime )
                        mhValue.jumpToElement( plugIndex )
                        hValue = mhValue.inputValue( )
                        value = hValue.asTime( )                
                except:
                        sys.stderr.write("Error getting start time value, setting to 0")
                        value = OpenMaya.MTime(0.0)
                return value

        def deltaTimeValue( self, plugIndex, block ):
                try:
                        mDeltaTime = OpenMayaMPx.cvar.MPxEmitterNode_mDeltaTime

                        mhValue = block.inputArrayValue( mDeltaTime )
                        mhValue.jumpToElement( plugIndex )

                        hValue = mhValue.inputValue()
                        value = hValue.asTime()
                except:
                        sys.stderr.write("Error getting delta time value, setting to 0\n")
                        value = OpenMaya.MTime(0.0)
                return value

        def rateValue( self, block ):
                try:
                        mRate = OpenMayaMPx.cvar.MPxEmitterNode_mRate
                        hValue = block.inputValue( mRate )
                        value = hValue.asDouble()               
                except:
                        sys.stderr.write("Error getting rate value, setting to 0\n")
                        value = 0.0
                return value

        def directionValue( self, block ):
                try:
                        mDirection = OpenMayaMPx.cvar.MPxEmitterNode_mDirection
                        hValue = block.inputValue( mDirection )
                        value = hValue.asDouble3()
                        dirV = OpenMaya.MVector(value[0], value[1], value[2])
                except:
                        sys.stderr.write("Error getting direction value, setting to 0,0,0\n")
                        dirV = OpenMaya.MVector(0.0, 0.0, 0.0)
                return dirV

        def speedValue( self, block ):
                try:
                        mSpeed = OpenMayaMPx.cvar.MPxEmitterNode_mSpeed
                        hValue = block.inputValue( mSpeed )
                        value = hValue.asDouble()               
                except:
                        sys.stderr.write("Error getting speed value, setting to 0\n")
                        value = 0.0
                return value

        def inheritFactorValue( self, plugIndex, block ):
                try:
                        mInheritFactor = OpenMayaMPx.cvar.MPxEmitterNode_mInheritFactor
                        mhValue = block.inputArrayValue( mInheritFactor )
                        mhValue.jumpToElement( plugIndex )
                        hValue = mhValue.inputValue( )
                        value = hValue.asDouble()               
                except:
                        sys.stderr.write("Error getting inherit factor value, setting to 0\n")
                        value = 0.0
                return value

        def useRotation( self, direction ):
                try:
                        thisNode = simpleEmitter.thisMObject(self)
                        fnThisNode = OpenMaya.MFnDependencyNode( thisNode )

                        worldMatrixAttr = fnThisNode.attribute( "worldMatrix" )

                        matrixPlug = OpenMaya.MPlug( thisNode, worldMatrixAttr )
                        matrixPlug = matrixPlug.elementByLogicalIndex( 0 )

                        matrixObject = OpenMaya.MObject()
                        matrixObject = matrixPlug.asMObject( )

                        worldMatrixData = OpenMaya.MFnMatrixData( matrixObject )
                        worldMatrix = worldMatrixData.matrix( )

                        rotatedVector = OpenMaya.MVector()
                        rotatedVector = direction * worldMatrix
                except:
                        sys.stderr.write("Error getting rotation value, setting to 0,0,0\n")
                        rotatedVector = OpenMaya.MVector(0.0, 0.0, 0.0)
                return rotatedVector


        def compute(self, plug, block):
                mOutput = OpenMayaMPx.cvar.MPxEmitterNode_mOutput

                # Determine if we are requesting the output plug for this emitter node.
                #
                if plug == mOutput:
                        # Get the logical index of the element this plug refers to,
                        # because the node can be emitting particles into more than
                        # one particle shape.
                        #
                        try:
                                multiIndex = plug.logicalIndex( )

                                # Get output data arrays (position, velocity, or parentId)
                                # that the particle shape is holding from the previous frame.
                                #
                                hOutArray = block.outputArrayValue ( mOutput )

                                # Create a builder to aid in the array construction efficiently.
                                #
                                bOutArray = hOutArray.builder( )

                                # Get the appropriate data array that is being currently evaluated.
                                #
                                hOut = bOutArray.addElement( multiIndex )

                                # Create the data and apply the function set,
                                # particle array initialized to length zero, 
                                # fnOutput.clear()
                                #
                                fnOutput = OpenMaya.MFnArrayAttrsData()
                                dOutput = fnOutput.create( )

                                # Check if the particle object has reached it's maximum,
                                # hence is full. If it is full then just return with zero particles.
                                #
                                beenFull = simpleEmitter.isFullValue( self, multiIndex, block )
                                if beenFull == 1:
                                        return

                                # Get input position and velocity arrays where new particles are from,
                                # also known as the owner. An owner is determined if connections exist
                                # to the emitter node from a shape such as nurbs, polymesh, curve, 
                                # or a lattice shape.
                                #
                                
                                # Get a single position from world transform
                                #
                                inPosAry = OpenMaya.MVectorArray()
                                inPosAry.clear()
                        
                                worldPos = OpenMaya.MPoint(0.0, 0.0, 0.0)
                                simpleEmitter.getWorldPosition( self, worldPos )

                                worldV = OpenMaya.MVector(worldPos[0], worldPos[1], worldPos[2])
                                inPosAry.append( worldV )

                                # Create a single velocity                      
                                inVelAry = OpenMaya.MVectorArray()
                                inVelAry.clear()
                                velocity = OpenMaya.MVector(0, 0, 0)
                                inVelAry.append( velocity )

                                # Get deltaTime, currentTime and startTime.
                                # If deltaTime <= 0.0, or currentTime <= startTime,
                                # do not emit new pariticles and return.
                                #
                                cT = simpleEmitter.currentTimeValue( self, block )
                                sT = simpleEmitter.startTimeValue( self, multiIndex, block )
                                dT = simpleEmitter.deltaTimeValue( self, multiIndex, block )

                                dTValue = dT.value()
                        
                                if cT <= sT or dTValue <= 0.0:
                                        # We do not emit particles before the start time, 
                                        # and do not emit particles when moving backwards in time.
                                        # 

                                        # This code is necessary primarily the first time to 
                                        # establish the new data arrays allocated, and since we have 
                                        # already set the data array to length zero it does 
                                        # not generate any new particles.
                                        # 
                                        hOut.setMObject( dOutput )
                                        block.setClean( plug )
                                        return

                                # Compute and store an emission rate
                                #
                                emitCountPP = OpenMaya.MIntArray()
                                emitCountPP.clear()

                                plugIndex = plug.logicalIndex( )

                                # Get rate and delta time.
                                #
                                rate = simpleEmitter.rateValue( self, block )
                                dtRate = simpleEmitter.deltaTimeValue( self, plugIndex, block )
                                dtRateDbl = dtRate.asUnits( OpenMaya.MTime.kSeconds )
                                dblCount = rate * dtRateDbl
                                intCount = int(dblCount)
                                emitCountPP.append( intCount )

                                # Get speed, direction vector, and inheritFactor attributes.
                                #                       
                                speed = simpleEmitter.speedValue( self, block )
                                dirV = simpleEmitter.directionValue( self, block )
                                inheritFactor = simpleEmitter.inheritFactorValue( self, multiIndex, block )

                                # Get the position, velocity, and normalized time arrays to append new particle data.
                                #
                                fnOutPos = fnOutput.vectorArray( "position" )
                                fnOutVel = fnOutput.vectorArray( "velocity" )
                                fnOutTime = fnOutput.doubleArray( "timeInStep" )

                                # Convert deltaTime into seconds.
                                #                       
                                dt = dT.asUnits( OpenMaya.MTime.kSeconds )

                                # Rotate the direction attribute by world transform
                                rotatedV = simpleEmitter.useRotation( self, dirV )

                                # Start emitting particles.
                                #
                                simpleEmitter.emit( self, inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor,\
                                                rotatedV, fnOutPos, fnOutVel, fnOutTime)

                                # Update the data block with new dOutput and set plug clean.
                                #       
                                # sys.__stdout__.write( " handle: " + str(hOut) + "\n" )                
                                hOut.setMObject( dOutput )
                                block.setClean( plug )
                        except:
                                sys.stderr.write("simpleEmitter compute error\n")
                                raise
                else:
                        return OpenMaya.kUnknownParameter

        def emit( self, inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor, dirV, outPosAry, outVelAry, outTimeAry):
                try:
                        posLength = inPosAry.length()
                        velLength = inVelAry.length()
                        countLength = emitCountPP.length()

                        if not posLength == velLength or not posLength == countLength:
                                return

                        totalCount = 0
                        for index in range(countLength):
                                totalCount += emitCountPP[index]
                        if totalCount <= 0:
                                return

                        dirV.normalize()
                        
                        for index in range(posLength):
                                emitCount = emitCountPP[index]
                                if emitCount <= 0:
                                        continue
                        
                                sPos = inPosAry[index]
                                sVel = inVelAry[index]
                                prePos = sPos - sVel * dt

                                for i in range(emitCount):
                                        alpha = ( float(i) + random.random() ) / float(emitCount)
                                        newPos = prePos * (1.0 - alpha) + sPos * alpha
                                        newVel = dirV * speed

                                        newPos += newVel * ( dt * (1.0 - alpha) )
                                        newVel += sVel * inheritFactor

                                        # Add new data into output arrays.
                                        #
                                        outPosAry.append( newPos )
                                        outVelAry.append( newVel )
                                        outTimeAry.append( alpha )
                except Exception, e:
                        sys.stderr.write( "Error in simpleEmitter.emit\n" )
                        raise

        def draw( self, view, path, style, status):
                view.beginGL()

                for j in range(0, SEGMENTS):
                        glFT.glPushMatrix()
                        glFT.glRotatef(float(360.0 * j/SEGMENTS), 0.0, 1.0, 0.0)
                        glFT.glTranslatef( 1.5, 0.0, 0.0)
                        
                        for i in range(0, EDGES):
                                glFT.glBegin(OpenMayaRender.MGL_LINE_STRIP)

                                p0 = float(TORUS_2PI * i/EDGES)
                                p1 = float(TORUS_2PI * (i+1)/EDGES)
                                glFT.glVertex2f(math.cos(p0), math.sin(p0))
                                glFT.glVertex2f(math.cos(p1), math.sin(p1))
                                
                                glFT.glEnd()

                        glFT.glPopMatrix()

                view.endGL()


def nodeCreator():
        return OpenMayaMPx.asMPxPtr( simpleEmitter() )

def nodeInitializer():
        return

# initialize the script plug-in
def initializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject)

        try:
                mplugin.registerNode( kSimpleEmitterNodeName, kSimpleEmitterNodeID, \
                                                                nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kEmitterNode )
        except:
                sys.stderr.write( "Failed to register node: %s\n" % kSimpleEmitterNodeName )
                raise

# uninitialize the script plug-in
def uninitializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject)

        try:
                mplugin.deregisterNode( kSimpleEmitterNodeID )
        except:
                sys.stderr.write( "Failed to unregister node: %s\n" % kSimpleEmitterNodeName )
                raise   

Autodesk® Maya® 2011 © 1997-2010 Autodesk, Inc. All rights reserved. Generated with doxygen 1.5.6