#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 <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 "hwDecalBumpShader_NV20.h"
#include "ShadingConnection.h"
MTypeId hwDecalBumpShader_NV20::id( 0x00105441 );
 const unsigned int hwDecalBumpShader_NV20::lookup_texture_size(256);
void hwDecalBumpShader_NV20::postConstructor( )
{
        setMPSafe(false);
}
MObject  hwDecalBumpShader_NV20::color;
MObject  hwDecalBumpShader_NV20::colorR;
MObject  hwDecalBumpShader_NV20::colorG;
MObject  hwDecalBumpShader_NV20::colorB;
MObject  hwDecalBumpShader_NV20::bump;
MObject  hwDecalBumpShader_NV20::bumpR;
MObject  hwDecalBumpShader_NV20::bumpG;
MObject  hwDecalBumpShader_NV20::bumpB;
MObject  hwDecalBumpShader_NV20::camera;
MObject  hwDecalBumpShader_NV20::cameraX;
MObject  hwDecalBumpShader_NV20::cameraY;
MObject  hwDecalBumpShader_NV20::cameraZ;
MObject  hwDecalBumpShader_NV20::light;
MObject  hwDecalBumpShader_NV20::lightX;
MObject  hwDecalBumpShader_NV20::lightY;
MObject  hwDecalBumpShader_NV20::lightZ;
MObject  hwDecalBumpShader_NV20::uCoord;
MObject  hwDecalBumpShader_NV20::vCoord;
MObject  hwDecalBumpShader_NV20::uvCoord;
MObject  hwDecalBumpShader_NV20::uBias;
MObject  hwDecalBumpShader_NV20::vBias;
MObject  hwDecalBumpShader_NV20::uvFilterSize;
MObject  hwDecalBumpShader_NV20::uvFilterSizeX;
MObject  hwDecalBumpShader_NV20::uvFilterSizeY;
MObject  hwDecalBumpShader_NV20::shininess;
MObject  hwDecalBumpShader_NV20::lightColor;
MObject  hwDecalBumpShader_NV20::lightColorR;
MObject  hwDecalBumpShader_NV20::lightColorG;
MObject  hwDecalBumpShader_NV20::lightColorB;
void hwDecalBumpShader_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 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];"
                
                
                "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];"
                
                
                "DP3   R7.x, c[4], v[1];"
                "DP3   R7.y, c[5], v[1];"
                "DP3   R7.z, c[6], v[1];"
                
                "DP3   R7.w, R7, R7;"                   
                "RSQ   R7.w, R7.w;"
                "MUL   R7.xyz, R7, R7.w;"
                
                
                "DP3   R6.x, c[4], v[4];"
                "DP3   R6.y, c[5], v[4];"
                "DP3   R6.z, c[6], v[4];"
                
                "DP3   R6.w, R6, R6;"
                "RSQ   R6.w, R6.w;"
                "MUL   R6.xyz, R6, R6.w;"
                
                "MUL   R5, R6.zxyw, R7.yzxw;"
                "MAD   R5, R6.yzxw, R7.zxyw, -R5;"
                
                "MUL   R5.xyz, R5, v[4].w;"
                
                "DP3   R5.w, R5, R5;"
                "RSQ   R5.w, R5.w;"
                "MUL   R5.xyz, R5, R5.w;"
                
                
                
                
                "DP3   R8.w, R4, R4;"
                "RSQ   R8.w, R8.w;"
                "MUL   R8.xyz, R4, -R8.w;"
                
                "ADD   R9, R8, c[11];"
                "DP3   R9.w, R9, R9;"
                "RSQ   R9.w, R9.w;"
                "MUL   R9.xyz, R9, R9.w;"
                
                "DP3   o[TEX3].x, R5, R9;"
                "DP3   o[TEX3].y, R6, R9;"
                "DP3   o[TEX3].z, R7, R9;"
                
                "DP3   o[TEX2].x, R5, c[11];"
                "DP3   o[TEX2].y, R6, c[11];"
                "DP3   o[TEX2].z, R7, c[11];"
#ifndef DEBUGGING_VERTEX_PROGRAM
                
                "DP3 o[COL0], R7, c[11];"
#else
                
                "MOV o[COL0], R7;"
#endif          
                
                "MOV o[TEX0], v[2];"
                "MOV o[TEX1], v[3];"
        "END";
