#ifdef WIN32
#pragma warning( disable : 4786 )               // Disable STL warnings.
#endif
#include <maya/MIOStream.h>
#include <math.h>
#include <maya/MString.h>
#include <maya/MPlug.h>
#include <maya/MDagPath.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MArrayDataHandle.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnLightDataAttribute.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/MEulerRotation.h>
#include <maya/MFnLight.h>
#include <maya/MFnNonAmbientLight.h>
#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 "hwAnisotropicShader_NV20.h"
#include "ShadingConnection.h"
#define TANGENT_INDEX 1 // macro to make normall array indexing a bit clearer
MTypeId hwAnisotropicShader_NV20::id( 0x00105444 );
 const unsigned int hwAnisotropicShader_NV20::lookup_texture_size(256);
void hwAnisotropicShader_NV20::postConstructor( )
{
        setMPSafe(false);
}
MObject  hwAnisotropicShader_NV20::color;
MObject  hwAnisotropicShader_NV20::colorR;
MObject  hwAnisotropicShader_NV20::colorG;
MObject  hwAnisotropicShader_NV20::colorB;
MObject  hwAnisotropicShader_NV20::roughness;
MObject  hwAnisotropicShader_NV20::kDiffuse;
MObject  hwAnisotropicShader_NV20::kSpecular;
void hwAnisotropicShader_NV20::printGlError( const char *call )
{
    GLenum error;
        while( (error = glGetError()) != GL_NO_ERROR ) {
                assert(0);
            cerr << call << ":" << error << " is " << (const char *)gluErrorString( error ) << "\n";
        }
}
char vertexProgramStringPointDecay[] = 
        "!!VP1.0\n"
                
                
                "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];"
                
                "MOV   R1, v[2];"
                "DP3   R1.w, R1, R1;"                   
                "RSQ   R1.w, R1.w;"
                "MUL   R1.xyz, R1, R1.w;"
                
                "ADD   R2, c[11], -v[0];"
                "DP3   R2.w, R2, R2;"                   
                "RSQ   R2.w, R2.w;"
                "MUL   R2.xyz, R2, R2.w;"
                
                
                "ADD   R0, c[12], -v[0];"               
                "DP3   R0.w, R0, R0;"                   
                "RSQ   R0.w, R0.w;"
                "MUL   R0.xyz, R0, R0.w;"
                
                
                
                
                
                
                "DP3    R3.x, R1, R2;"
                "DP3    R3.y, R1, R0;"
                
                
                "MAD o[TEX0].x, R3.x, c[4].x, c[4].x;"
                "MAD o[TEX0].y, R3.y, c[4].x, c[4].x;"
                
                
                "MOV R4, c[9];"
                "MOV R5, c[9];"
                "MUL R5.xyz, R4, R2.w;"
                "MUL o[COL0], v[3], R5;"
        "END";
char vertexProgramStringPoint[] = 
        "!!VP1.0\n"
                
                
                "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];"
                
                "MOV   R1, v[2];"
                "DP3   R1.w, R1, R1;"                   
                "RSQ   R1.w, R1.w;"
                "MUL   R1.xyz, R1, R1.w;"
                
                "ADD   R2, c[11], -v[0];"
                "DP3   R2.w, R2, R2;"                   
                "RSQ   R2.w, R2.w;"
                "MUL   R2.xyz, R2, R2.w;"
                
                
                "ADD   R0, c[12], -v[0];"               
                "DP3   R0.w, R0, R0;"                   
                "RSQ   R0.w, R0.w;"
                "MUL   R0.xyz, R0, R0.w;"
                
                
                
                
                
                
                "DP3    R3.x, R1, R2;"
                "DP3    R3.y, R1, R0;"
                
                
                "MAD o[TEX0].x, R3.x, c[4].x, c[4].x;"
                "MAD o[TEX0].y, R3.y, c[4].x, c[4].x;"
                
                "MUL o[COL0], v[3], c[9];"
        "END";
