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

# hwRefractReflectShader_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 produces reflection and refraction effects.
    //
    //  This shader builds on the foundation demonstrated in the hwUnlitShader.
    //
    //

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

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

    #include <maya/MString.h>
    #include <maya/MPlug.h>
    #include <maya/MDagPath.h>
    #include <maya/MFnDependencyNode.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/MSceneMessage.h>

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

    #include <GL/gl.h>
    #include <GL/glext.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.
    //
    #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 "hwRefractReflectShader_NV20.h"
    #include "ShadingConnection.h"

    MTypeId hwRefractReflectShader_NV20::id( 0x00105445 );

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

    // Static attribute instances.
    //
    MObject  hwRefractReflectShader_NV20::color;
    MObject  hwRefractReflectShader_NV20::colorR;
    MObject  hwRefractReflectShader_NV20::colorG;
    MObject  hwRefractReflectShader_NV20::colorB;

    MObject  hwRefractReflectShader_NV20::refractionIndex;
    MObject  hwRefractReflectShader_NV20::reflectivity;

    // The Vertex Program for the reflection&refraction shading effect.
    //
    // CONSTANTS:
    //  0- 3    4x4 ModelView-Projection composite matrix
    //  4- 7    4x4 ModelView   inverseTranspose matrix
    //  8- 11   4x4 ModelView   matrix
    //  12-15   4x4 Texture     matrix
    //
    // 58   refraction index [rIdx, rIdx*rIdx, 0.0, 0.0]
    // 59   camera position in eye space [0.0, 0.0, 0.0, 1.0]
    //      (camera could be offseted if necessary, should work but untested)
    //
    // 64   misc constants [0.0, 1.0, 2.0, 3.0]

    // VERTEX REGISTERS (mapped so that standard gl calls work):
    // 0 - coord
    // 2 - normal
    //
    // RESULTS:
    // texcoord0    (Refraction coords in eye-space)
    // texcoord1    (Reflection coords in eye-space)
    //
    char vertexProgramString[] =

            "!!VP1.0 # Refraction and Reflection\n"

            // Multiply the vertex coords by the GL_MODELVIEW_PROJECTION
            // composite matrix, to get clip space coordinates.
            //
            "DP4    o[HPOS].x, c[0], v[OPOS];"
            "DP4    o[HPOS].y, c[1], v[OPOS];"
            "DP4    o[HPOS].z, c[2], v[OPOS];"
            "DP4    o[HPOS].w, c[3], v[OPOS];"

            // =====================================================
            // The rest of the computations are done in the eyeSpace
            // =====================================================

            // Transform, vertex position to eye space, with the MODELVIEW matrix
            //
            "DP4    R9.x, c[8],  v[OPOS];"
            "DP4    R9.y, c[9],  v[OPOS];"
            "DP4    R9.z, c[10], v[OPOS];"
            "DP4    R9.w, c[11], v[OPOS];"      // R9 = eye space Position of this vertex

            // Using the inverseTranspose of the MODELVIEW matrix,
            // transform the vertex normal to eye space and normalize it
            //
            "DP3    R0.x, c[4], v[NRML];"
            "DP3    R0.y, c[5], v[NRML];"
            "DP3    R0.z, c[6], v[NRML];"
            "DP3    R11.w, R0, R0;"
            "RSQ    R11.w, R11.w;"
            "MUL    R11, R0, R11.w;"            // R11 = normalized normal vector in the eyeSpace

            // Compute the 'vertex->eye' vector and normalize it
            //
            "ADD    R0, -R9, c[59];"            // c[59] = eye position in eye space (0,0,0,1)
            "DP3    R8.w, R0, R0;"
            "RSQ    R8.w, R8.w;"
            "MUL    R8, R0, R8.w;"              // R8 = the eye/incident vector (I)

            // Calculate REFRACTION: Renderman style
            // float    eta;    // the refraction index value
            //
            // float    IdotN = I.N;
            // float    k = 1 - eta*eta*(1 - IdotN*IdotN);
            // return   k < 0 ? (0,0,0) : eta*I - (eta*IdotN + sqrt(k))*N;
            //
            "DP3    R0.x, R11, -R8;"                // R0 = N.I
            //
            "MAD    R1.x, -R0.x, R0.x, c[64].y;"    // R1.x = (1 - IdotN * IdotN)       == SQR( sin(Ti) )
            "MUL    R1.x, R1.x, c[58].y;"           // R1.x = R1.x * eta*eta
            "ADD    R1.x, c[64].y, -R1.x;"          // R1.x = (1 - (R1.x * eta * eta) ) == 1 - SQR( eta * sin(Ti) )
            //
            "RSQ    R2.x, R1.x;"                    // R2.x = 1 / SQRT(R1.x)
            "RCP    R2.x, R2.x;"                    // R2.x = cos(Tr) = SQRT(R1.x)  <=== OK
            "MAD    R2.x, c[58].x, R0.x, R2.x;"     // R2.x = eta*(IdotN) + cos(Tr)
            "MUL    R2, R11, R2.x;"                 // R2 = N * R2.x
            "MAD    R2, c[58].x, -R8, R2;"          // R2 is the refracted ray direction
            //
            // Transform refracted ray by cubemap transform (texture matrix)
            //
            "DP3    o[TEX0].x, c[12], R2;"
            "DP3    o[TEX0].y, c[13], R2;"
            "DP3    o[TEX0].z, c[14], R2;"

            // Calculate REFLECTION in cubeMap space
            //
            "MUL    R0, R11, c[64].z;"      // R0   = 2*N
            "DP3    R3.w, R11, R8;"         // R3.w = N.dot.I
            "MAD    R3, R3.w, R0, -R8;"     // R3   = 2*N*(N.dot.I) - I
            //
            // Transform reflected ray by cubemap transform (texture matrix)
            //
            "DP3    o[TEX1].x, c[12], R3;"
            "DP3    o[TEX1].y, c[13], R3;"
            "DP3    o[TEX1].z, c[14], R3;"

            "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 hwRefractReflectShader_NV20::loadVertexProgramGL( M3dView& view )
    {
        view.beginGL();
        {
            // Don't load/initialize the vertex program more than once.
            //
            if (vertex_program_id == 0)
                initVertexProgram(vertexProgramString, &vertex_program_id);

            // Set up the constant values.
            //
            // CONSTANTS:
            //  0- 3    4x4 ModelView-Projection composite matrix
            //  4- 7    4x4 ModelView   inverseTranspose matrix
            //  8- 11   4x4 ModelView   matrix
            //  12-15   4x4 Texture     matrix
            //
            glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0,  GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV);
            glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4,  GL_MODELVIEW,               GL_INVERSE_TRANSPOSE_NV);
            glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 8,  GL_MODELVIEW,               GL_IDENTITY_NV);
            glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 12, GL_TEXTURE,                 GL_IDENTITY_NV);

            float rIdx = fRefractionIndex;
            glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 58, rIdx, rIdx*rIdx, 0.0, 0.0);    // refraction index
            glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 59, 0.0, 0.0, 0.0, 1.0);           // eye position
            glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 64, 0.0, 1.0, 2.0, 3.0);           // misc constants
        }
        view.endGL();
    }

    // Load the file textures for the cube maps.
    //
    MStatus hwRefractReflectShader_NV20::loadTextures(const MDrawRequest& request, M3dView& view)
    {
        // Get the cube map file names
        //
        MStringArray    decalNames;
        MString         decalName;

        // Find the cubemap textures by tracing through the connection from the color atttribute
        //
        ShadingConnection   colorConnection(thisMObject(), request.multiPath().partialPathName(), "color");

        // 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 = "internalDefaultTexture";

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

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

        // Reload cube maps if the name of the textures
        // for any of the cube maps changes
        //
        bool reload = FALSE;
        for (int i=0; i<6; i++)
        {
            if (currentTextureNames[i] != decalNames[i])
            {
                reload = TRUE;
                break;
            }
        }

        view.beginGL();
        {
            if ( reload )
            {
                MString ypTexName(decalNames[0]);   // y+ == top
                MString ynTexName(decalNames[1]);   // y- == bottom
                MString xpTexName(decalNames[2]);   // x+ == left
                MString xnTexName(decalNames[3]);   // x- == right
                MString zpTexName(decalNames[4]);   // z+ == front
                MString znTexName(decalNames[5]);   // z- == back

                MStatus stat;
                if (! (stat = theImage_XP.readFromFile(xpTexName)) )    return MS::kFailure;
                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 (fTextureName == -1)     glGenTextures(1, &fTextureName);

                glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName );
                glEnable( GL_TEXTURE_CUBE_MAP_ARB );

                // The cubeMap textures have to have the same size
                //
                unsigned int width, height;
                stat = theImage_XP.getSize( width, height );

                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
                    0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XP.pixels() );
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
                    0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XN.pixels() );
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
                    0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YP.pixels() );
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
                    0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YN.pixels() );
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
                    0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZP.pixels() );
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
                    0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZN.pixels() );

                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_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

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

            // stage 0 -- cubeMap texture for the refraction
            //
            glActiveTextureARB( GL_TEXTURE0_ARB );
            glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName );
            glEnable( GL_TEXTURE_CUBE_MAP_ARB );

            // stage 1 -- cubeMap texture for the reflection
            //
            glActiveTextureARB( GL_TEXTURE1_ARB );
            glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName );
            glEnable( GL_TEXTURE_CUBE_MAP_ARB );
        }
        view.endGL();

        return MS::kSuccess;
    }

    // Initialize the register combiners setting
    //
    void    hwRefractReflectShader_NV20::initCombiners(const MDrawRequest& request, M3dView& view)
    {
        view.beginGL();
        {
            // Use only the 1st stage of the register combiner stages
            //
            glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);

            {
                float refractivity[4], reflectivity[4];
                refractivity[0] = refractivity[1] = refractivity[2] = refractivity[3] = 1.0f - fReflectivity;
                reflectivity[0] = reflectivity[1] = reflectivity[2] = reflectivity[3] = fReflectivity;

                glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, refractivity);
                glCombinerParameterfvNV(GL_CONSTANT_COLOR1_NV, reflectivity);
            }

            // combiner 0
            // a*b+c*d
            // a is from the refractive color
            // c is from the refrlective texture
            glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE0_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_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
            glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

            // output:
            // (stage, portion, abOutput, cdOutput, sumOutput, scale, bias, abDotProduct, cdDotProduct, muxSum)
            glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

            // final combiner
            // output: Frgb = A*B + (1-A)*C + D
            // (variable, input, mapping, componentUsage);
            // Just pass through the D variable
            //
            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);
        }
        view.endGL();
    }

    // Load the textures, update the necessary variable values, initialize register combiners,
    // save and load the matrices with the proper values
    //
    MStatus hwRefractReflectShader_NV20::preDraw(const MDrawRequest& request, M3dView& view)
    {
        MStatus stat = loadTextures( request, view);

        if( MS::kSuccess != stat )      return stat;

        // get the reflectivity value
        //
        MPlug   tPlug(thisMObject(), reflectivity);
        if( tPlug.getValue( fReflectivity ) )
        {
            if( fReflectivity < 0.01f ) fReflectivity = 0.01f;
            if( fReflectivity > 1.0f )  fReflectivity = 1.0f;
        }
        else    fReflectivity = 0.5f;

        // get the refraction index value
        //
        MPlug   rPlug(thisMObject(), refractionIndex);
        if( rPlug.getValue( fRefractionIndex ) )
        {
            if ( fRefractionIndex < 1.0f )  fRefractionIndex = 1.0f;
            if ( fRefractionIndex > 2.0f )  fRefractionIndex = 2.0f;
        }
        else    fRefractionIndex = 1.0f;

        initCombiners( request, view );

        // Compute the camera rotation angle and axis
        //
        MDagPath    cameraPath;
        MStatus     status = view.getCamera( cameraPath );
        MMatrix     mmatrix = cameraPath.inclusiveMatrix( &status );
        MTransformationMatrix tmatrix( mmatrix );

        MQuaternion camRotation = tmatrix.rotation();
        MVector     camAxis;
        double      camTheta;
        camRotation.getAxisAngle(  camAxis, camTheta );

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

        view.beginGL();
            glMatrixMode( GL_TEXTURE );
            glPushMatrix();
            glLoadIdentity();
            glScalef(1.0, -1.0, 1.0);
            glRotated( camTheta, camAxis[0], camAxis[1], camAxis[2]);
            glMatrixMode( GL_MODELVIEW );
        view.endGL();

        return stat;
    }

    /* virtual */
    MStatus hwRefractReflectShader_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::kSuccess;

        // Save the current states of the openGL attributes
        //
        view.beginGL();
            glPushAttrib( GL_ALL_ATTRIB_BITS );
            glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
        view.endGL();

        MStatus preDrawStatus = preDraw( request, view );

        if( MS::kSuccess == preDrawStatus )
        {
            loadVertexProgramGL( view );

            view.beginGL();
            {
                glEnable(GL_REGISTER_COMBINERS_NV);
                //
                // Load, bind and enable the vertex program
                //
                glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id);
                glEnable(GL_VERTEX_PROGRAM_NV);
                {
                    // VERTEX REGISTERS (Attributes):
                    // 0 - coord
                    // 2 - normal
                    //
                    glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray );
                    glVertexAttribPointerNV( 2, 3, GL_FLOAT, 0, normalArrays[0] );

                    glEnableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV );
                    glEnableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV );

                    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray);

                    glDisableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV );
                    glDisableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV );
                }
                glDisable(GL_VERTEX_PROGRAM_NV);
                //
                glDisable(GL_REGISTER_COMBINERS_NV);
            }
            view.endGL();

            postDraw( request, view );
        }

        // Restore the openGL attributes
        //
        view.beginGL();
            glPopClientAttrib();
            glPopAttrib();
        view.endGL();

        return preDrawStatus;
    }

    // Retore the openGL matrices and the openGL texture objects states
    //
    MStatus hwRefractReflectShader_NV20::postDraw(
        const MDrawRequest& request,
        M3dView& view )
    {
        view.beginGL();
        {
            glMatrixMode( GL_TEXTURE );
            glPopMatrix();
            glMatrixMode( GL_MODELVIEW );

            glActiveTextureARB( GL_TEXTURE1_ARB );
            glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 );
            glDisable(GL_TEXTURE_CUBE_MAP_ARB);

            glActiveTextureARB( GL_TEXTURE0_ARB );
            glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 );
            glDisable(GL_TEXTURE_CUBE_MAP_ARB);
        }
        view.endGL();

        return MS::kSuccess;
    }

    /* virtual */
    int     hwRefractReflectShader_NV20::normalsPerVertex()
    {
        return 1;
    }

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

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

    // The constructor
    //
    hwRefractReflectShader_NV20::hwRefractReflectShader_NV20()
    {
        // Get an reference to the singleton texture cache.
        m_pTextureCache = MTextureCache::instance();

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

        // Initialize the cubeMap texture names
        //
        fTextureName = -1;
        currentTextureNames[0] = "";
        currentTextureNames[1] = "";
        currentTextureNames[2] = "";
        currentTextureNames[3] = "";
        currentTextureNames[4] = "";
        currentTextureNames[5] = "";

        // Initialize callbacks.
        fBeforeNewCB = 0;
        fBeforeOpenCB = 0;
        fBeforeRemoveReferenceCB = 0;
        fMayaExitingCB = 0;
        attachSceneCallbacks();

        vertex_program_id = 0;      // handle for the Vertex Program
    }

    hwRefractReflectShader_NV20::~hwRefractReflectShader_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 hwRefractReflectShader_NV20::releaseEverything()
    {
        if (fTextureName != -1) glDeleteTextures(1, &fTextureName);

        releaseVertexProgram(&vertex_program_id);

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

    void hwRefractReflectShader_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 hwRefractReflectShader_NV20::releaseCallback(void* clientData)
    {
        hwRefractReflectShader_NV20 *pThis = (hwRefractReflectShader_NV20*) clientData;
        pThis->releaseEverything();
    }

    void hwRefractReflectShader_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( "hwRefractReflectShader_NV20", hwRefractReflectShader_NV20::id,
                                      hwRefractReflectShader_NV20::creator, hwRefractReflectShader_NV20::initialize,
                                      MPxNode::kHwShaderNode, &UserClassify );
        if (!status) {
            status.perror("registerNode");
            return status;
        }

        return MS::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj )
    {
        MStatus     status;
        MFnPlugin   plugin( obj );

        status = plugin.deregisterNode( hwRefractReflectShader_NV20::id );
        if (!status) {
            status.perror("deregisterNode");
            return status;
        }

        return MS::kSuccess;
    }

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

    // Initialize the plug-in. Called once when the plug-in is loaded.
    // This mostly involve creating attributes.
    MStatus hwRefractReflectShader_NV20::initialize()
    {
        MFnNumericAttribute nAttr;
        MStatus status;
        MFnTypedAttribute sAttr; // For string attributes

        // Create input attributes

        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);

        refractionIndex = nAttr.create( "refractionIndex", "ri", MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setMin(1.0f);
        nAttr.setMax(2.0f);
        nAttr.setDefault(1.1f);

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

     // Add the attributes here

        addAttribute(color);
        addAttribute(refractionIndex);
        addAttribute(reflectivity);

        attributeAffects (colorR, outColor);
        attributeAffects (colorG, outColor);
        attributeAffects (colorB, outColor);
        attributeAffects (color,  outColor);

        attributeAffects (refractionIndex,   outColor);
        attributeAffects (reflectivity,   outColor);

        return MS::kSuccess;
    }

    // This function gets called by Maya to evaluate the texture.
    // See "Writing a shading node plug-in" in the documentation
    // for more information.
    //
    // CAVEAT: This part of the HW shader plug-in is meant to allow
    // seamless transition from HW to SW rendering.
    // Unfortunately, as of 4.0.1 it's somewhat flaky.
    // Meanwhile, it is recommended to build two shading networks
    // in parallel (one for SW, one for HW) and use MEL scripts
    // to switch from one to the other.
    //
    MStatus hwRefractReflectShader_NV20::compute(
    const MPlug&      plug,
          MDataBlock& block )
    {
        // Get color and lightModel from the input block.
        // Get UV coordinates from the input block.

        bool k = false;
        k |= (plug==outColor);
        k |= (plug==outColorR);
        k |= (plug==outColorG);
        k |= (plug==outColorB);
        if( !k ) return MS::kUnknownParameter;

        MFloatVector resultColor(0.0,0.0,0.0);

        // set ouput color attribute
        MDataHandle outColorHandle = block.outputValue( outColor );
        MFloatVector& outColor = outColorHandle.asFloatVector();
        outColor = resultColor;
        outColorHandle.setClean();

        return MS::kSuccess;
    }