void hwDecalBumpShader_NV20::loadVertexProgramGL()
{
        GLenum error = glGetError();
        assert(!error);
        
        
        
        if (vertex_program_id == 0)
    {
            glGenProgramsNV(1, &vertex_program_id);
        error = glGetError();
            assert(!error);
            
            unsigned int length = strlen(vertexProgramString);
            glLoadProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id, length, (const GLubyte *) vertexProgramString);
            error = glGetError();
            assert(!error);
            if (error)
            {
                    
                    
                    
                    
                    
                    if (error == GL_INVALID_OPERATION)
                    {
                            int error_position = -2;
                            glGetIntegerv(GL_PROGRAM_ERROR_POSITION_NV, &error_position);
                    }
            }
    }
    
        
        
        
        
        
        
        
        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, 8, 1, 1, 1, 1);
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, 1, 1, 1, 1);
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 10, 1, 1, 1, 1);
        glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, -lightRotation[0], -lightRotation[1], -lightRotation[2], 0);   
}
void hwDecalBumpShader_NV20::make_lookup_texture()
{
        
        
        float   shininessScale = 1.0f;
        MPlug   plug(thisMObject(), shininess);
        
        
        
        MStatus status = plug.getValue(shininessScale);
        if (!status)
        {
                status.perror("hwDecalBumpShader_NV20::bind plug.getValue.");
                return;
        }
        if ( shininessScale < 0.01f )
        shininessScale = 0.01f;
        if ( shininessScale > 1.0f )
        shininessScale = 1.0f;
        
        
        float shininessValue = 1.0f;
        shininessValue = 200.0f * (1.0f - 0.8f * shininessScale);
        
        if (shininessValue == currentShininessValue && shininessScale == currentShininessScale)
                return;
        
        currentShininessValue = shininessValue;
        currentShininessScale = shininessScale;
        unsigned int imgsize = lookup_texture_size;
        float imgsizeM1 = (float) (imgsize - 1);
        
        if (lookup_table == NULL)
        lookup_table = new unsigned char[imgsize*imgsize*2];
        if (lookup_texture == NULL)
        lookup_texture = new tex_object_2D;
        
        unsigned char * ip = lookup_table;
        for(int j=0; j < imgsize; j++)
        {
                unsigned char a = (unsigned char) (shininessScale * (255.0 * pow((j/imgsizeM1), shininessValue)));
                for(int i=0; i < imgsize; i++)
                {
                        *ip++ = (unsigned char) ((255.0 - 64) * (i/imgsizeM1)) + 64;
                        *ip++ = a;
                }
        }
        fLookupTextureReprocessed = true;
        return;
}
void hwDecalBumpShader_NV20::bind_lookup_table()
{
        make_lookup_texture();
        lookup_texture->bind();
        if (fLookupTextureReprocessed)
        {
                glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, lookup_texture_size, lookup_texture_size, 0, GL_LUMINANCE_ALPHA, 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 hwDecalBumpShader_NV20::init_ext(const char * ext)
{
        if(!glh_init_extension(ext))
        { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); }
}
hwDecalBumpShader_NV20::hwDecalBumpShader_NV20()
{
        
        m_pTextureCache = MTextureCache::instance();
        init_ext("GL_ARB_multitexture");
        init_ext("GL_NV_register_combiners");
        init_ext("GL_NV_vertex_program");
        isDirectionalLight = true;      
        
        
        currentShininessValue = -1.0;
        currentShininessScale = -1.0;
        lookup_texture = NULL;
        lookup_table   = NULL;
        fLookupTextureReprocessed = false;
        
        fBeforeNewCB = 0;
        fBeforeOpenCB = 0;
        fBeforeRemoveReferenceCB = 0;
        fMayaExitingCB = 0;
        attachSceneCallbacks();
    vertex_program_id = 0;              
    
        
        
        
        
}
hwDecalBumpShader_NV20::~hwDecalBumpShader_NV20()
{
        detachSceneCallbacks();
}
void hwDecalBumpShader_NV20::releaseEverything()
{
        release_lookup_texture();
        if (vertex_program_id > 0)
        {
                glFinish();
                
                glBindProgramNV(GL_VERTEX_PROGRAM_NV, 0);
                
                
                glDeleteProgramsNV(1, &vertex_program_id);
                
        vertex_program_id = 0;
        }
        
        m_pTextureCache->release();
        if(!MTextureCache::getReferenceCount())
        {
                m_pTextureCache = 0;
        }
}
void hwDecalBumpShader_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 hwDecalBumpShader_NV20::releaseCallback(void* clientData)
{
        hwDecalBumpShader_NV20 *pThis = (hwDecalBumpShader_NV20*) clientData;
        pThis->releaseEverything();
}
void hwDecalBumpShader_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( "hwDecalBumpShader_NV20", hwDecalBumpShader_NV20::id, 
                                              hwDecalBumpShader_NV20::creator, hwDecalBumpShader_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( hwDecalBumpShader_NV20::id );
        if (!status) {
                status.perror("deregisterNode");
                return status;
        }
        return MS::kSuccess;
}
void * hwDecalBumpShader_NV20::creator()
{
    return new hwDecalBumpShader_NV20();
}
MStatus hwDecalBumpShader_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);
    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);
    lightX = nAttr.create( "lightX", "lgtx",MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(0.0f);
    lightY = nAttr.create( "lightY", "lgty",MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(1.0f);
    lightZ = nAttr.create( "lightZ", "lgtz",MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(1.0f);
    light = nAttr.create( "light", "lgt", lightX, lightY, lightZ);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(0.0f, 1.0f, 1.0f);
    cameraX = nAttr.create( "cameraX", "camx",MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(0.0f);
    cameraY = nAttr.create( "cameraY", "camy",MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(0.0f);
    cameraZ = nAttr.create( "cameraZ", "camz",MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(1.0f);
    camera = nAttr.create( "camera", "cam", cameraX, cameraY, cameraZ);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(0.0f, 0.0f, 1.0f);
    shininess = nAttr.create( "shininess", "sn", MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setMin(0.0f);
    nAttr.setMax(1.0f);
    nAttr.setDefault(0.5f);
    lightColorR = nAttr.create( "lightColorR", "lcr", MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(1.0f);
    lightColorG = nAttr.create( "lightColorG", "lcg", MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(1.0f);
    lightColorB = nAttr.create( "lightColorB", "lcb", MFnNumericData::kFloat);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(1.0f);
    lightColor = nAttr.create( "lightColor", "lc", lightColorR, lightColorG, lightColorB);
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    nAttr.setDefault(1.0f, 1.0f, 1.0f);
    nAttr.setUsedAsColor(true);
 
        
        
        
 
    addAttribute(color);
    addAttribute(bump);
    addAttribute(uvCoord);
    addAttribute(uBias);
    addAttribute(vBias);
    addAttribute(uvFilterSize);
        addAttribute(light);
        addAttribute(camera);
    addAttribute(shininess);
    addAttribute(lightColor);
    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);
        attributeAffects (lightX,       outColor);
        attributeAffects (lightY,       outColor);
        attributeAffects (lightZ,       outColor);
        attributeAffects (light,        outColor);
        attributeAffects (cameraX,      outColor);
        attributeAffects (cameraY,      outColor);
        attributeAffects (cameraZ,      outColor);
        attributeAffects (camera,       outColor);
    attributeAffects (shininess,   outColor);
    attributeAffects (lightColorR, outColor);
    attributeAffects (lightColorG, outColor);
    attributeAffects (lightColorB, outColor);
    attributeAffects (lightColor,  outColor);
    return MS::kSuccess;
}
MStatus hwDecalBumpShader_NV20::compute(
const MPlug&      plug,
      MDataBlock& block ) 
{ 
    bool k;
    k = (plug == outColor)  ||
        (plug == outColorR) ||
        (plug == outColorG) ||
        (plug == outColorB);
    if( !k )
        return MS::kUnknownParameter;
    
    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;
}
MStatus hwDecalBumpShader_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("hwDecalBumpShader_NV20::getFloat3 plug.getValue.");
                return status;
        }
        MFnNumericData data(object, &status);
        if (!status)
        {
                status.perror("hwDecalBumpShader_NV20::getFloat3 construct data.");
                return status;
        }
        status = data.getData(value[0], value[1], value[2]);
        if (!status)
        {
                status.perror("hwDecalBumpShader_NV20::getFloat3 get values.");
                return status;
        }
        return status;
}
MStatus hwDecalBumpShader_NV20::getString(MObject attr, MString &str)
{
        MPlug   plug(thisMObject(), attr);
        MStatus status = plug.getValue( str );
        return status;
}
MStatus hwDecalBumpShader_NV20::bind(const MDrawRequest& request, M3dView& view)
{
        MStatus status;
        
        
        float diffuse_color[4];
        status = getFloat3(color, diffuse_color);
        diffuse_color[3] = 1.0;
        if (!status)
        return status;
        
        
        float light_color[4];
        light_color[3] = 1.0f;
        status = getFloat3(lightColor, light_color);
        if (!status)
        return status;
        
        
        status = getFloat3(light, &lightRotation[0]);
        if (!status)
        return status;
        
        
        float   bumpScaleValue = 2.0f;
        
        
        bool    isHeightFieldMap = true;
        
        
        
        
        
        double  scale = M_PI/180.0;             
        MEulerRotation  lightRot( lightRotation[0] * scale, lightRotation[1] * scale, lightRotation[2] * scale );
        MVector light_v = MVector(0, 0, -1).rotateBy( lightRot );       
        MDagPath camDag;
        view.getCamera(camDag);
        light_v = light_v * camDag.inclusiveMatrixInverse();
        lightRotation[0] = (float) light_v[0];
        lightRotation[1] = (float) light_v[1];
        lightRotation[2] = (float) light_v[2];
        
        
        status = getFloat3(camera, &cameraPos[0]);
        if (!status)
        return status;
        
        
        MString decalName = "";
        MString bumpName  = "";
        ShadingConnection colorConnection(thisMObject(), request.multiPath().partialPathName(), "color");
        ShadingConnection bumpConnection (thisMObject(), request.multiPath().partialPathName(), "bump");
        
        
        if (colorConnection.type() == ShadingConnection::TEXTURE &&
                colorConnection.texture().hasFn(MFn::kFileTexture))
        {
                
                MFnDependencyNode textureNode(colorConnection.texture());
                MPlug filenamePlug( colorConnection.texture(), textureNode.attribute(MString("fileTextureName")) );
                filenamePlug.getValue(decalName);
        }
        
        
        if (bumpConnection.type() == ShadingConnection::TEXTURE &&
                bumpConnection.texture().hasFn(MFn::kFileTexture))
        {
                
                MFnDependencyNode textureNode(colorConnection.texture());
                MPlug filenamePlug( bumpConnection.texture(), textureNode.attribute(MString("fileTextureName")) );
                filenamePlug.getValue(bumpName);
        }
        
        
        if (bumpName.length() == 0 ||
                decalName.length() == 0)
        {
                view.beginGL();
                glPushAttrib( GL_ALL_ATTRIB_BITS );             
                glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
                glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
                glEnable(GL_COLOR_MATERIAL);
                glColor4fv(diffuse_color);
                view.endGL();
                return MS::kSuccess;
        }
        view.beginGL();
        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
    
        glEnable(GL_TEXTURE_SHADER_NV);
        
        
        glActiveTextureARB( GL_TEXTURE0_ARB );
        if(m_pTextureCache)
                m_pTextureCache->bind(colorConnection.texture(), MTexture::RGBA, false);
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
        
    
        glActiveTextureARB( GL_TEXTURE1_ARB );
        
        
        if( isHeightFieldMap ) {
                
                if(m_pTextureCache)
                        m_pTextureCache->bind(bumpConnection.texture(), MTexture::NMAP, false);
        }
        else {
                if(m_pTextureCache)
                        m_pTextureCache->bind(bumpConnection.texture(), MTexture::RGBA, false);
        }
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
        
        
        glActiveTextureARB( GL_TEXTURE2_ARB );
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV);
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV, GL_EXPAND_NORMAL_NV);
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE1_ARB);
        
        glActiveTextureARB( GL_TEXTURE3_ARB );
        bind_lookup_table();    
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_TEXTURE_2D_NV);
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV, GL_EXPAND_NORMAL_NV);
        glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE1_ARB);
        
        
        
        glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, diffuse_color);
        glCombinerParameterfvNV(GL_CONSTANT_COLOR1_NV, light_color);
        
        
        
        glEnable(GL_REGISTER_COMBINERS_NV);
        