char vertexProgramString[] = 
        "!!VP1.0\n"
                
                
                "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];"
                
                "MOV   R1, v[2];"
                "DP3   R1.w, R1, R1;"                   
                "RSQ   R1.w, R1.w;"
                "MUL   R1.xyz, R1, R1.w;"
                
                "MOV   R2, c[11];"
                "DP3   R2.w, R2, R2;"                   
                "RSQ   R2.w, R2.w;"
                "MUL   R2.xyz, R2, R2.w;"
                
                
                "ADD   R0, c[12], -v[0];"               
                "DP3   R0.w, R0, R0;"                   
                "RSQ   R0.w, R0.w;"
                "MUL   R0.xyz, R0, R0.w;"
                
                
                
                
                
                
                "DP3    R3.x, R1, R2;"
                "DP3    R3.y, R1, R0;"
                
                
                "MAD o[TEX0].x, R3.x, c[4].x, c[4].x;"
                "MAD o[TEX0].y, R3.y, c[4].x, c[4].x;"
                
                "MUL o[COL0], v[3], c[9];"
        "END";
void initVertexProgram(const char vertexProgramCode[], GLuint* pVertexProgramId)
{
        
        glGenProgramsNV(1, pVertexProgramId);
        GLenum error = glGetError();
        assert(error == GL_NO_ERROR);
        
        unsigned int length = strlen(vertexProgramCode);
        glLoadProgramNV(GL_VERTEX_PROGRAM_NV, *pVertexProgramId, length, 
                (const GLubyte *) vertexProgramCode);
        error = glGetError();
        
        
        if (error != GL_NO_ERROR)
        {
                
                
                
                
                
                if (error == GL_INVALID_OPERATION)
                {
                        int error_position = -2;
                        glGetIntegerv(GL_PROGRAM_ERROR_POSITION_NV, &error_position);
                        
                        assert(0);
                }
        }
}
void hwAnisotropicShader_NV20::loadVertexProgramGL()
{
        GLenum error = glGetError();
        assert(!error);
        
        
        if (!fVertexProgramsLoaded)
        {
                initVertexProgram(vertexProgramString, &fVertexProgramDirectional);
                initVertexProgram(vertexProgramStringPointDecay, &fVertexProgramPointDecay);
                initVertexProgram(vertexProgramStringPoint, &fVertexProgramPointNoDecay);
                fVertexProgramsLoaded = true;
        }
}
void hwAnisotropicShader_NV20::make_lookup_texture()
{
        
        
        
        float   t_roughness = 0.025f;
        float   t_kd = 0.8f;
        float   t_ks = 0.2f;
        
        MPlug   roughPlug(thisMObject(), roughness);
        MPlug   diffPlug(thisMObject(), kDiffuse);
        MPlug   specPlug(thisMObject(), kSpecular);
        
        roughPlug.getValue(t_roughness);
        diffPlug.getValue(t_kd);
        specPlug.getValue(t_ks);
        if ( t_roughness < 0.0f ) t_roughness = 0.0f;
        if ( t_roughness > 1.0f ) t_roughness = 1.0f;
        if ( t_kd < 0.000000000001f ) t_kd = 0.000000000001f; 
        if ( t_kd> 1.0f ) t_kd= 1.0f;
        if ( t_ks < 0.0f ) t_ks= 0.0f;
        if ( t_ks > 1.0f ) t_ks = 1.0f;
        
        boolean dirty = false;
        if      (currentKd != t_kd) {
                dirty = true; currentKd = t_kd;
        }
        if (currentKs != t_ks) {
                dirty = true; currentKs = t_ks; 
        }
        if (currentRoughness != t_roughness) {
                dirty = true; currentRoughness = t_roughness;           
        }
        if (!dirty) 
                return;
        
        unsigned int imgsize = lookup_texture_size;
        float imgsizeM1 = (float) (imgsize - 1);
        
        if (lookup_table == NULL)
                lookup_table = new unsigned char[imgsize*imgsize*3];
        if (lookup_texture == NULL)
                lookup_texture = new tex_object_2D;
        
#ifdef _DEBUG_DUMP_LOOKUP_TEXTURE
        
        
        
        static boolean firstTime = true;
#else
        static boolean firstTime = false;
#endif
        if (firstTime)
        {
                MImage image;
                image.create(lookup_texture_size, lookup_texture_size);
                unsigned char *ip  = image.pixels();
                float           diff, spec, phong;
                float           lt, vt;
                float           h;
                int                     i, j;
                float           invRough = 1.0 / t_roughness;
                for (i=0; i < imgsize; i++)
                {
                        
                        lt = 1.0 - 2.0 * (float)i/(float)(imgsize-1);
                        diff= sqrt( 1.0-lt*lt );
                        for(j=0; j < imgsize; j++)
                        {
                                vt= 2.0 * (float)j/(float)(imgsize-1) - 1.0;
                                spec= diff*sqrt( 1-vt*vt ) - lt*vt;
                                
                                
                                phong= t_ks*powf( spec < 0.0 ? 0.0 : spec, invRough );
                                float Kddiff = t_kd*diff;
      
                                h= (Kddiff+phong)*255.0 + 0.5;
                                *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h);
                                h= (Kddiff+phong)*255.0 + 0.5;
                                *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h);
                                h= (Kddiff+phong)*255.0 + 0.5;
                                *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h);
                                *ip++ = 1.0;
                        }
                }
                image.writeToFile("c:/aniso.iff");
                firstTime = false;
        }
        unsigned char *ip  = lookup_table;
        float           diff, spec, phong;
        float           lt, vt;
        float           h;
        int                     i, j;
        float           invRough = 1.0 / t_roughness;
        for (i=0; i < imgsize; i++)
        {
                
                lt = 1.0 - 2.0 * (float)i/(float)(imgsize-1);
                diff= sqrt( 1.0-lt*lt );
                for(j=0; j < imgsize; j++)
                {
                        vt= 2.0 * (float)j/(float)(imgsize-1) - 1.0;
                        spec= diff*sqrt( 1-vt*vt ) - lt*vt;
                        
                        
                        phong= t_ks*powf( spec < 0.0 ? 0.0 : spec, invRough );
                        float Kddiff = t_kd*diff;
  
                        h= (Kddiff+phong)*255.0 + 0.5;
                        *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h);
                        h= (Kddiff+phong)*255.0 + 0.5;
                        *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h);
                        h= (Kddiff+phong)*255.0 + 0.5;
                        *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h);
                }
        }
        
        fLookupTextureReprocessed = true;
        return;
}
void hwAnisotropicShader_NV20::bind_lookup_table()
{
        
        make_lookup_texture();
        
        lookup_texture->bind();
        if (fLookupTextureReprocessed)
        {
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, lookup_texture_size, lookup_texture_size, 
                        0, GL_RGB, GL_UNSIGNED_BYTE, lookup_table); 
                fLookupTextureReprocessed = false;
        }
        lookup_texture->parameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        lookup_texture->parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        lookup_texture->parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        lookup_texture->parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
