noiseShader.cpp

//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

#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/MFloatPoint.h>
#include <maya/MFloatVector.h>
#include <maya/MFloatMatrix.h>
#include <maya/MFnPlugin.h>


class noise3 : public MPxNode
{
    public:

    noise3();
    virtual ~noise3();

    virtual MStatus compute( const MPlug&, MDataBlock& );
    virtual void    postConstructor();

    static  void *  creator();
    static  MStatus initialize();

    //  Id tag for use with binary file format
    static MTypeId id;

    private:

    static void init();
    static float pnoise3( MFloatPoint& vec );
    static float pnoise3( float vx, float vy, float vz );

    // Input attributes
    static MObject aColor1;
    static MObject aColor2;
    static MObject aScale;
    static MObject aBias;
    static MObject aPlaceMat;
    static MObject aPointWorld;

    // Output attributes
    static MObject          aOutColor;
    static MObject          aOutAlpha;
};

// Static data
MTypeId noise3::id( 0x8100a );

// Attributes
MObject noise3::aColor1;
MObject noise3::aColor2;
MObject noise3::aPlaceMat;
MObject noise3::aPointWorld;
MObject noise3::aScale;
MObject noise3::aBias;
 
MObject noise3::aOutColor;
MObject noise3::aOutAlpha;

#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 noise3::postConstructor( )
{
    setMPSafe(true);
}

noise3::noise3()
{
}

noise3::~noise3()
{
}

// creates an instance of the node
void * noise3::creator()
{
    return new noise3();
}

// initializes attribute information
MStatus noise3::initialize()
{
    MFnMatrixAttribute mAttr; 
    MFnNumericAttribute nAttr; 

    // Create input attributes

    aColor1 = nAttr.createColor("color1", "c1");
    MAKE_INPUT(nAttr);
    CHECK_MSTATUS ( nAttr.setDefault(0., .58824, .644) );       // Light blue

    aColor2 = nAttr.createColor("color2", "c2");
    MAKE_INPUT(nAttr);
    CHECK_MSTATUS ( nAttr.setDefault(1., 1., 1.) );         // White

    aScale = nAttr.create( "scale", "s", MFnNumericData::kFloat);
    MAKE_INPUT(nAttr);
    CHECK_MSTATUS ( nAttr.setDefault( 1. ) );

    aBias = nAttr.create( "bias", "b", MFnNumericData::kFloat);
    MAKE_INPUT(nAttr);

    aPlaceMat = mAttr.create("placementMatrix", "pm",
                             MFnMatrixAttribute::kFloat);
    MAKE_INPUT(mAttr);

    // Implicit shading network attributes

    aPointWorld = nAttr.createPoint("pointWorld", "pw");
    MAKE_INPUT(nAttr);
    CHECK_MSTATUS ( nAttr.setHidden(true) );

    // Create output attributes

    aOutColor = nAttr.createColor("outColor", "oc");
    MAKE_OUTPUT(nAttr);

    aOutAlpha = nAttr.create( "outAlpha", "oa", MFnNumericData::kFloat);
    MAKE_OUTPUT(nAttr);

    // Add the attributes here
    CHECK_MSTATUS ( addAttribute(aColor1) );
    CHECK_MSTATUS ( addAttribute(aColor2) );
    CHECK_MSTATUS ( addAttribute(aScale) );
    CHECK_MSTATUS ( addAttribute(aBias) );
    CHECK_MSTATUS ( addAttribute(aPointWorld) );
    CHECK_MSTATUS ( addAttribute(aPlaceMat) );

    CHECK_MSTATUS ( addAttribute(aOutColor) );
    CHECK_MSTATUS ( addAttribute(aOutAlpha) );

    // All input affect the output color and alpha
    CHECK_MSTATUS ( attributeAffects (aColor1, aOutColor) );
    CHECK_MSTATUS ( attributeAffects (aColor1, aOutAlpha) );

    CHECK_MSTATUS ( attributeAffects (aColor2, aOutColor) ) ;
    CHECK_MSTATUS ( attributeAffects (aColor2, aOutAlpha) );

    CHECK_MSTATUS ( attributeAffects (aScale, aOutAlpha) );
    CHECK_MSTATUS ( attributeAffects (aScale, aOutColor) );

    CHECK_MSTATUS ( attributeAffects (aBias, aOutAlpha) );
    CHECK_MSTATUS ( attributeAffects (aBias, aOutColor) ); 

    CHECK_MSTATUS ( attributeAffects (aPointWorld, aOutColor) );
    CHECK_MSTATUS ( attributeAffects (aPointWorld, aOutAlpha) );

    CHECK_MSTATUS ( attributeAffects (aPlaceMat, aOutColor) );
    CHECK_MSTATUS ( attributeAffects (aPlaceMat, aOutAlpha) );

    return MS::kSuccess;
}

/* Ken Perlin */

#define DOT(a,b) (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) 
#define B 256 
static int p[B +B +2]; 
static float g[B + B + 2][3]; 
static int start = 1; 

#define setup(i,b0,b1,r0,r1) t = i + 10000.0f; b0 = ((int)t) & (B-1);  b1 = (b0+1) & (B-1);  r0 = t - (int)t;  r1 = r0 - 1.0f; 

inline float noise3::pnoise3( MFloatPoint& vec ) 
{
    return pnoise3( vec.x, vec.y, vec.z );
}

