#include <math.h>
#include <stdlib.h>
#include <maya/MPxNode.h>
#include <maya/MIOStream.h>
#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h> 
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFloatVector.h>
#include <maya/MFloatPoint.h>
#include <maya/MFnPlugin.h>
float Noise(float, float, float);
void  Noise_init();
static float Omega(int i, int j, int k, float t[3]);
static float omega(float);
static double turbulence(double u,double v,double w,int octaves);
#define PI                  3.14159265358979323846
#ifdef FLOOR
#undef FLOOR
#endif
#define FLOOR(x)            ((int)floorf(x))
#define TABLELEN            512
#define TLD2                256    // TABLELEN 
static int                  Phi[TABLELEN];
static char                 fPhi[TABLELEN];
static float                G[TABLELEN][3];
{
    public:
                    Flame3D();
    virtual         ~Flame3D();
    static  void *  creator();
    
    private:
    
    
};
#define MAKE_INPUT(attr)    \
    CHECK_MSTATUS(attr.setKeyable(true));       \
    CHECK_MSTATUS(attr.setStorable(true));      \
    CHECK_MSTATUS(attr.setReadable(true));      \
    CHECK_MSTATUS(attr.setWritable(true));
#define MAKE_OUTPUT(attr)   \
    CHECK_MSTATUS(attr.setKeyable(false));      \
    CHECK_MSTATUS(attr.setStorable(false));     \
    CHECK_MSTATUS(attr.setReadable(true));      \
    CHECK_MSTATUS(attr.setWritable(false));
void Flame3D::postConstructor( )
{
    setMPSafe(true);
}
Flame3D::Flame3D()
{
}
Flame3D::~Flame3D()
{
}
void * Flame3D::creator()
{
    return new Flame3D();
}
{
    
    MAKE_INPUT(nAttr);
    MAKE_INPUT(nAttr);
    MAKE_INPUT(nAttr);
    MAKE_INPUT(nAttr);
    MAKE_INPUT(nAttr);
    MAKE_INPUT(nAttr);
    MAKE_INPUT(nAttr);
    MAKE_INPUT(nAttr);
    
    aPlaceMat = mAttr.
create(
"placementMatrix", 
"pm",
    MAKE_INPUT(mAttr);
    
    MAKE_INPUT(nAttr);
    
    MAKE_OUTPUT(nAttr);
    MAKE_OUTPUT(nAttr);
    
    
}
{ 
    
    if((plug != aOutColor) && (plug.
parent() != aOutColor) && 
 
       (plug != aOutAlpha))
    
    q *= mat;                               
    
    float rise_distance = -1.0f * rise_speed * frame;
    float u,v,w;
    u = q.x + ( rise_distance * axis[0]);
    v = q.y + ( rise_distance * axis[1]);
    w = q.z + ( rise_distance * axis[2]);
    
    
    
    float dist = flicker_speed * frame;
    float au, av, aw;
    au = u + dist;
    av = v + dist;
    aw = w + dist;
    
    float ascale = Noise(au,av,aw);
    
    
    
    
    
    
    
    u += ascale * dscale;
    v += ascale * dscale;
    w += ascale * dscale;
    
    float scalar = (float) (turbulence(u, v, w, 3) + 0.5);
    
    if (power != 1) scalar = powf (scalar, power);
    if (scalar >= 1)
        resultColor = cFlame;
    else if (scalar < 0) 
        resultColor = cBase;
    else
        resultColor = ((cFlame-cBase)*scalar) + cBase;
    outColor = resultColor;
}
{
    const MString UserClassify( 
"texture/3d" );
 
    MFnPlugin plugin( obj, PLUGIN_COMPANY, 
"3.0", 
"Any" );
 
        Flame3D::creator, Flame3D::initialize,
    Noise_init();
    
}
{
}
float Noise(float u, float v, float w)
{
    int         i;
    int         j;
    int         k;
    int         ul;
    int         vl;
    int         wl;
    float       ans;
    float       t[3];
    ans = 0.0;
    ul  = FLOOR(u);
    vl  = FLOOR(v);
    wl  = FLOOR(w);
    for(i = ul + 1; i >= ul; i--)
    {  
        t[0] = u - i;
        for(j = vl + 1; j >= vl; j--)
        {   
            t[1] = v - j;
            for(k = wl + 1; k >= wl; k--)
            {
                t[2] = w - k;
                ans += Omega(i, j, k, t);
            }
        }
    }
    return ans;
}
static float Omega(int i, int j, int k, float t[3])
{
    int ct;
    ct = Phi[((i + 
         Phi[((j + 
         Phi[(k%TLD2)+TLD2]) % TLD2) + TLD2]) % TLD2) + TLD2];
    return omega(t[0]) * omega(t[1]) * omega(t[2]) *
        ( G[ct][0]*t[0] + G[ct][1]*t[1] + G[ct][2]*t[2] );
}
static float omega(float t)
{
    t  = fabsf(t);
    return (t * (t * (t * (float)2.0 - (float)3.0))) + (float)1.0;
}
void Noise_init()
{
    int i;
    float u, v, w, s, len;
    static int first_time = 1;
    if (first_time)
        first_time = 0;
    else
        return;
    (void)srand48(0l);
    for(i = 0; i < TABLELEN; i++)
        fPhi[i] = 0;
    for(i = 0; i < TABLELEN; i++) {
        Phi[i] = lrand48() % TABLELEN;
        if (fPhi[Phi[i]])
            i--;
        else
            fPhi[Phi[i]] = 1;
    }
    for(i = 0; i < TABLELEN; i++) {
        u = (float) (2.0 * drand48() - 1.0);
        v = (float) (2.0 * drand48() - 1.0);
        w = (float) (2.0 * drand48() - 1.0);
        if((s = u*u + v*v + w*w) > 1.0) 
        {   i--;
        continue;
        }
        else
            if (s == 0.0)
            {   i--;
            continue;
            }
        len = 1.0f / sqrtf(s);
        G[i][0] = u * len;
        G[i][1] = v * len;
        G[i][2] = w * len;
    }
}
static double turbulence(double u,double v,double w,int octaves)
{
    double s,t;
    s = 1.0;
    t = 0.0;
    while (octaves--) {
        t += Noise((float)u, (float)v, (float)w)*s;
        s *= 0.5;
        u*=2.0; v*=2.0; w*=2.0;
    }
    return t;
}