void hwAnisotropicShader_NV20::init_ext(const char * ext)
{
        if(!glh_init_extension(ext))
        { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); }
}
hwAnisotropicShader_NV20::hwAnisotropicShader_NV20()
{
        
        m_pTextureCache = MTextureCache::instance();
        init_ext("GL_ARB_multitexture");
        
        init_ext("GL_NV_vertex_program");
        isDirectionalLight = true;      
        isNonAmbientLight = false; 
        
        
        currentColor[0] = currentColor[1] = currentColor[2] = -1.0;
        currentRoughness = -1.0;
        currentKd = -1.0;
        currentKs = -1.0;
        lookup_texture = NULL;
        lookup_table   = NULL;
        fLookupTextureReprocessed = false;
        
        fBeforeNewCB = 0;
        fBeforeOpenCB = 0;
        fBeforeRemoveReferenceCB = 0;
        fMayaExitingCB = 0;
        attachSceneCallbacks();
        
        fVertexProgramsLoaded = false;
        fVertexProgramDirectional = 0;
        fVertexProgramPointDecay = 0;
        fVertexProgramPointNoDecay = 0;
        
        
}
hwAnisotropicShader_NV20::~hwAnisotropicShader_NV20()
{
        detachSceneCallbacks();
}
void releaseVertexProgram(GLuint* pVertexProgramId)
{
        
        if (*pVertexProgramId > 0)
        {
                
                glBindProgramNV(GL_VERTEX_PROGRAM_NV, 0);
                glDeleteProgramsNV(1, pVertexProgramId);
                
                *pVertexProgramId = 0;
        }
}
void hwAnisotropicShader_NV20::releaseEverything()
{
        release_lookup_texture();
        
        if (fVertexProgramsLoaded)
        {
                releaseVertexProgram(&fVertexProgramDirectional);
                releaseVertexProgram(&fVertexProgramPointDecay);
                releaseVertexProgram(&fVertexProgramPointNoDecay);
                fVertexProgramsLoaded = false;
        }
        
        m_pTextureCache->release();
        if(!MTextureCache::getReferenceCount())
        {
                m_pTextureCache = 0;
        }
}
void hwAnisotropicShader_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);
}
void hwAnisotropicShader_NV20::releaseCallback(void* clientData)
{
        hwAnisotropicShader_NV20 *pThis = (hwAnisotropicShader_NV20*) clientData;
        pThis->releaseEverything();
}
void hwAnisotropicShader_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.5", "Any");
        status = plugin.registerNode( "hwAnisotropicShader_NV20", hwAnisotropicShader_NV20::id, 
                                              hwAnisotropicShader_NV20::creator, hwAnisotropicShader_NV20::initialize,
                                                                  MPxNode::kHwShaderNode, &UserClassify );
        if (!status) {
                status.perror("registerNode");
                return status;
        }
        return MS::kSuccess;
}
MStatus uninitializePlugin( MObject obj )
{
        MStatus   status;
        
        MFnPlugin plugin( obj );
        plugin.deregisterNode( hwAnisotropicShader_NV20::id );
        if (!status) {
                status.perror("deregisterNode");
                return status;
        }
        return MS::kSuccess;
}
void * hwAnisotropicShader_NV20::creator()
{
    return new hwAnisotropicShader_NV20();
}
MStatus hwAnisotropicShader_NV20::initialize()
{
    MFnNumericAttribute nAttr; 
        MStatus status;
        MFnTypedAttribute sAttr; 
        
    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);
        
        
    kDiffuse = nAttr.create( "kDiffuse", "kd", MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setMin(0.0f);
    nAttr.setMax(1.0f);
    nAttr.setDefault(0.2f);
    kSpecular = nAttr.create( "kSpecular", "ks", MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setMin(0.0f);
    nAttr.setMax(1.0f);
    nAttr.setDefault(0.9f);
    roughness = nAttr.create( "roughness", "rn", MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setMin(0.0f);
    nAttr.setMax(1.0f);
    nAttr.setDefault(0.025f);
        
        
    addAttribute(color);
    addAttribute(roughness);
        addAttribute(kDiffuse);
    addAttribute(kSpecular);
    attributeAffects (colorR, outColor);
    attributeAffects (colorG, outColor);
    attributeAffects (colorB, outColor);
    attributeAffects (color,  outColor);
    attributeAffects (roughness, outColor);
    attributeAffects (kDiffuse, outColor);
    attributeAffects (kSpecular, outColor);
    return MS::kSuccess;
}
MStatus hwAnisotropicShader_NV20::getFloat3(MObject attr, float value[3])
{
        MStatus status = MS::kSuccess;
        
        
        MPlug   plug(thisMObject(), attr);
        MObject object;
        status = plug.getValue(object);
        if (!status)
        {
                status.perror("hwAnisotropicShader_NV20::getFloat3 plug.getValue.");
                return status;
        }
        MFnNumericData data(object, &status);
        if (!status)
        {
                status.perror("hwAnisotropicShader_NV20::getFloat3 construct data.");
                return status;
        }
        status = data.getData(value[0], value[1], value[2]);
        if (!status)
        {
                status.perror("hwAnisotropicShader_NV20::getFloat3 get values.");
                return status;
        }
        return status;
}
MStatus hwAnisotropicShader_NV20::getString(MObject attr, MString &str)
{
        MPlug   plug(thisMObject(), attr);
        MStatus status = plug.getValue( str );
        return status;
}
MStatus hwAnisotropicShader_NV20::bind(const MDrawRequest& request, M3dView& view)
{
        MStatus status;
        
        float t_color[3];
        getFloat3(color, t_color);
        view.beginGL();
        {
                glPushAttrib( GL_ALL_ATTRIB_BITS );
                glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
                
                glColor4f(t_color[0], t_color[1], t_color[2], 1.0f);
                
                glActiveTextureARB( GL_TEXTURE0_ARB );
                glEnable(GL_TEXTURE_2D);
                bind_lookup_table();    
                
        }
        view.endGL();
        return MS::kSuccess;
}
MStatus hwAnisotropicShader_NV20::unbind(const MDrawRequest& request,
                           M3dView& view)
{
        view.beginGL();
        glActiveTextureARB( GL_TEXTURE0_ARB );
        glDisable(GL_TEXTURE_2D);
        glPopClientAttrib();
        glPopAttrib();
        view.endGL();
        return MS::kSuccess;
}
MStatus hwAnisotropicShader_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)
{
        
        
        if (prim != GL_TRIANGLES)
                return MS::kSuccess;            
        view.beginGL();
        
        
        
        
        
        isDirectionalLight = true; 
        isNonAmbientLight = false;
        boolean useDefaultLight = false;
        unsigned int numLights;
        MDagPath lightPath;
        view.getLightCount( numLights );
        if (numLights)
        {
                M3dView::LightingMode mode;
                view.getLightingMode(mode);
                if (mode == M3dView::kLightDefault)
                {
                        useDefaultLight = true;
                        isDirectionalLight = true;
                }
                else
                {
                        view.getLightPath( 0, lightPath );
                        MObject lightObj = lightPath.node();
                        isDirectionalLight = lightObj.hasFn( MFn::kDirectionalLight );
                        isNonAmbientLight = lightObj.hasFn( MFn::kNonAmbientLight );
                        if (isNonAmbientLight)
                        {
                                MFnNonAmbientLight mNonAmbientLight(lightObj);
                                if (mNonAmbientLight.decayRate() == 0)
                                        isNonAmbientLight = false;
                        }
                }
        }
        
        loadVertexProgramGL();
        
        
        
        if (isDirectionalLight)
                glBindProgramNV(GL_VERTEX_PROGRAM_NV, fVertexProgramDirectional);
        else if (isNonAmbientLight)
                glBindProgramNV(GL_VERTEX_PROGRAM_NV, fVertexProgramPointDecay);
        else
                glBindProgramNV(GL_VERTEX_PROGRAM_NV, fVertexProgramPointNoDecay);
        
        GLenum error = glGetError();
        assert(error == GL_NO_ERROR);
        
        glEnable(GL_VERTEX_PROGRAM_NV);
        
        MDagPath objPath = request.multiPath();
        MMatrix objMatrix = objPath.inclusiveMatrixInverse();
        
        
        
        
        if (numLights)
        {
                
                if (useDefaultLight )
                {
                        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, 0, 0, 1, 1); 
                        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, 1.0, 1.0, 1.0, 1); 
                }
                
                else
                {
                        view.getLightPath( 0, lightPath );
                        MMatrix matrix = lightPath.inclusiveMatrix();
                        isDirectionalLight = lightPath.node().hasFn( MFn::kDirectionalLight );
                        
                        if (isDirectionalLight)
                        {
                                MVector lightDir(0,0,1); 
                                lightDir *= matrix;
                                
                                lightDir *= objMatrix;
                                
                                
                                glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, -lightDir.x, -lightDir.y, -lightDir.z, 1);
                        }
                        
                        else
                        {
                                MPoint lightPos(0,0,0); 
                                lightPos *= matrix;
                                
                                lightPos *= objMatrix;
                                
                                
                                glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, -lightPos.x, -lightPos.y, -lightPos.z, 1);
                        }
                        MFnLight mLight(lightPath.node());
                        
                        MColor lightColor = mLight.color();
                        float intensity = mLight.intensity();
                        lightColor.r *= intensity;
                        lightColor.g *= intensity;
                        lightColor.b *= intensity;
                        
                        
                        
                        
                        
                        
                        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, lightColor.r, lightColor.g, lightColor.b, 1);
                }
        }
        else
        {
                
                glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, 0, 0, 1, 1);
                glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, 1.0, 1.0, 1.0, 1);
        }
        
        float cameraPos[4] = {0.0f, 0.0f, 0.0f, 0.0f};
        {
                MDagPath camDag;
                view.getCamera(camDag);
                MPoint cameraInObject(0,0,0); 
                
                MMatrix cameraToWorldMatrix = camDag.inclusiveMatrix();
                cameraInObject *= cameraToWorldMatrix; 
                cameraInObject *= objMatrix;
                glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 12, cameraInObject.x, cameraInObject.y, cameraInObject.z, 1);
        }
        
        
        
        
        glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV);
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 4, 0.5, 1.0, 0.0, 0.0);
        
        
        
        glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray );
        glVertexAttribPointerNV( 2, 3, GL_FLOAT, 0, normalArrays[TANGENT_INDEX] ); 
        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);
        view.endGL();
        return MS::kSuccess;
}
int             hwAnisotropicShader_NV20::normalsPerVertex()
{
        
        return 2;
}
int             hwAnisotropicShader_NV20::texCoordsPerVertex()
{
        return 1;
}
void hwAnisotropicShader_NV20::release_lookup_texture()
{
        if (lookup_table)
        {
                delete lookup_table;
                lookup_table = NULL;
        }
        
        if (lookup_texture)
        {
                delete lookup_texture;
                lookup_table = NULL;
        }
}