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

# hwReflectBumpShader_NV20.cpp

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

    //
    // NOTE: PLEASE READ THE README.TXT FILE FOR INSTRUCTIONS ON
    // COMPILING AND USAGE REQUIREMENTS.
    //
    // DESCRIPTION: NV20-specific (Geforce3) sample shader.
    //              This shader can simultaneously display a
    //              bumpy, reflective surface. The bump
    //              is controlled through a user-specified
    //              2D texture, while the reflection map
    //              is a cube map.
    //
    //  This shader builds on the foundation demonstrated in
    //  hwUnlitShader.
    //
    //  Additionally, this sample demonstrates how to:
    //      - Use vendor-specific extensions, namely vertex programs,
    //        texture shaders and register combiners, to achieve
    //        effects that are impossible in standard OpenGL.
    //      - Convert height field bump format (used by Maya) into
    //        a normal map format, for real-time rendering.
    //
    //  Many parameters are easily customizable:
    //      - The MNormalMapConverter::convertToNormalMap_InPlace()
    //        bumpScale parameter is currently constant. You can change
    //        it to a different value to increase or decrease the
    //        bumpiness.
    //

    #ifdef WIN32
    #pragma warning( disable : 4786 )       // Disable STL warnings.
    #endif

    #include <maya/MIOStream.h>
    #include <math.h>

    #include <maya/MString.h>
    #include <maya/MStringArray.h>
    #include <maya/MPlug.h>
    #include <maya/MDataBlock.h>
    #include <maya/MDataHandle.h>
    #include <maya/MArrayDataHandle.h>
    #include <maya/MFnNumericAttribute.h>
    #include <maya/MFnTypedAttribute.h>
    #include <maya/MFloatVector.h>
    #include <maya/MFnStringData.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MGlobal.h>

    #include <maya/MPoint.h>
    #include <maya/MMatrix.h>
    #include <maya/MVector.h>
    #include <maya/MEulerRotation.h>
    #include <maya/MQuaternion.h>

    #include <maya/MDagPath.h>
    #include <maya/MFnDependencyNode.h>
    #include <maya/MSceneMessage.h>

    // Include NVIDIA's helper libraries.  These libraries have
    // copyright info in them so we cannot release them but we
    // can use them to verify that the API works correctly.
    //
    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <GL/glext.h>

    #define GLH_EXT_SINGLE_FILE
    #include "glh_extensions.h"
    #undef GL_NV_vertex_array_range
    #include "glh_genext.h"
    #include "glh_obs.h"

    using namespace glh;

    #include "hwReflectBumpShader_NV20.h"
    #include "ShadingConnection.h"

    MTypeId hwReflectBumpShader_NV20::id( 0x00105442 );

    void hwReflectBumpShader_NV20::postConstructor( )
    {
        setMPSafe(false);
    }

    //
    // DESCRIPTION:
    MObject  hwReflectBumpShader_NV20::color;
    MObject  hwReflectBumpShader_NV20::colorR;
    MObject  hwReflectBumpShader_NV20::colorG;
    MObject  hwReflectBumpShader_NV20::colorB;

    MObject  hwReflectBumpShader_NV20::bump;
    MObject  hwReflectBumpShader_NV20::bumpR;
    MObject  hwReflectBumpShader_NV20::bumpG;
    MObject  hwReflectBumpShader_NV20::bumpB;

    MObject  hwReflectBumpShader_NV20::uCoord;
    MObject  hwReflectBumpShader_NV20::vCoord;
    MObject  hwReflectBumpShader_NV20::uvCoord;

    MObject  hwReflectBumpShader_NV20::uBias;
    MObject  hwReflectBumpShader_NV20::vBias;

    MObject  hwReflectBumpShader_NV20::uvFilterSize;
    MObject  hwReflectBumpShader_NV20::uvFilterSizeX;
    MObject  hwReflectBumpShader_NV20::uvFilterSizeY;

    char gszErrMsg[100];          // Global error message text

    void hwReflectBumpShader_NV20::printGlError( const char *call )
    {
        GLenum error;

        while( (error = glGetError()) != GL_NO_ERROR ) {
            cerr << call << ":" << error << " is " << (const char *)gluErrorString( error ) << "\n";
        }
    }

    // Verify that the configuration of the texture shaders are consistent
    //
    void hwReflectBumpShader_NV20::verify_shader_config(M3dView& view)
    {
        int consistent;

        view.beginGL();

            glActiveTextureARB( GL_TEXTURE0_ARB );
            glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
            if(consistent == GL_FALSE)
                cerr << "Shader stage 0 is inconsistent!" << endl;

            glActiveTextureARB( GL_TEXTURE1_ARB );
            glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
            if(consistent == GL_FALSE)
                cerr << "Shader stage 1 is inconsistent!" << endl;

            glActiveTextureARB( GL_TEXTURE2_ARB );
            glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
            if(consistent == GL_FALSE)
                cerr << "Shader stage 2 is inconsistent!" << endl;

            glActiveTextureARB( GL_TEXTURE3_ARB );
            glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
            if(consistent == GL_FALSE)
                cerr << "Shader stage 3 is inconsistent!" << endl;

            glActiveTextureARB( GL_TEXTURE0_ARB );

        view.endGL();
    }

    // The very simple VertexProgram for the Reflective Bump effect. This one is faster
    // (it doesn't require the tangent space calculation) but is world-aligned.
    // Therefore it could be useful for some effects (ex: ground or wall), but for
    // a character it would be unnacceptable.
    //
    // CONSTANTS:
    //  0- 3  4x4 ModelView-Projection composite matrix
    //  4- 7  4x4 ModelView  matrix
    // 24-27  4x4 view transpose
    //
    // VERTEX REGISTERS:
    // 0 - coord
    // 1 - normal
    // 2 - texcoord0
    //
    // REGISTERS:
    // 4 = skinned (eye space) coord
    // 5 = skinned (eye space) tangent
    // 6 = skinned (eye space) binormal
    // 7 = skinned (eye space) normal
    //
    char superEasyVertexProgramString[] =
            "!!VP1.0\n"

            // final projection transformation
            // transform the skinned coords by the projection matrix
            "DP4   o[HPOS].x, c[0], v[0];"
            "DP4   o[HPOS].y, c[1], v[0];"
            "DP4   o[HPOS].z, c[2], v[0];"
            "DP4   o[HPOS].w, c[3], v[0];"

            // transform the coords to the eye-space
            "DP4   R4.x, c[4], v[0];"
            "DP4   R4.y, c[5], v[0];"
            "DP4   R4.z, c[6], v[0];"
            "DP4   R4.w, c[7], v[0];"

            // transform the normals to eye-space
            "DP3   R7.x, c[4], v[1];"
            "DP3   R7.y, c[5], v[1];"
            "DP3   R7.z, c[6], v[1];"
            "DP3   R7.w, c[7], v[1];"

            // transform the normals from eye-space to world-space
            "DP3 o[TEX1].x, R7, c[24];"
            "DP3 o[TEX2].y, R7, c[25];"
            "DP3 o[TEX3].z, R7, c[26];"

            // put view dir into w of tex[1..3]
            "DP4 o[TEX1].w, R4, c[24];"
            "DP4 o[TEX2].w, R4, c[25];"
            "DP4 o[TEX3].w, R4, c[26];"

            // copy texcoords
            "MOV o[TEX0], v[2];"

            // done
            "END";

    // More complex vertex program. It uses tangent space transformations to
    // achieve a more realistic bump.
    //
    // CONSTANTS:
    //  0- 3  4x4 Projection matrix
    //  4- 7  4x4 ModelView  matrix
    // 20-22  light amb/diff/spec
    // 23     light dir
    // 24-27  4x4 view transpose
    //
    // VERTEX REGISTERS:
    // 0 - coord
    // 1 - normal
    // 2 - texcoord0
    // 3 - texcoord1
    // 4 - texcoord2 (binorm)
    //
    // REGISTERS:
    // 4  = skinned (eye space) coord
    // 5 = skinned (eye space) tangent
    // 6 = skinned (eye space) binormal
    // 7 = skinned (eye space) normal
    // 8 = eye space view vector
    // 9 = eye space half-angle vector
    char originalVertexProgramString[] =
        "!!VP1.0\n"

        // skin the vertices
        "DP4   R4.x, c[4], v[0];"
        "DP4   R4.y, c[5], v[0];"
        "DP4   R4.z, c[6], v[0];"
        "DP4   R4.w, c[7], v[0];"

        // final projection transformation
        // transform the skinned coords by the projection matrix
        "DP4   o[HPOS].x, c[0], v[0];"
        "DP4   o[HPOS].y, c[1], v[0];"
        "DP4   o[HPOS].z, c[2], v[0];"
        "DP4   o[HPOS].w, c[3], v[0];"

        // skin the binormals
        // skin binormals for bone0
        "DP3   R6.x, c[4], v[4];"
        "DP3   R6.y, c[5], v[4];"
        "DP3   R6.z, c[6], v[4];"
        "DP3   R6.w, c[7], v[4];"

        // skin the normals
        // skin normals for bone0
        "DP3   R7.x, c[4], v[1];"
        "DP3   R7.y, c[5], v[1];"
        "DP3   R7.z, c[6], v[1];"
        "DP3   R7.w, c[7], v[1];"

        // renormalize and orthogonalize binormal, tangent & normal
        // build tangent
        "MUL   R5, R6.zxyw, R7.yzxw;"
        "MAD   R5, R6.yzxw, R7.zxyw, -R5;"

        // normalize tangent
        "DP3   R5.w, R5, R5;"
        "RSQ   R5.w, R5.w;"
        "MUL   R5.xyz, R5, R5.w;"

        // put the sign in the tangent
        "MUL R5.xyz, R5, v[4].w;"

        // fill texture coords with tangent space matrix for pixel shaders
        // rotate tangent space matrix by view transpose
        "DP3 o[TEX1].x, -R5, c[24];"
        "DP3 o[TEX2].x, -R5, c[25];"
        "DP3 o[TEX3].x, -R5, c[26];"

        "DP3 o[TEX1].y, -R6, c[24];"
        "DP3 o[TEX2].y, -R6, c[25];"
        "DP3 o[TEX3].y, -R6, c[26];"

        "DP3 o[TEX1].z, R7, c[24];"
        "DP3 o[TEX2].z, R7, c[25];"
        "DP3 o[TEX3].z, R7, c[26];"

        // put view dir into w of tex[1..3]
        "DP4 o[TEX1].w, -R4, c[24];"
        "DP4 o[TEX2].w, -R4, c[25];"
        "DP4 o[TEX3].w, -R4, c[26];"

        // misc
        // put diffuse lighting into color
        "DP3 o[COL0], R7, c[23];"
        "MOV o[COL0].w, c[50];"

        // copy texcoords
        "MOV o[TEX0], v[3];"

        // done
        "END";

    void initVertexProgram(const char vertexProgramCode[], GLuint* pVertexProgramId)
    {
        // Allocate and initialize the vertex program.
        glGenProgramsNV(1, pVertexProgramId);
        GLenum error = glGetError();
        assert(error == GL_NO_ERROR);

        // Load the program.
        unsigned int length = strlen(vertexProgramCode);
        glLoadProgramNV(GL_VERTEX_PROGRAM_NV, *pVertexProgramId, length,
            (const GLubyte *) vertexProgramCode);
        error = glGetError();

        // If an error occured, find the location in the vertex program
        // code and assert.
        if (error != GL_NO_ERROR)
        {
            // If an error occured, it's most likely due to a syntax or
            // logic error in the vertex program. The error position
            // below will contain the index in the vertex program
            // string that is faulty. See the NV_vertex_program
            // extension specification for more details.
            if (error == GL_INVALID_OPERATION)
            {
                int error_position = -2;

                glGetIntegerv(GL_PROGRAM_ERROR_POSITION_NV, &error_position);

                // Most likely a bug in the vertex program code...
                assert(0);
            }
        }
    }

    // Load the vertexProgram and fill in the necessary constants used in the vertex program
    //
    void hwReflectBumpShader_NV20::loadVertexProgramGL()
    {
        // Only load this vertex program once.
        if (vertex_program_id == 0)
            initVertexProgram(originalVertexProgramString, &vertex_program_id);

        // CONSTANTS:
        //  0- 3  4x4 ModelView-Projection composite matrix
        //  4- 7  4x4 ModelView  matrix
        // 20-22  light amb/diff/spec
        // 23     light dir
        // 24-27  4x4 view transpose
        //
        // VERTEX REGISTERS:
        // 0 - coord
        // 1 - normal
        // 2 - texcoord0
        //
        // In this example, the upper-left 3x3 of the modelview matrix (M) and
        // the upper-left 3x3 of the inverse transpose of the modelview matrix (M-t)
        // are used interchangeably.  This is because the modelview matrix contains
        // only rigid-body transformations (rotation and translation), and in this
        // case the matrices are identical.
        //
        glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV);
        glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4, GL_MODELVIEW, GL_IDENTITY_NV);

        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 20, 1.0, 1.0, 1.0, 1.0);   // light amb color
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 21, 1.0, 1.0, 1.0, 1.0);   // light diff color
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 22, 1.0, 1.0, 1.0, 1.0);   // light spec color
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 23, 0.0, 0.0, 1.0, 0.0);   // light direction, for now.

        // Get the modelView matrix
        //
        GLfloat modelViewMatrix[16];
        glGetFloatv(GL_MODELVIEW_MATRIX, modelViewMatrix);
        float stupidMatrix[4][4];
        for (int i=0; i<16; i++)
        {
            stupidMatrix[i/4][i%4] = modelViewMatrix[i];
        }
        MMatrix mvMatrix(stupidMatrix);

        // Calculate the view transpose matrix.
        //
        MMatrix mv = m_ModelMatrix.inverse() * mvMatrix;
        mv = mv.transpose();

        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 24, mv[0][0], mv[1][0], mv[2][0], mv[3][0]);
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 25, mv[0][1], mv[1][1], mv[2][1], mv[3][1]);
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 26, mv[0][2], mv[1][2], mv[2][2], mv[3][2]);
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 27, mv[0][3], mv[1][3], mv[2][3], mv[3][3]);
    }

    // Initialize the necessary OpenGL extensions
    //
    void hwReflectBumpShader_NV20::init_ext(const char * ext)
    {
        if(!glh_init_extension(ext))
        { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); }
    }

    hwReflectBumpShader_NV20::hwReflectBumpShader_NV20()
    {
        m_pTextureCache = MTextureCache::instance();

        init_ext("GL_ARB_multitexture");
        init_ext("GL_NV_register_combiners");
        init_ext("GL_NV_texture_shader");
        init_ext("GL_NV_vertex_program");

        bumpScale = 1.0;
        cubeMapOnly = FALSE;

        texNames[0] = texNames[1] = texNames[2] = texNames[3] = texNames[4] = texNames[5] = 0;
        currentTextureNames[0] = "";
        currentTextureNames[1] = "";
        currentTextureNames[2] = "";
        currentTextureNames[3] = "";
        currentTextureNames[4] = "";
        currentTextureNames[5] = "";

        attachSceneCallbacks();

        vertex_program_id = 0;
    }

    hwReflectBumpShader_NV20::~hwReflectBumpShader_NV20()
    {
        detachSceneCallbacks();
    }

    void releaseVertexProgram(GLuint* pVertexProgramId)
    {
        // If the vertex program id is set...
        if (*pVertexProgramId > 0)
        {
            // Unbind any vertex program...
            glBindProgramNV(GL_VERTEX_PROGRAM_NV, 0);

            glDeleteProgramsNV(1, pVertexProgramId);

            // For sanity, set the id to 0.
            *pVertexProgramId = 0;
        }
    }

    void hwReflectBumpShader_NV20::releaseEverything()
    {
        if (texNames[0] != 0)
            glDeleteTextures(6, &texNames[0]);

        releaseVertexProgram(&vertex_program_id);

        // Release the texture cache through refcounting.
        m_pTextureCache->release();
        if(!MTextureCache::getReferenceCount())
        {
            m_pTextureCache = 0;
        }
    }

    void hwReflectBumpShader_NV20::attachSceneCallbacks()
    {
        fBeforeNewCB  = MSceneMessage::addCallback(MSceneMessage::kBeforeNew,  releaseCallback, this);
        fBeforeOpenCB = MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, releaseCallback, this);
        fBeforeRemoveReferenceCB = MSceneMessage::addCallback(MSceneMessage::kBeforeRemoveReference,
                                                              releaseCallback, this);
        fMayaExitingCB = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, releaseCallback, this);
    }

    /*static*/
    void hwReflectBumpShader_NV20::releaseCallback(void* clientData)
    {
        hwReflectBumpShader_NV20 *pThis = (hwReflectBumpShader_NV20*) clientData;
        pThis->releaseEverything();
    }

    void hwReflectBumpShader_NV20::detachSceneCallbacks()
    {
        if (fBeforeNewCB)
            MMessage::removeCallback(fBeforeNewCB);
        if (fBeforeOpenCB)
            MMessage::removeCallback(fBeforeOpenCB);
        if (fBeforeRemoveReferenceCB)
            MMessage::removeCallback(fBeforeRemoveReferenceCB);
        if (fMayaExitingCB)
            MMessage::removeCallback(fMayaExitingCB);

        fBeforeNewCB = 0;
        fBeforeOpenCB = 0;
        fBeforeRemoveReferenceCB = 0;
        fMayaExitingCB = 0;
    }

    MStatus initializePlugin( MObject obj )
    {
        MStatus   status;

        const MString UserClassify( "shader/surface/utility" );

        MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.0", "Any");
        status = plugin.registerNode( "hwReflectBumpShader_NV20", hwReflectBumpShader_NV20::id,
                                      hwReflectBumpShader_NV20::creator, hwReflectBumpShader_NV20::initialize,
                                      MPxNode::kHwShaderNode, &UserClassify );
        if (!status) {
            status.perror("registerNode");
            return status;
        }

        return MS::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj )
    {
        MStatus   status;

        MFnPlugin plugin( obj );

        // Unregister all chamelion shader nodes
        plugin.deregisterNode( hwReflectBumpShader_NV20::id );
        if (!status) {
            status.perror("deregisterNode");
            return status;
        }

        return MS::kSuccess;
    }

    void * hwReflectBumpShader_NV20::creator()
    {
        return new hwReflectBumpShader_NV20();
    }

    MStatus hwReflectBumpShader_NV20::initialize()
    {
        MFnNumericAttribute nAttr;
        MStatus status;
        MFnTypedAttribute sAttr; // For string attributes

        // Create input attrubutes

        colorR = nAttr.create( "colorR", "cr",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f);

        colorG = nAttr.create( "colorG", "cg",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        colorB = nAttr.create( "colorB", "cb",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        color = nAttr.create( "color", "c", colorR, colorG, colorB);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f, 0.5f, 0.5f);
        nAttr.setUsedAsColor(true);

        bumpR = nAttr.create( "bumpR", "c2r",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f);

        bumpG = nAttr.create( "bumpG", "c2g",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f);

        bumpB = nAttr.create( "bumpB", "c2b",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f);

        bump = nAttr.create( "bump", "c2", bumpR, bumpG, bumpB);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f, 1.0f, 1.0f);
        nAttr.setUsedAsColor(true);

        uCoord = nAttr.create( "uCoord", "u", MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        vCoord = nAttr.create( "vCoord", "v", MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        uvCoord = nAttr.create( "uvCoord","uv", uCoord, vCoord);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f, 0.5f );
        nAttr.setHidden(true);

        uBias = nAttr.create( "uBias", "bu", MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setMin(0.0f);
        nAttr.setMax(1.0f);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        vBias = nAttr.create( "vBias", "bv", MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setMin(0.0f);
        nAttr.setMax(1.0f);
        nAttr.setDefault(0.5f);

        uvFilterSizeX = nAttr.create( "uvFilterSizeX", "fsx", MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setReadable(true);
        nAttr.setWritable(true);
        nAttr.setHidden(true);

        uvFilterSizeY = nAttr.create( "uvFilterSizeY", "fsy", MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setReadable(true);
        nAttr.setWritable(true);
        nAttr.setHidden(true);

        uvFilterSize = nAttr.create("uvFilterSize","fs",uvFilterSizeX,uvFilterSizeY);
        nAttr.setStorable(false);
        nAttr.setReadable(true);
        nAttr.setWritable(true);
        nAttr.setHidden(true);

     // create output attributes here
        // outColor is the only output attribute and it is inherited
        // so we do not need to create or add it.
        //

     // Add the attributes here

        addAttribute(color);
        addAttribute(bump);

        addAttribute(uvCoord);

        addAttribute(uBias);
        addAttribute(vBias);

        addAttribute(uvFilterSize);

        attributeAffects (colorR, outColor);
        attributeAffects (colorG, outColor);
        attributeAffects (colorB, outColor);
        attributeAffects (color,  outColor);
        attributeAffects (bumpR, outColor);
        attributeAffects (bumpG, outColor);
        attributeAffects (bumpB, outColor);
        attributeAffects (bump,  outColor);
        attributeAffects (uCoord,  outColor);
        attributeAffects (vCoord,  outColor);
        attributeAffects (uvCoord, outColor);
        attributeAffects (uBias,   outColor);
        attributeAffects (vBias,   outColor);

        return MS::kSuccess;
    }

    // DESCRIPTION:
    //   See hwDecalBumpShader_NV20::compute().
    //
    MStatus hwReflectBumpShader_NV20::compute(
    const MPlug&      plug,
          MDataBlock& block )
    {
        bool k = false;
        k |= (plug==outColor);
        k |= (plug==outColorR);
        k |= (plug==outColorG);
        k |= (plug==outColorB);
        if( !k ) return MS::kUnknownParameter;

        // set output color attribute
        MDataHandle outColorHandle = block.outputValue( outColor );
        MFloatVector& outColor = outColorHandle.asFloatVector();
        outColor.x = 1.0;
        outColor.y = 0.5;
        outColor.z = 0.5;
        outColorHandle.setClean();
        return MS::kSuccess;
    }

    // To get 3 float values from the node attribute
    //
    MStatus hwReflectBumpShader_NV20::getFloat3(MObject attr, float value[3])
    {
        MStatus status = MS::kSuccess;

        // Get the attr to use
        //
        MPlug   plug(thisMObject(), attr);

        MObject object;
        status = plug.getValue(object);
        if (!status)
        {
            status.perror("hwReflectBumpShader_NV20::bind plug.getValue.");
            return status;
        }

        MFnNumericData data(object, &status);
        if (!status)
        {
            status.perror("hwReflectBumpShader_NV20::bind construct data.");
            return status;
        }

        status = data.getData(value[0], value[1], value[2]);
        if (!status)
        {
            status.perror("hwReflectBumpShader_NV20::bind get values.");
            return status;
        }

        return status;
    }

    // To get a string value from the node attribute
    //
    MStatus hwReflectBumpShader_NV20::getString(MObject attr, MString &str)
    {
        MPlug   plug(thisMObject(), attr);
        MStatus status = plug.getValue( str );

        return status;
    }

    /* virtual */
    MStatus hwReflectBumpShader_NV20::bind(const MDrawRequest& request,
                                M3dView& view)
    {
        MStatus status;

        bool isHeightFieldMap = true;   // Should be set to the value of an attribute

        m_ModelMatrix = request.multiPath().inclusiveMatrix();

        // Get the cube mapand bump map file names
        MStringArray decalNames;
        MString decalName;
        MString bumpName;

        ShadingConnection colorConnection(thisMObject(),
                            request.multiPath().partialPathName(), "color");
        ShadingConnection bumpConnection (thisMObject(),
                            request.multiPath().partialPathName(), "bump");

        // If the color attribute is ultimately connected to a environment,
        // find its filenames, otherwise use the default color texture.

        bool gotAllEnvironmentMaps = TRUE;
        if (colorConnection.type() == ShadingConnection::TEXTURE &&
            colorConnection.texture().hasFn(MFn::kEnvCube))
        {
            // Get the filenames of the texture.
            MFnDependencyNode textureNode(colorConnection.texture());
            MString attributeName;
            MString envNames[6] = { "top", "bottom", "left", "right", "front", "back" };

            // Scan for connected file textures to the environment map node
            //
            for (int i=0; i<6; i++)
            {
                ShadingConnection conn(colorConnection.texture(), request.multiPath().partialPathName(),
                                envNames[i]);

                if (conn.type() == ShadingConnection::TEXTURE &&
                    conn.texture().hasFn(MFn::kFileTexture))
                {
                    MFnDependencyNode envNode(conn.texture());
                    MPlug filenamePlug( conn.texture(), envNode.attribute(MString("fileTextureName")) );
                    filenamePlug.getValue(decalName);

                    if (decalName.length() == 0)
                        decalName = "D:/chameleon/textures/Cham_body_color_real.tga";

                    // Append next environment map name
                    decalNames.append( decalName );
                }

                // If any of the environment maps are not mapped put in a fake texture
                else
                {
                    decalName = "D:/chameleon/textures/Cham_body_color_real.tga";
                    decalNames.append( decalName );
                }
            }
        }
        else
        {
            // Put in a fake texture for each side
            decalName = "D:/chameleon/textures/Cham_body_color_real.tga";
            for (int i=0; i<6; i++)
            {
                decalNames.append( decalName );
            }
        }

        // If the bump attribute is ultimately connected to a file texture, find its filename.
        // otherwise use the default bump texture.
        if (bumpConnection.type() == ShadingConnection::TEXTURE &&
            bumpConnection.texture().hasFn(MFn::kFileTexture))
        {
            // Get the filename of the texture.
            MFnDependencyNode textureNode(bumpConnection.texture());
            MPlug filenamePlug( bumpConnection.texture(), textureNode.attribute(MString("fileTextureName")) );
            filenamePlug.getValue(bumpName);
        }
        else
        {
            bumpName = "";
        }

        // See if we are doing cube-map only. i.e. no bump
        //
        cubeMapOnly = (bumpName.length() == 0);

        // Reload cube maps if the name of the textures
        // for any of the cube maps changes
        //
        unsigned int width, height;

        bool reload = FALSE;
        for (int i=0; i<6; i++)
        {
            if (currentTextureNames[i] != decalNames[i])
            {
                reload = TRUE;
                break;
            }
        }
        if ( reload )
        {
            MString xpTexName(decalNames[2]);
            MString xnTexName(decalNames[3]);
            MString ypTexName(decalNames[0]);
            MString ynTexName(decalNames[1]);
            MString zpTexName(decalNames[4]);
            MString znTexName(decalNames[5]);

            MStatus stat;

            if (! (stat = theImage_XP.readFromFile(xpTexName)) )
                return MS::kFailure;
            stat = theImage_XP.getSize( width, height );

            if (! (stat = theImage_XN.readFromFile(xnTexName)) )
                return MS::kFailure;
            if (! (stat = theImage_YP.readFromFile(ypTexName)) )
                return MS::kFailure;
            if (! (stat = theImage_YN.readFromFile(ynTexName)) )
                return MS::kFailure;
            if (! (stat = theImage_ZP.readFromFile(zpTexName)) )
                return MS::kFailure;
            if (! (stat = theImage_ZN.readFromFile(znTexName)) )
                return MS::kFailure;

            // Only create texture names the first time
            if (texNames[0] == 0)
                glGenTextures(6, &texNames[0]);

            glBindTexture( GL_TEXTURE_2D, texNames[0] );
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
                0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XP.pixels() );
            glBindTexture( GL_TEXTURE_2D, texNames[1] );
            glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
                0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XN.pixels() );
            glBindTexture( GL_TEXTURE_2D, texNames[2] );
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
                0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YP.pixels() );
            glBindTexture( GL_TEXTURE_2D, texNames[3] );
            glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
                0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YN.pixels() );
            glBindTexture( GL_TEXTURE_2D, texNames[4] );
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
                0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZP.pixels() );
            glBindTexture( GL_TEXTURE_2D, texNames[5] );
            glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
                0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZN.pixels() );

            for (i=0; i<6; i++) {
                currentTextureNames[i] = decalNames[i];
            }
        }

        // Get camera information needed
        //
        MDagPath cameraPath;
        status = view.getCamera( cameraPath );

        // Get rotation angle and axis
        //
        MVector camAxis;
        double camTheta;
        MMatrix mmatrix = cameraPath.inclusiveMatrix( &status );
        MTransformationMatrix tmatrix( mmatrix );

        m_CameraRotation = tmatrix.rotation();
        m_CameraRotation.getAxisAngle(  camAxis, camTheta );

        // Convert to degrees from radians
        camTheta *= 57.295779513082320876798154814105;  // == (180 / M_PI)

        view.beginGL();

        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);

        // Background color is always white
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
        glEnable(GL_COLOR_MATERIAL);
        glColor4f( 1, 1, 1, 1 );

        if (cubeMapOnly)
        {
            glActiveTextureARB( GL_TEXTURE0_ARB );

            glEnable(GL_TEXTURE_CUBE_MAP_ARB);
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
            glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
            glEnable(GL_TEXTURE_GEN_S);
            glEnable(GL_TEXTURE_GEN_T);
            glEnable(GL_TEXTURE_GEN_R);

            for (i=0; i<6; i++)
                glBindTexture( GL_TEXTURE_2D, texNames[i] );

            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

            // Flip from Maya to OGL coordinates +
            // rotate the Textures according to the camera orientation
            //
            glMatrixMode( GL_TEXTURE );
            glPushMatrix();
            glLoadIdentity();
            glRotated( 180.0, 1.0, 0.0, 0.0 );
            glRotated( camTheta, camAxis[0], camAxis[1], camAxis[2]);

            // Pop the matrix is done during unbind, not here
            //glPopMatrix();

            glMatrixMode( GL_MODELVIEW );
        }
        else
        {
            loadVertexProgramGL();

            // Setup texture combiners
            //
            glEnable(GL_TEXTURE_SHADER_NV);

            // stage 0 -- bump normal map (input is u,v and normal map)
            glActiveTextureARB( GL_TEXTURE0_ARB );
            glEnable(GL_TEXTURE_2D);
            //
            // We need to be able to pass the bumpScaleValue
            // to the texture cache and rebuild the bump or normal map
            if( isHeightFieldMap ) {
                // convert the HeightField to the NormalMap
                if(m_pTextureCache)
                    m_pTextureCache->bind(bumpConnection.texture(), MTexture::NMAP, true);
            }
            else {
                if(m_pTextureCache)
                    m_pTextureCache->bind(bumpConnection.texture(), MTexture::RGBA, true);
            }
            glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);

            // stage 1 -- dot product (input is strq)
            glActiveTextureARB( GL_TEXTURE1_ARB );
            glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV);
            glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);

            // stage 2 -- dot product (input is strq)
            glActiveTextureARB( GL_TEXTURE2_ARB );
            glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV);
            glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);

            // stage 3 -- dot product reflect cube map (input is strq, and cube maps)
            // ======================================================================
            glActiveTextureARB( GL_TEXTURE3_ARB );
            glEnable(GL_TEXTURE_CUBE_MAP_ARB);

            glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV);
            glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

            // Bind the 6 textures
            //
            for (i=0; i<6; i++)
                glBindTexture( GL_TEXTURE_2D, texNames[i] );

            // Specify the texture parameters
            //
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

            // Done setting the texture unit 3
            //
            glActiveTextureARB( GL_TEXTURE0_ARB );

            // define a white color
            //
            float white_color[4] = {1.0, 1.0, 1.0, 1.0};
            glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, white_color);

            // The register combiner will do the multiplication between
            // the illumination and the decal color
            //
            glEnable(GL_REGISTER_COMBINERS_NV);
            glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);

            // Combiner stage 0 get the input from texture stage3, pass through
            //
            glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
            glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
            glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
            glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

            glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
                GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

            // The final Combiner just pass through
            //
            glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
            glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
            glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
            glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

            verify_shader_config( view );
        }

        view.endGL();

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwReflectBumpShader_NV20::unbind(const MDrawRequest& request,
                   M3dView& view)
    {
        view.beginGL();

        if (cubeMapOnly)
        {
            // Pop the texture matrix pushed during bind
            glActiveTextureARB( GL_TEXTURE0_ARB );
            glMatrixMode( GL_TEXTURE );
            glPopMatrix();
            glMatrixMode( GL_MODELVIEW );

            glDisable( GL_TEXTURE_GEN_S );
            glDisable( GL_TEXTURE_GEN_T );
            glDisable( GL_TEXTURE_GEN_R );
            glDisable( GL_TEXTURE_CUBE_MAP_ARB );
        }
        else
        {
            glDisable(GL_TEXTURE_SHADER_NV);

            glActiveTextureARB( GL_TEXTURE0_ARB );
            glDisable(GL_TEXTURE_2D);
            glDisable(GL_TEXTURE_CUBE_MAP_ARB);

            glActiveTextureARB( GL_TEXTURE1_ARB );
            glDisable(GL_TEXTURE_2D);
            glDisable(GL_TEXTURE_CUBE_MAP_ARB);

            glActiveTextureARB( GL_TEXTURE2_ARB );
            glDisable(GL_TEXTURE_2D);
            glDisable(GL_TEXTURE_CUBE_MAP_ARB);

            glActiveTextureARB( GL_TEXTURE3_ARB );
            glDisable(GL_TEXTURE_2D);
            glDisable(GL_TEXTURE_CUBE_MAP_ARB);

            glDisable(GL_REGISTER_COMBINERS_NV);
            glDisable(GL_VERTEX_PROGRAM_NV);
        }

        glActiveTextureARB( GL_TEXTURE0_ARB );

        glPopClientAttrib();
        glPopAttrib();

        view.endGL();

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwReflectBumpShader_NV20::geometry( const MDrawRequest& request,
                                    M3dView& view,
                                    int prim,
                                    unsigned int writable,
                                    int indexCount,
                                    const unsigned int * indexArray,
                                    int vertexCount,
                                    const int * vertexIDs,
                                    const float * vertexArray,
                                    int normalCount,
                                    const float ** normalArrays,
                                    int colorCount,
                                    const float ** colorArrays,
                                    int texCoordCount,
                                    const float ** texCoordArrays)
    {
        // We assume triangles here.
        //
        if (prim != GL_TRIANGLES)   return MS::kFailure;

        view.beginGL();

        glEnable(GL_VERTEX_ARRAY);

        if (cubeMapOnly)
        {
            glVertexPointer(3, GL_FLOAT, 0, vertexArray);
            glEnable(GL_VERTEX_ARRAY);

            if (normalCount > 0)
            {
                glNormalPointer(GL_FLOAT, 0, normalArrays[0]);
                glEnable(GL_NORMAL_ARRAY);
            }

            glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray);

            glDisable(GL_VERTEX_ARRAY);
            glDisable(GL_NORMAL_ARRAY);
        }
        else
        {
            // Bind and enable the vertex program
            glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id);
            glEnable(GL_VERTEX_PROGRAM_NV);

            // VERTEX REGISTERS:
            // 0 - coord
            // 1 - normal
            // 2 - texcoord0
            // 3 - texcoord1
            // 4 - texcoord2 (binorm)

            glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray );
            glVertexAttribPointerNV( 1, 3, GL_FLOAT, 0, normalArrays[0] );
            glVertexAttribPointerNV( 2, 2, GL_FLOAT, 0, texCoordArrays[0] );
            glVertexAttribPointerNV( 3, 2, GL_FLOAT, 0, texCoordArrays[0] );
            glVertexAttribPointerNV( 4, 3, GL_FLOAT, 0, normalArrays[2] );

            glEnableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV );
            glEnableClientState( GL_VERTEX_ATTRIB_ARRAY1_NV );
            glEnableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV );
            glEnableClientState( GL_VERTEX_ATTRIB_ARRAY3_NV );
            glEnableClientState( GL_VERTEX_ATTRIB_ARRAY4_NV );

            glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray);

            glDisableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV );
            glDisableClientState( GL_VERTEX_ATTRIB_ARRAY1_NV );
            glDisableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV );
            glDisableClientState( GL_VERTEX_ATTRIB_ARRAY3_NV );
            glDisableClientState( GL_VERTEX_ATTRIB_ARRAY4_NV );

            glDisable(GL_VERTEX_PROGRAM_NV);
        }

        glDisable(GL_VERTEX_ARRAY);
        view.endGL();

        return MS::kSuccess;
    }

    /* virtual */
    int hwReflectBumpShader_NV20::normalsPerVertex()
    {
        return 3;
    }

    /* virtual */
    int hwReflectBumpShader_NV20::texCoordsPerVertex()
    {
        return 1;
    }