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

# cellShader.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.
    // ==========================================================================
    //+

    // DESCRIPTION:
    //  3D cell texture plugin node for Maya
    //
    // Inputs:
    //  colorOffset - output color offset
    //  colorGain   - output color scale
    //  pointWorld  - world space sample point
    //  placementMatrix - world to texture space transform
    //
    // Output:
    //  F0      - distance to nearest cell center
    //  F1      - distance to 2nd nearest cell center
    //  borderDistance  - distance to border
    //  N0      - index of cell containing sample point
    //  outColor    - colorGain * F0 + colorOffset
    //

    #include <math.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>

    #ifdef LINUX
    #define fsqrt   sqrtf
    #include <stdlib.h>
    #endif

    class R3 {
        public:
        R3(float a, float b, float c): x(a), y(b), z(c) {}
        R3() {}
        public:
        float x;
        float y;
        float z;
    };

    static void initCellFunc();
    static void cellFunc(const R3 &, float &n0, float &f1, float &f2);

    class Cell3D : public MPxNode
    {
    public:
                        Cell3D();
        virtual         ~Cell3D();

        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:

        // Input attributes
        static MObject  aColorGain;
        static MObject  aColorOffset;
        static MObject  aPointWorld;
        static MObject  aPlaceMat;

        // Output attributes
        static MObject  aOutColor;
        static MObject  aOutAlpha;
        static MObject  aOutF0;
        static MObject  aOutF1;
        static MObject  aOutN0;
        static MObject  aOutBorderDist;
    };

    // Static data
    MTypeId Cell3D::id(0x81017);

    // Attributes

    MObject Cell3D::aColorGain;
    MObject Cell3D::aColorOffset;
    MObject Cell3D::aOutF0;
    MObject Cell3D::aOutF1;
    MObject Cell3D::aOutN0;
    MObject Cell3D::aOutBorderDist;
    MObject Cell3D::aPointWorld;
    MObject Cell3D::aPlaceMat;

    MObject Cell3D::aOutColor;
    MObject Cell3D::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) );

    //
    // DESCRIPTION:
    void Cell3D::postConstructor( )
    {
        setMPSafe(true);
    }

    //
    // DESCRIPTION:
    Cell3D::Cell3D()
    {
    }

    //
    // DESCRIPTION:
    Cell3D::~Cell3D()
    {
    }

    //
    // DESCRIPTION:
    void * Cell3D::creator()
    {
        return new Cell3D();
    }

    //
    // DESCRIPTION:
    MStatus Cell3D::initialize()
    {
        MFnMatrixAttribute mAttr;
        MFnNumericAttribute nAttr;

        // Input attributes

        aColorGain = nAttr.createColor("colorGain", "cg");
        MAKE_INPUT(nAttr);
        CHECK_MSTATUS( nAttr.setDefault(1.0f,1.0f,1.0f) );

        aColorOffset = nAttr.createColor("colorOffset", "co");
        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

        aOutF0 = nAttr.create( "F0", "f0", MFnNumericData::kFloat);
        MAKE_OUTPUT(nAttr);

        aOutF1 = nAttr.create( "F1", "f1", MFnNumericData::kFloat);
        MAKE_OUTPUT(nAttr);

        aOutN0 = nAttr.create( "N0", "n0", MFnNumericData::kFloat);
        MAKE_OUTPUT(nAttr);

        aOutBorderDist = nAttr.create("borderDistance", "bd",
                                      MFnNumericData::kFloat);
        MAKE_OUTPUT(nAttr);

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

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

        // Add attributes to the node database.

        CHECK_MSTATUS( addAttribute(aColorGain) );
        CHECK_MSTATUS( addAttribute(aColorOffset) );
        CHECK_MSTATUS( addAttribute(aPointWorld) );
        CHECK_MSTATUS( addAttribute(aPlaceMat) );

        CHECK_MSTATUS( addAttribute(aOutAlpha) );
        CHECK_MSTATUS( addAttribute(aOutColor) );
        CHECK_MSTATUS( addAttribute(aOutF0) );
        CHECK_MSTATUS( addAttribute(aOutF1) );
        CHECK_MSTATUS( addAttribute(aOutN0) );
        CHECK_MSTATUS( addAttribute(aOutBorderDist) );

        // All input affect the output color and alpha

        CHECK_MSTATUS( attributeAffects (aColorGain, aOutColor) );
        CHECK_MSTATUS( attributeAffects (aColorOffset, aOutColor) );
        CHECK_MSTATUS( attributeAffects (aPlaceMat, aOutColor) );
        CHECK_MSTATUS( attributeAffects (aPointWorld, aOutColor) );

        CHECK_MSTATUS( attributeAffects (aColorGain, aOutAlpha) );
        CHECK_MSTATUS( attributeAffects (aColorOffset, aOutAlpha) );
        CHECK_MSTATUS( attributeAffects (aPlaceMat, aOutAlpha) );
        CHECK_MSTATUS( attributeAffects (aPointWorld, aOutAlpha) );

        // Geometry attribute affect all other outputs.

        CHECK_MSTATUS( attributeAffects (aPlaceMat, aOutF0) );
        CHECK_MSTATUS( attributeAffects (aPointWorld, aOutF0) );

        CHECK_MSTATUS( attributeAffects (aPlaceMat, aOutF1) );
        CHECK_MSTATUS( attributeAffects (aPointWorld, aOutF1) );

        CHECK_MSTATUS( attributeAffects (aPlaceMat, aOutN0) );
        CHECK_MSTATUS( attributeAffects (aPointWorld, aOutN0) );

        CHECK_MSTATUS( attributeAffects (aPlaceMat, aOutBorderDist) );
        CHECK_MSTATUS( attributeAffects (aPointWorld, aOutBorderDist) );

        return MS::kSuccess;
    }

    // DESCRIPTION:
    // This function gets called by Maya to evaluate the texture.
    //

    MStatus Cell3D::compute(const MPlug& plug, MDataBlock& block)
    {
        if ( (plug != aOutColor) && (plug.parent() != aOutColor) &&
             (plug != aOutAlpha) &&
             (plug != aOutBorderDist) &&
             (plug != aOutF0) && (plug != aOutF1) && (plug != aOutN0)
           )
           return MS::kUnknownParameter;

        const float3& worldPos = block.inputValue(aPointWorld).asFloat3();
        const MFloatMatrix& m = block.inputValue(aPlaceMat).asFloatMatrix();
        const MFloatVector& cGain = block.inputValue(aColorGain).asFloatVector();
        const MFloatVector& cOff = block.inputValue(aColorOffset).asFloatVector();

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

        float n0, f0, f1;
        cellFunc(R3(q.x, q.y, q.z), n0, f0, f1);

        MDataHandle outHandle = block.outputValue(aOutF0);
        outHandle.asFloat() = f0;
        outHandle.setClean();

        outHandle = block.outputValue(aOutF1);
        outHandle.asFloat() = f1;
        outHandle.setClean();

        outHandle = block.outputValue(aOutN0);
        outHandle.asFloat() = n0;
        outHandle.setClean();

        outHandle = block.outputValue(aOutBorderDist);
        outHandle.asFloat() = 0.5f*(f1 - f0);
        outHandle.setClean();

        outHandle = block.outputValue( aOutColor );
        MFloatVector & outColor = outHandle.asFloatVector();
        outColor = cGain * f0 + cOff;
        outHandle.setClean();

        outHandle = block.outputValue(aOutAlpha);
        outHandle.asFloat() = f0;
        outHandle.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("cells", Cell3D::id,
                            Cell3D::creator, Cell3D::initialize,
                            MPxNode::kDependNode, &UserClassify) );

        initCellFunc();

        return MS::kSuccess;
    }

    // DESCRIPTION:
    MStatus uninitializePlugin( MObject obj )
    {
        MFnPlugin plugin( obj );
        CHECK_MSTATUS( plugin.deregisterNode( Cell3D::id ) );

        return MS::kSuccess;
    }

    #define N_CELLS 1000

    static int permuteTable[N_CELLS*2];

    //
    // Initialize permutation table used by fold()
    //

    static void initPermute()
    {
        int i;
        for (i = 0; i < N_CELLS; ++i) {
            permuteTable[i] = i;
        }
        for (i = N_CELLS - 1; i >= 1; --i) {
            int n = lrand48() % (i + 1);
            int tmp = permuteTable[n];
            permuteTable[n] = permuteTable[i];
            permuteTable[i] = tmp;
        }
        for (i = 0; i < N_CELLS; ++i) {
            permuteTable[i + N_CELLS] = permuteTable[i];
        }
    }

    //
    // Fold is a pseudo-random function mapping 3 integers
    // into a single integer in the range [0, (N_CELLS-1)].

    static int fold(int i, int j, int k)
    {
        //i %= N_CELLS; j %= N_CELLS; k %= N_CELLS;
        if (i < 0) i = (i + N_CELLS*(i/N_CELLS + 1)) % N_CELLS;
        else i %= N_CELLS;
        if (j < 0) j = (j + N_CELLS*(j/N_CELLS + 1)) % N_CELLS;
        else j %= N_CELLS;
        if (k < 0) k = (k + N_CELLS*(k/N_CELLS + 1)) % N_CELLS;
        else k %= N_CELLS;

        return permuteTable[permuteTable[permuteTable[i] + j] + k];
    }

    #define N_CELLS 1000

    static R3 CellSampleTable[N_CELLS];

    static void initCellFunc()
    {
        int i;
        srand48(10);                            // Make sure we always
                                                // compute the same random
                                                // numbers.
        for (i = 0; i < N_CELLS; ++i) {
        CellSampleTable[i] = R3((float)drand48(),
                                (float)drand48(),
                                (float)drand48());
        }
        initPermute();
    }

    static inline float sqr(float t) { return t*t; }

    static inline float distance2(const R3 &a, const R3 &b)
    {
        float t = sqr(b.x - a.x) + sqr(b.y - a.y) + sqr(b.z - a.z);
        return t;
    }

    static void cellFunc(const R3 &p, float &n0, float &f0, float &f1)
    {
        R3 q = p;
        int i = (int)floorf(q.x);
        int j = (int)floorf(q.y);
        int k = (int)floorf(q.z);
        q.x -= i;
        q.y -= j;
        q.z -= k;
        int index = fold(i,j,k);
        float minDist = distance2(CellSampleTable[index], q);
        float minDist2 = 2.0;
        int k0;
        k0 = index;

        // easy but slow way, check distance to point in each adjacent
        // cell to find closest and second closest.
        R3 q1;
        for (int ii = -1; ii <= 1; ++ii) {
            q1.x = q.x - ii;
            int i1 = i + ii;
            for (int jj = -1; jj <= 1; ++jj) {
                q1.y = q.y - jj;
                int j1 = j + jj;
                for (int kk = -1; kk <= 1; ++kk) {
                    if (!ii && !jj && !kk) continue;
                    q1.z = q.z - kk;
                    int k1 = k + kk;
                    index = fold(i1, j1, k1);
                    float t = distance2(CellSampleTable[index], q1);
                    if (minDist > t) {
                        minDist2 = minDist;
                        minDist = t;
                        k0 = index;
                    }
                    else if (minDist2 > t) {
                        minDist2 = t;
                    }
                }
            }
        }
        f0 = sqrtf(minDist);
        f1 = sqrtf(minDist2);
        n0 = k0/(float)(N_CELLS);
    }