- Main Page
- Related Pages
- Modules
- Classes
- Examples

# 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