#ifndef DEBUGGING_VERTEX_PROGRAM
        glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 2);
#else
        
        glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
#endif
        float constColor0[4];
        constColor0[0] = constColor0[1] = constColor0[2] = constColor0[3] = 1.0;
        glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, constColor0);
#ifndef DEBUGGING_VERTEX_PROGRAM
        
        
        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_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
        glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
        glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
        glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV, 
                                           GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
        
        
        glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
        glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
        glCombinerOutputNV(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV, 
                                           GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
#else
        
        glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_PRIMARY_COLOR_NV, 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_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
        glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
        glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV, 
                                           GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
#endif // DEBUGGING_VERTEX_PROGRAM
        
        
        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_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
        view.endGL();
        return MS::kSuccess;
}
MStatus hwDecalBumpShader_NV20::unbind(const MDrawRequest& request,
                           M3dView& view)
{
        view.beginGL();
        
        glDisable(GL_REGISTER_COMBINERS_NV);
        glDisable(GL_TEXTURE_SHADER_NV);
        glActiveTextureARB( GL_TEXTURE0_ARB );
        glPopClientAttrib();
        glPopAttrib();
        view.endGL();
        return MS::kSuccess;
}
float* hwDecalBumpShader_NV20::computeBinormals(int indexCount,
                                                                                   const unsigned int * indexArray,
                                                                                   int vertexCount,
                                                                                   const float* vertexArray, 
                                                                                   const float* normalArray, 
                                                                                   const float* texCoordArray)
{
        
        float *biNormalArray = new float[3*vertexCount];
        
        
        
        const float *   v[3];   
        const float *   t[3];   
        const float *   n[3];   
        float *                 b[3];   
        
        for (int i = 0; i < indexCount; i += 3)
        {
                int i0 = indexArray[i+0];
                int i1 = indexArray[i+1];
                int i2 = indexArray[i+2];
                
                
                
                v[0] = vertexArray + 3 * i0;
                v[1] = vertexArray + 3 * i1;
                v[2] = vertexArray + 3 * i2;
                
                
                
                t[0] = texCoordArray + 2 * i0;
                t[1] = texCoordArray + 2 * i1;
                t[2] = texCoordArray + 2 * i2;
                
                
                
                n[0] = normalArray + 3 * i0;
                n[1] = normalArray + 3 * i1;
                n[2] = normalArray + 3 * i2;
                
                
                
                b[0] = biNormalArray + 3 * i0;
                b[1] = biNormalArray + 3 * i1;
                b[2] = biNormalArray + 3 * i2;
                
                
                
                
                vec3f   plane[3];
                vec3f   c0, c1, c2;
                vec3f   s0, s1;
                
                
                
                int idx;
                for (idx = 0; idx < 3; ++idx)
                {
                        
                        c0.set_value(v[0][idx], t[0][0], t[0][1]);
                        c1.set_value(v[1][idx], t[1][0], t[1][1]);
                        c2.set_value(v[2][idx], t[2][0], t[2][1]);
                        
                        
                        s0 = c0 - c2;
                        s1 = c1 - c2;
                        
                        
                        plane[idx] = s1.cross(s0);
                }
                
                
                vec3f ds, dt, dn;
                
                for (idx = 0; idx < 3; ++idx)
                {
                        ds[idx] = -plane[idx][1]/plane[idx][0];
                        dt[idx] = -plane[idx][2]/plane[idx][0];
                }
                dt.normalize();
                dn = ds.cross(dt);
        
                
                
                vec3f normal(n[0]);
                if (normal.dot(dn) < 0)
                {
                        
                        ds.negate();
                        dt.negate();
                        dn.negate();
                }
                
                
                for (idx = 0; idx < 3; ++idx)
                {
                        vec3f vn = n[idx];
                        vec3f vb = vn.cross( dt );
                        vb.normalize();
                        
                        for( int j = 0; j < 3; ++j )
                                b[idx][j] = vb[j];
                }
        }
        return biNormalArray;
}
MStatus hwDecalBumpShader_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();
        
        
        
        
    loadVertexProgramGL();
        
        
        glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id);
        glEnable(GL_VERTEX_PROGRAM_NV);
        
        
        
        
        
        
        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);
        glClientActiveTextureARB(GL_TEXTURE0_ARB);
        view.endGL();
        return MS::kSuccess;
}
int             hwDecalBumpShader_NV20::normalsPerVertex()
{
        return 3;
}
int             hwDecalBumpShader_NV20::texCoordsPerVertex()
{
        return 1;
}
void hwDecalBumpShader_NV20::release_lookup_texture()
{
        if (lookup_table)
        {
                delete lookup_table;
                lookup_table = NULL;
        }
        
        if (lookup_texture)
        {
                delete lookup_texture;
                lookup_table = NULL;
        }
}