#include <maya/MIOStream.h>
#include <math.h>
#include <stdlib.h>
#include "simpleFluidEmitter.h"
#include <maya/MVectorArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MIntArray.h>
#include <maya/MMatrix.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnVectorArrayData.h>
#include <maya/MFnDoubleArrayData.h>
#include <maya/MFnArrayAttrsData.h>
#include <maya/MFnMatrixData.h>
#include <maya/MDagPath.h>
#include <maya/MMatrix.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MFnDynSweptGeometryData.h>
#include <maya/MDynSweptTriangle.h>
#include <maya/MPlugArray.h>
MTypeId simpleFluidEmitter::id( 0x81020 );
 
simpleFluidEmitter::simpleFluidEmitter()
{
}
simpleFluidEmitter::~simpleFluidEmitter()
{
}
void *simpleFluidEmitter::creator()
{
    return new simpleFluidEmitter;
}
MStatus simpleFluidEmitter::initialize()
 
{
}
{
    {
        
        
    }
    else
    {
        
        
    }
}
simpleFluidEmitter::fluidEmitter( 
    int plugIndex 
)
{
    
    
    
    
    
    {
    }
    
    
    
    
    if( dTime == 0.0 )
    {
        
        
    }
    
    
    
    MTime cTime = getCurrentTime( block );
 
    MTime sTime = getStartTime( plugIndex, block );
 
    
    
    
    if( cTime < sTime )
    {
        resetRandomState( plugIndex, block );
    }
    
    
    
    
    
    
    double density = fluidDensityEmission( block ); 
    double heat = fluidHeatEmission( block );   
    double fuel = fluidFuelEmission( block );   
    bool doColor = fluidEmitColor( block ); 
    
    fluid.getDensityMode( densityMode, grad );
    fluid.getTemperatureMode( tempMode, grad );
    fluid.getFuelMode( fuelMode, grad );
    fluid.getColorMode( colorMode );
    fluid.getFalloffMode( falloffMode );
    
    
    
    if( !densityToEmit && !heatToEmit && !fuelToEmit && !colorToEmit && !falloffEmit )
    {
    }
    
    
    double dropoff = fluidDropoff( block );
    
    
    
    
    double xformScale[3];
    double dropoffScale = sqrt( xformScale[0]*xformScale[0] + 
                                xformScale[1]*xformScale[1] + 
                                xformScale[2]*xformScale[2] );
    if( dropoffScale > 0.1 )
    {
        dropoff /= dropoffScale;
    }
    
    
    
    
    
    
    getRandomState( plugIndex, block );
    
    
    
    double conversion = 0.01;
    MEmitterType emitterType = getEmitterType( block );
    switch( emitterType )
    {
        case kOmni:
            omniFluidEmitter( fluid, worldMatrix, plugIndex, block, dTime,
                              conversion, dropoff );
            break;
        case kVolume:
            volumeFluidEmitter( fluid, worldMatrix, plugIndex, block, dTime,
                                conversion, dropoff );
            break;
            
        case kSurface:
            surfaceFluidEmitter( fluid, worldMatrix, plugIndex, block, dTime,
                                 conversion, dropoff );
            break;
        default:
            break;
    }
    
    
    setRandomState( plugIndex, block );
}
#ifdef MIN
#undef MIN
#endif
#ifdef MAX
#undef MAX
#endif
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))
void 
simpleFluidEmitter::omniFluidEmitter(
    int             plugIndex,
    double          dt,
    double          conversion,
    double          dropoff
)
{
    
    
    
    
    
    
    
    bool gotOwnerPositions = false;
    MObject ownerShape = getOwnerShape();
 
    {
        {
            {
                
                
                for( 
unsigned int i = 0; i < posArray.
length(); i ++ )
 
                {
                    emitterPositions.
append( posArray[i] );
                }
                
                gotOwnerPositions = true;
            }
        }
    }
    
    
    
    
    if( !gotOwnerPositions )
    {
        MPoint emitterPos = getWorldPosition();
 
        emitterPositions.
append( emitterPos );
    }
    
    
    double densityEmit = fluidDensityEmission( block );
    double fuelEmit = fluidFuelEmission( block );
    double heatEmit = fluidHeatEmission( block );
    bool doEmitColor = fluidEmitColor( block );
    MColor emitColor = fluidColor( block );
 
    
    
    
    
    double theRate = getRate(block) * dt * conversion;
    
    
    double size[3];
    unsigned int res[3];
    
    
    double dx = size[0] / res[0];
    double dy = size[1] / res[1];
    double dz = size[2] / res[2];
    
    
    double Ox = -size[0]/2;
    double Oy = -size[1]/2;
    double Oz = -size[2]/2; 
    
    
    
    double minDist = getMinDistance( block );
    double maxDist = getMaxDistance( block );
    
    
    
    
    
    double fluidScale[3];
    
    
    double wsX =  fabs(fluidScale[0]*dx);
    double wsY = fabs(fluidScale[1]*dy);
    double wsZ = fabs(fluidScale[2]*dz);
    double wsMin = MIN( MIN( wsX, wsY), wsZ );
    double wsMax = MAX( MAX( wsX, wsY), wsZ );
    double wsDiag  = wsMin * sqrt(3.0);
    
    if ( maxDist <= minDist || maxDist <= (wsDiag/2.0) ) {
        if ( minDist < 0 ) minDist = 0;
        maxDist = minDist + wsDiag/2.0;
        dropoff = 0;
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    int numSamples = 1;
    
    if(wsMin >.00001) 
    {
        numSamples = (int)(wsMax/wsMin + .5);
        if(numSamples > 8) 
            numSamples = 8;
        if(numSamples < 1)
            numSamples = 1;
    }
    
    bool jitter =  fluidJitter(block);
    if( !jitter )
    {
        
        
        
        
        numSamples = 1;
    }
    for( 
unsigned int p = 0; p < emitterPositions.
length(); p++ )
 
    {
        MPoint emitterWorldPos = emitterPositions[p];
 
        
        
        
        for( unsigned int i = 0; i < res[0]; i++ )
        {
            double x = Ox + i*dx;
            
            for( unsigned int j = 0; j < res[1]; j++ )
            {
                double y = Oy + j*dy;
                
                for( unsigned int k = 0; k < res[2]; k++ )
                {
                    double z = Oz + k*dz;
    
                    int si;
                    for( si = 0; si < numSamples; si++ )
                    {
                        
                        
                        double rx, ry, rz;
                        if( jitter )
                        {
                            rx = x + randgen()*dx;
                            ry = y + randgen()*dy;
                            rz = z + randgen()*dz;
                        }
                        else
                        {
                            rx = x + 0.5*dx;
                            ry = y + 0.5*dy;
                            rz = z + 0.5*dz;
                        }
                        
                        
                        point *= fluidWorldMatrix;
                        MVector diff = point - emitterWorldPos;
 
                        double distSquared = diff * diff;
                    
                        
                        
                        if( (dist < minDist) || (dist > maxDist) )
                        {
                            continue;
                        }
                        
                        
                        
                        
                        
                        double distDrop = dropoff * distSquared;
                        double newVal = theRate * exp( -distDrop ) / (double)numSamples;
                        
                        
                        if( newVal != 0 )
                        {
                            fluid.
emitIntoArrays( (
float) newVal, i, j, k, (
float)densityEmit, (
float)heatEmit, (
float)fuelEmit, doEmitColor, emitColor );
                        }
                        if( fArray != NULL )
                        {
                            MPoint midPoint( x+0.5*dx, y+0.5*dy, z+0.5*dz );
 
                            midPoint.x *= 0.2;
                            midPoint.y *= 0.2;
                            midPoint.z *= 0.2;
                            float fdist = (float) sqrt( midPoint.x*midPoint.x + midPoint.y*midPoint.y + midPoint.z*midPoint.z );
                            fdist /= sqrtf(3.0f);
                            fArray[fluid.
index(i,j,k)] = 1.0f-fdist;
                        }
                    }
                }
            }
        }
    }
}
void 
simpleFluidEmitter::volumeFluidEmitter(
    int             plugIndex,
    double          dt,
    double          conversion,
    double          dropoff
)
{
    
    
    MPoint emitterPos = getWorldPosition();
 
    MMatrix emitterWorldMatrix = getWorldMatrix();
 
    
    
    
    double densityEmit = fluidDensityEmission( block );
    double fuelEmit = fluidFuelEmission( block );
    double heatEmit = fluidHeatEmission( block );
    bool doEmitColor = fluidEmitColor( block );
    MColor emitColor = fluidColor( block );
 
    
    
    
    
    
    double theRate = getRate(block) * dt * conversion;
    
    
    
    
    if( !volumePrimitiveBoundingBox( bbox ) )
    {
        
        
        return;
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    bool autoResize = false;
    bool resizeToEmitter = false;
    MPlug fnPlug = nodeFn.findPlug(
"emissionFunction");
 
        if (connections.
length() > 0) {
 
            MObject sourceNode = connections[0].node();
 
                autoResize = fluidFn.isAutoResize();
                resizeToEmitter =  fluidFn.isResizeToEmitter();
                if(autoResize && resizeToEmitter) {
                    fluidFn.expandToInclude(lowCorner, highCorner);
                }
            }
        }
    }
    if(autoResize && resizeToEmitter) {
    }
    
    
    double size[3];
    unsigned int res[3];
    
    double dx = size[0] / res[0];
    double dy = size[1] / res[1];
    double dz = size[2] / res[2];
    
    
    double Ox = -size[0]/2;
    double Oy = -size[1]/2;
    double Oz = -size[2]/2; 
    
    
    int3 lowCoords;
    int3 highCoords;
    
    int i;
    for ( i = 0; i < 3; i++ )
    {
        if ( lowCoords[i] < 0 ) {
            lowCoords[i] = 0;
        } else if ( lowCoords[i] > ((int)res[i])-1 ) {
            lowCoords[i] = ((int)res[i])-1;
        }
        if ( highCoords[i] < 0 ) {
            highCoords[i] = 0;
        } else if ( highCoords[i] > ((int)res[i])-1 ) {
            highCoords[i] = ((int)res[i])-1;
        }
        
    }
    
    
    
    
    
    double emitterVoxelSize[3];
    emitterVoxelSize[0] = (highCorner[0]-lowCorner[0])/dx;
    emitterVoxelSize[1] = (highCorner[1]-lowCorner[1])/dy;
    emitterVoxelSize[2] = (highCorner[2]-lowCorner[2])/dz;
        
    double minVoxelSize = MIN(emitterVoxelSize[0],MIN(emitterVoxelSize[1],emitterVoxelSize[2]));
    if( minVoxelSize < 1.0 )
    {
        minVoxelSize = 1.0;
    }
    int maxSamples = 8;
    int numSamples = (int)(8.0/(minVoxelSize*minVoxelSize*minVoxelSize) + 0.5);
    if( numSamples < 1 ) numSamples = 1;
    if( numSamples > maxSamples ) numSamples = maxSamples;
    
    
    
    
    bool jitter = fluidJitter(block);
    if( !jitter )
    {
        numSamples = 1;
    }
    
    
    
    
    
    
    for( i = lowCoords[0]; i <= highCoords[0]; i++ )
    {
        double x = Ox + (i+0.5)*dx;
            
        for( int j = lowCoords[1]; j < highCoords[1]; j++ )
        {
            double y = Oy + (j+0.5)*dy;
            for( int k = lowCoords[2]; k < highCoords[2]; k++ )
            {
                double z = Oz + (k+0.5)*dz;
                
                for ( int si = 0; si < numSamples; si++) {
                    
                    
                    
                    double rx, ry, rz;
                    if(jitter) {
                        rx = x + dx*(randgen() - 0.5);
                        ry = y + dy*(randgen() - 0.5);
                        rz = z + dz*(randgen() - 0.5);
                    } else {
                        rx = x;
                        ry = y;
                        rz = z;
                    }
                    
                    
                    pt *= fluidWorldMatrix;
                    
                    
                    if( volumePrimitivePointInside( pt, emitterWorldMatrix ) )
                    {
                        
                        
                        double dist = pt.distanceTo( emitterPos );
                        double distDrop = dropoff * (dist*dist);
                        double newVal = (theRate * exp( -distDrop )) / (double)numSamples;
                        
                        
                        
                        if( newVal != 0.0 )
                        {
                            fluid.
emitIntoArrays( (
float) newVal, i, j, k, (
float)densityEmit, (
float)heatEmit, (
float)fuelEmit, doEmitColor, emitColor );
                        }
                    }
                }
            }
        }
    }
}
void 
simpleFluidEmitter::surfaceFluidEmitter(
    int             plugIndex,
    double          dt,
    double          conversion,
    double          dropoff
)
{
    
    
    
    
    
    double densityEmit = fluidDensityEmission( block );
    double fuelEmit = fluidFuelEmission( block );
    double heatEmit = fluidHeatEmission( block );
    bool doEmitColor = fluidEmitColor( block );
    MColor emitColor = fluidColor( block );
 
    
    
    
    
    
    double theRate = getRate(block) * dt * conversion;
    
    
    double size[3];
    unsigned int res[3];
        
    
    double dx = size[0] / res[0];
    double dy = size[1] / res[1];
    double dz = size[2] / res[2];
    
    
    double Ox = -size[0]/2;
    double Oy = -size[1]/2;
    double Oz = -size[2]/2; 
    
    
    
    
    
    
    
    
    
    
    bool jitter = fluidJitter(block);
    if( !jitter )
    {
        resetRandomState( plugIndex, block );
    }
    if( fnSweptData.triangleCount() > 0 )
    {
        
        
        
        
        double vfArea = pow(dx*dy*dz, 2.0/3.0);
        
        
        
        
        
        
        MObject rateTextureAttr = fnNode.attribute( 
"textureRate" );
 
        MObject colorTextureAttr = fnNode.attribute( 
"particleColor" );
 
        bool texturedRate = hasValidEmission2dTexture( rateTextureAttr );
        bool texturedColor = hasValidEmission2dTexture( colorTextureAttr );
        
        
        
        if( texturedRate || texturedColor )
        {
            uCoords.
setLength( fnSweptData.triangleCount() );
            vCoords.
setLength( fnSweptData.triangleCount() );
            
            int t;
            for( t = 0; t < fnSweptData.triangleCount(); t++ )
            {
                
                uCoords[t] = uvMid[0];
                vCoords[t] = uvMid[1];
            }
        }
        
        
        if( texturedRate )
        {
            evalEmission2dTexture( rateTextureAttr, uCoords, vCoords, NULL, &texturedRateValues );
        }
        
        if( texturedColor )
        {
            evalEmission2dTexture( colorTextureAttr, uCoords, vCoords, &texturedColorValues, NULL );
        }
        
        for( int t = 0; t < fnSweptData.triangleCount(); t++ )
        {
            
            
            double curTexturedRate = texturedRate ? texturedRateValues[t] : 1.0;
            if( texturedColor )
            {
                MVector& curVec = texturedColorValues[t];
 
                curTexturedColor.
r = (float)curVec[0];
                curTexturedColor.
g = (float)curVec[1];
                curTexturedColor.
b = (float)curVec[2];
                curTexturedColor.
a = 1.0;
            }
            else
            {
                curTexturedColor = emitColor;
            }
            
            
            
            
            double triArea = tri.
area();
 
            int numSamples = (int)(triArea / vfArea);
            if( numSamples < 1 ) numSamples = 1;
            
            
            
            
            
            double triRate = (theRate*(triArea/vfArea))/numSamples;
            
            triRate *= curTexturedRate;
            
            for( int j = 0; j < numSamples; j++ )
            {
                
                
                
                double r1 = randgen();
                double r2 = randgen();
                
                if( r1 + r2 > 1 )
                {
                    r1 = 1-r1;
                    r2 = 1-r2;
                }
                double r3 = 1 - (r1+r2);
                MPoint randPoint = r1*v0 + r2*v1 + r3*v2;
 
                randPoint *= fluidInverseWorldMatrix;
                
                
                
                int3 coord;
                
                if( (coord[0]<0) || (coord[1]<0) || (coord[2]<0) ||
                    (coord[0]>=(int)res[0]) || (coord[1]>=(int)res[1]) || (coord[2]>=(int)res[2]) )
                {
                    continue;
                }
                
                
                
                
                gridPoint.
x = Ox + (coord[0]+0.5)*dx;
                gridPoint.y = Oy + (coord[1]+0.5)*dy;
                gridPoint.z = Oz + (coord[2]+0.5)*dz;
                
                MVector diff = gridPoint - randPoint;
 
                double distSquared = diff * diff;
                double distDrop = dropoff * distSquared;
                
                double newVal = triRate * exp( -distDrop );
        
                
                
                if( newVal != 0 )
                {
                    fluid.
emitIntoArrays( (
float) newVal, coord[0], coord[1], coord[2], (
float)densityEmit, (
float)heatEmit, (
float)fuelEmit, doEmitColor, curTexturedColor );      
                }
            }
        }
    }
}
{
    MFnPlugin plugin(obj, PLUGIN_COMPANY, 
"3.0", 
"Any");
 
    status = plugin.registerNode( "simpleFluidEmitter", simpleFluidEmitter::id,
                            &simpleFluidEmitter::creator, &simpleFluidEmitter::initialize,
    if (!status) {
        status.
perror(
"registerNode");
        return status;
    }
    return status;
}
{
    status = plugin.deregisterNode( simpleFluidEmitter::id );
    if (!status) {
        status.
perror(
"deregisterNode");
        return status;
    }
    return status;
}