float noise3::pnoise3(float vx, float vy, float vz) 
{ 
    int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; 
    float rx0, rx1, ry0, ry1, rz0, rz1, *q, sx, sy, sz, a, b, c, d, t, u, v; 
    register int i, j; 

    if (start) 
    { 
        start = 0; 
        init(); 
    } 
    setup(vx, bx0,bx1, rx0,rx1); 
    setup(vy, by0,by1, ry0,ry1); 
    setup(vz, bz0,bz1, rz0,rz1); 
    i = p[ bx0 ]; 
    j = p[ bx1 ]; 
    b00 = p[ i + by0 ]; 
    b10 = p[ j + by0 ]; 
    b01 = p[ i + by1 ]; 
    b11 = p[ j + by1 ];

    #define at(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] ) 
    #define s_curve(t) ( t * t * (3.0f - 2.0f * t) ) 
    #define lerp(t, a, b) ( a + t * (b - a) ) 

    sx = s_curve(rx0); 
    sy = s_curve(ry0); 
    sz = s_curve(rz0); 
    q = g[ b00 + bz0 ] ; 
    u = at(rx0,ry0,rz0); 
    q = g[ b10 + bz0 ] ; 
    v = at(rx1,ry0,rz0); 
    a = lerp(sx, u, v); 
    q = g[ b01 + bz0 ] ; 
    u = at(rx0,ry1,rz0); 
    q = g[ b11 + bz0 ] ; 
    v = at(rx1,ry1,rz0); 
    b = lerp(sx, u, v); 
    c = lerp(sy, a, b);                     // interpolate in y at lo x
    q = g[ b00 + bz1 ] ; 
    u = at(rx0,ry0,rz1); 
    q = g[ b10 + bz1 ] ; 
    v = at(rx1,ry0,rz1); 
    a = lerp(sx, u, v); 
    q = g[ b01 + bz1 ] ; 
    u = at(rx0,ry1,rz1); 
    q = g[ b11 + bz1 ] ; 
    v = at(rx1,ry1,rz1); 
    b = lerp(sx, u, v); 
    d = lerp(sy, a, b);                     // interpolate in y at hi x
    return 1.5f * lerp(sz, c, d);           // interpolate in z
}

void noise3::init() 
{
    int i, j, k; 
    float v[3], s; 
 
    // Create an array of random gradient vectors uniformly on the
    // unit sphere
    srandom(1); 
    for (i = 0 ; i < B ; i++) 
    { 
        do 
        { 
            // Choose uniformly in a cube
            for (j=0 ; j<3 ; j++) 
                v[j] = (float)((random() % (B + B)) - B) / B; 
            s = DOT(v,v); 
        } while (s > 1.0);                  // If not in sphere try again
        s = sqrtf(s); 
        for (j = 0 ; j < 3 ; j++)           // Else normalize
            g[i][j] = v[j] / s; 
    } 

    // Create a pseudorandom permutation of [1..B]
    for (i = 0 ; i < B ; i++) 
        p[i] = i; 
    for(i=B ;i >0 ;i -=2)
    { 
        k = p[i]; 
        p[i] = p[j = random() % B]; 
        p[j] = k; 
    } 
    // Extend g and p arrays to allow for faster indexing
    for(i=0 ;i <B +2 ;i++) 
    { 
        p[B + i] = p[i]; 
        for (j = 0 ; j < 3 ; j++) 
            g[B + i][j] = g[i][j]; 
    }
}

//
// This function gets called by Maya to evaluate the texture.
//
MStatus noise3::compute(const MPlug& plug, MDataBlock& block) 
{
    // outColor or individial R, G, B channel, or alpha
    if((plug != aOutColor) && (plug.parent() != aOutColor) && 
       (plug != aOutAlpha))
        return MS::kUnknownParameter;

    MFloatVector resultColor;
    MFloatVector & col1 = block.inputValue(aColor1).asFloatVector();
    MFloatVector & col2 = block.inputValue(aColor2).asFloatVector();
    
    float3 & worldPos = block.inputValue( aPointWorld ).asFloat3();
    MFloatMatrix& mat = block.inputValue( aPlaceMat ).asFloatMatrix();
    float& sc = block.inputValue( aScale ).asFloat();
    float& bi = block.inputValue( aBias ).asFloat();

    MFloatPoint solidPos(worldPos[0], worldPos[1], worldPos[2]);
    solidPos *= mat;                        // Convert into solid space

    float val = fabsf( pnoise3( solidPos ) * sc + bi );
    if (val < 0.) val = 0.;
    if (val > 1.) val = 1.;
    resultColor = col1 * val + col2*(1-val);

    // Set output color attribute
    MDataHandle outColorHandle = block.outputValue( aOutColor );
    MFloatVector& outColor = outColorHandle.asFloatVector();
    outColor = resultColor;
    outColorHandle.setClean();

    MDataHandle outAlphaHandle = block.outputValue( aOutAlpha );
    float& outAlpha = outAlphaHandle.asFloat();
    outAlpha = val;
    outAlphaHandle.setClean();

    return MS::kSuccess;
}


MStatus initializePlugin( MObject obj )
{
    const MString UserClassify( "texture/3d" );

    MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");
    CHECK_MSTATUS ( plugin.registerNode( "solidNoise", noise3::id, 
                         &noise3::creator, &noise3::initialize,
                         MPxNode::kDependNode, &UserClassify ) );

    return MS::kSuccess;
}

MStatus uninitializePlugin( MObject obj )
{
    MFnPlugin plugin( obj );
    CHECK_MSTATUS ( plugin.deregisterNode( noise3::id ) );

    return MS::kSuccess;
}