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

# GLSLShaderNode.cpp

    //-
    // ==========================================================================
    // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+

    #include <maya/MFnStringData.h>
    #include <maya/MFnMatrixData.h>
    #include <maya/MFnTypedAttribute.h>
    #include <maya/MFnEnumAttribute.h>
    #include <maya/MFnMatrixAttribute.h>
    #include <maya/MFloatVector.h>
    #include <maya/MGlobal.h>
    #include <maya/MDGModifier.h>
    #include <maya/MImage.h>
    #include <maya/MPlugArray.h>
    #include <maya/MFileIO.h>
    #include <maya/MMatrix.h>
    #include <maya/MFnMesh.h>
    #include <maya/MString.h>
    #include <maya/MStringResource.h>
    #include <maya/MStringResourceId.h>

    #include <sys/types.h>
    #include <sys/stat.h>

    #include <algorithm>

    #include "Platform.h"

    #include "GLSLShaderNode.h"
    #include "AshliShaderStrings.h"
    #include "glExtensions.h"

    #include "ResourceManager.h"

    #include <maya/MHWShaderSwatchGenerator.h>
    #include <maya/MHardwareRenderer.h>
    #include <maya/MGeometryData.h>
    #include <maya/MImage.h>

    // If and when a bug in Maya is fixed so that you
    // can keyframe internal dynamic attributes, the
    // code within this ifdef cannot be used.
    // If you set the attributes to be internal, you
    // can keyframe, but playback may or may not work
    // erratically. This is a known API bug.
    //
    //#define _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
    //

    //
    //statics
    //

    // This id is supplied by Maya and should never be changed.
    MTypeId glslShaderNode::sId( 0x81022);

    //these are the attributes shared by all instances
    MObject glslShaderNode::sShader;
    MObject glslShaderNode::sTechnique;
    MObject glslShaderNode::sTechniqueList; // Convenience attribute
    MObject glslShaderNode::sShaderPath; // Convenience attribute
    MObject glslShaderNode::sNakedTexCoords[8];
    MObject glslShaderNode::sNakedColors[2];

    //this is the default shader, used to render a checkered phong
    shader *glslShaderNode::sDefShader = NULL;

    //  This is a list of nodes presently created of this type.
    // It allows us to find all shader nodes and update them after a file load,
    // and it eases searching for use by the command.
    std::vector<glslShaderNode*> glslShaderNode::sNodeList;

    //default texture map strings
    //const char *glslShaderNode::nakedSetNames[8] = { "map1", "", "", "", "", "", "", ""};
    const char *glslShaderNode::nakedSetNames[8] = { "map1", "tangent", "binormal", "", "", "", "", ""};

    //
    //
    //
    glslShaderNode::glslShaderNode() : m_shader(NULL), m_shaderDirty(false), m_parsedUserAttribList( false) {

        // Make sure we have our extensions initialised
        // We do it here rather than at render time so that setting a shader program
        // in batch mode will populate the shader attributes
        //
        InitializeExtensions();

      for (int ii=0; ii<8; ii++) {
        m_nakedTexSets[ii] = nakedSetNames[ii];
        m_setNums[ii] = -1;
        m_mayaType[ii] = kNonMaya;
      }
    }

    //
    //
    //
    glslShaderNode::~glslShaderNode() {

        if (MGlobal::mayaState() != MGlobal::kBatch)
        {
            MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
            if (pRenderer)
            {
                const MString & backEndStr = pRenderer->backEndString();
                MStatus status = pRenderer->makeResourceContextCurrent( backEndStr );
                if (status != MStatus::kSuccess) {
                    MStatus strStat;
                    MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedResourceContext, strStat));
                    MGlobal::displayError(status.errorString());
                }
            }
        }

      //clean up any textures we have created
      for (std::vector<samplerAttrib>::iterator it=m_samplerAttribList.begin(); it<m_samplerAttribList.end(); it++) {
        ResourceManager::destroyTexture( it->texName);
      }

      //keep the list of allocated shaders correct
      { //this fixes MSVC and its busted scoping rules

        std::vector<glslShaderNode*>::iterator it = std::find( sNodeList.begin(), sNodeList.end(), this);
        if (it != sNodeList.end()) {
          //remove this one from the list
          sNodeList.erase(it);
        }
        else {
          //this is really bad, somehow one got created without going through create
        }
      }

      delete m_shader;
    }

    //
    //
    //
    void glslShaderNode::postConstructor() {
    }

    //
    //compute when DAG change occurs (likely nothing for HwShader)
    //
    MStatus glslShaderNode::compute( const MPlug& plug, MDataBlock& data) {

      //need to look to compute a default color

      if ((plug == outColor) || (plug.parent() == outColor))
      {
        MFloatVector color(.95f, .1f, .07f);
        MDataHandle outputHandle = data.outputValue( outColor );
        outputHandle.asFloatVector() = color;
        outputHandle.setClean();
        return MS::kSuccess;
      }

      return MS::kUnknownParameter;
    }

    //
    //factory for node creation
    //
    void* glslShaderNode::creator() {
      glslShaderNode *ret = new glslShaderNode;

      //add this new node to the list
      sNodeList.push_back(ret);

      return ret;
    }

    //
    // initialization (static)
    //
    MStatus glslShaderNode::initialize() {

      //add the necessary nodes
      MFnTypedAttribute typedAttr;
      MFnStringData stringData;
      MStatus stat;
      MObject string;

      //  The attribute names and short names below are selected to be compatible with
      // the CgFX plugin for simplicity

      //
      // Create the shader attribute and add it to the node type
      //

      // The shader attribute holds the file name of an fx file
      string = stringData.create(&stat);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      sShader = typedAttr.create("shader", "s", MFnData::kString, string, &stat);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      stat = typedAttr.setInternal(true);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      // Add the attribute to the node
      if ( addAttribute(sShader) != MS::kSuccess)
        return MS::kFailure;

      //
      // Create the technique
      //

      // The technique attribute holds the currently selected technique name
      string = stringData.create(&stat);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      sTechnique = typedAttr.create("technique", "t", MFnData::kString, string, &stat);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      stat = typedAttr.setInternal(true);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      // Add the attribute to the node
      if ( addAttribute(sTechnique) != MS::kSuccess)
        return MS::kFailure;

      // Create a techniqueList to allow access by the AE for UI purposes
      sTechniqueList = typedAttr.create("techniqueList", "tl", MFnData::kString, string, &stat);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      stat = typedAttr.setInternal(true);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      typedAttr.setWritable( false );
      typedAttr.setStorable( false );
      typedAttr.setHidden( true );
      typedAttr.setConnectable( false );

      // Add the attribute to the node
      if ( addAttribute(sTechniqueList) != MS::kSuccess)
        return MS::kFailure;

      // Create a shader path attrib to allow access by the AE for UI purposes
      sShaderPath = typedAttr.create("shaderPath", "sp", MFnData::kString, string, &stat);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      stat = typedAttr.setInternal(true);
      if (stat != MS::kSuccess)
        return MS::kFailure;

      typedAttr.setWritable( false );
      typedAttr.setStorable( false );
      typedAttr.setHidden( true );
      typedAttr.setConnectable( false );

      // Add the attribute to the node
      if ( addAttribute(sShaderPath) != MS::kSuccess)
        return MS::kFailure;

      int ii;

      //
      // Add the naked texture coord sets to the node
      //
      for (ii=0; ii<8; ii++) {
        string = stringData.create( MString(nakedSetNames[ii]));
        sNakedTexCoords[ii] = typedAttr.create( MString("TexCoord") + ii, MString("t")+ ii, MFnData::kString, string);
        typedAttr.setInternal( true);
        typedAttr.setKeyable( true );
        addAttribute( sNakedTexCoords[ii]);
      }

      // Add the naked color sets to the node
      for (ii=0; ii<2; ii++) {
        sNakedColors[ii] = typedAttr.create( MString("Color") + ii, MString("c")+ ii, MFnData::kString);
        typedAttr.setInternal( true);
        typedAttr.setKeyable( true );
        addAttribute( sNakedColors[ii]);
      }

      return MS::kSuccess;
    }

    //
    // callback to configure shaders, post file load
    //
    void glslShaderNode::rejigShaders( void *data) {

      //iterate over all shaders, and rebuild them, so they can reconnect attributes
      for (std::vector<glslShaderNode*>::iterator it = sNodeList.begin(); it < sNodeList.end(); it++) {
        (*it)->rebuildShader();
      }
    }

    //
    // Update shader and attributes
    //
    static bool textureInitPowerOfTwo(unsigned int val, unsigned int & retval)
    {
        unsigned int res = 0;               // May be we should return 1 when val == 0
        if (val)
        {
            // Muliply all values by 2, to simplify the testing:
            // 3*(res/2) < val*2 <= 3*res
            val <<= 1;
            unsigned int low = 3;
            res = 1;
            while (val > low)
            {
                low <<= 1;
                res <<= 1;
            }
        }

        retval = res;
        return (res == (val>>1)) ? 1 : 0;
    }

    bool glslShaderNode::rebuildShader()
    {
      //handle shader initialization

        bool rebuiltAnything = false;

        GL_CHECK;

        // Re (build) current shader
        if (m_shaderDirty) {

            MGlobal::displayInfo(MString("Rebuild shader : ") + m_shaderPath.asChar() );

            // Clean up old shader (delete NULL is safe, does not need protected)
            delete m_shader;
            GL_CHECK;

            // Create a new shader
            m_shader = shader::create(m_shaderPath.asChar());
            GL_CHECK;

            if (!m_shader) {
                MStatus strStat;
                MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedLoadShader, strStat));
                MGlobal::displayError(shader::sError.c_str());
            }
            else {
                rebuiltAnything = true;

                // Try to reuse the existing technique name.
                // This will allow for being able to re-read
                // files which have saved a technique name which
                // is not technique 0.
                bool foundExistingTechnique = false;
                if (m_techniqueName.length())
                {
                    for (int ii=0; ii<m_shader->techniqueCount(); ii++) {
                        if (m_techniqueName  == m_shader->techniqueName(ii)) {
                            m_shader->setTechnique(ii);
                            foundExistingTechnique = true;
                            break;
                        }
                    }
                }
                // Just use technique 0 as a default, since we don't
                // know which one to use.
                if (!foundExistingTechnique)
                {
                    m_techniqueName = m_shader->techniqueName(0);
                }

                m_techniqueList = m_techniqueName ;
                for (int ii=1; ii<m_shader->techniqueCount(); ii++)
                    m_techniqueList = m_techniqueList + " " + m_shader->techniqueName(ii);

                // Reconfigure node attributes
                configureAttribs();
                GL_CHECK;
            }
            m_shaderDirty = false;
        }

        m_techniqueList = "";
        if (m_shader && m_shader->techniqueCount())
        {
            for (int ii=0; ii<m_shader->techniqueCount(); ii++)
                m_techniqueList = m_techniqueList + " " + m_shader->techniqueName(ii);
        }

        return rebuiltAnything;
    }

    //
    // search the dependency graph for a file texture node
    //
    MObject glslShaderNode::findFileTextureNode( const MPlug &plug) {
      MPlugArray arr;
      MStatus stat;
      plug.connectedTo(arr, true, false, &stat);
      MObject srcNode;

      if (stat != MStatus::kSuccess)
        return MObject::kNullObj;

      if (arr.length() == 1)
        srcNode = arr[0].node();
      else if (arr.length() == 0 ) {
        //plug is not connected
        return MObject::kNullObj;
      }
      else {
        //plug is multiply connected
        MGlobal::displayWarning("Texture plug is connected to multiple nodes");
        return MObject::kNullObj;
      }

      if( srcNode != MObject::kNullObj) {
        MStatus rc;
        MFnDependencyNode dgFn( srcNode, &rc);
        MPlug fnPlug = dgFn.findPlug( "fileTextureName", &rc);
        if (rc == MStatus::kSuccess) {
          MStatus tStat;
          MObject fTex = fnPlug.node(&tStat);
          if ( tStat != MStatus::kSuccess)
            return MObject::kNullObj;
          else
            return fTex;
        }
      }
      return MObject::kNullObj;
    }

    //
    // search the dependency graph for an envCube node
    //
    MObject glslShaderNode::findEnvCubeNode( const MPlug &plug) {
      MPlugArray arr;
      plug.connectedTo(arr, true, false);
      MObject srcNode;

      if (arr.length() == 1)
        srcNode = arr[0].node();
      else if (arr.length() == 0 ) {
        //plug is not connected
        return MObject::kNullObj;
      }
      else {
        //plug is multiply connected
        MGlobal::displayWarning("Texture plug is connected to multiple nodes");
        return MObject::kNullObj;
      }

      // if we have a real node, look for the envCube
      if( srcNode != MObject::kNullObj) {
        MStatus rc;
        MFnDependencyNode dgFn( srcNode, &rc);

        // searching for the plug seems a pretty good solution
        MPlug fnPlug = dgFn.findPlug( "left", &rc);

        if (rc == MStatus::kSuccess) {
          MStatus tStat;
          MObject fCube = fnPlug.node(&tStat);
          if ( tStat != MStatus::kSuccess)
            return MObject::kNullObj;
          else
            return fCube;
        }
      }
      return MObject::kNullObj;
    }

    //
    // load a cubemap face from a cubeEnv node
    //
    void glslShaderNode::loadCubeFace( glslShaderNode::CubeFace cf, MObject &cube, MFnDependencyNode &dn){
      const char* faceNames[] = { "left", "right", "top", "bottom", "front", "back"};
      const GLenum targets[] = {GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
      float rgb[3];

      MObject face = dn.attribute( faceNames[cf]);
      MPlug plug( cube, face);
      MObject fTex = findFileTextureNode( plug);

      if (fTex != MObject::kNullObj) {
          MImage img;
          unsigned int width, height;

          img.readFromTextureNode(fTex);
          MStatus status = img.getSize( width, height);

          // If not power of two and NPOT is not supported, then we need
          // to resize the original system pixmap before binding.
          //
          // Also handle non-square cube map textures
          bool resizeToBeSquare = false;
          if (width != height)
          {
              width = height;
              resizeToBeSquare = true;
          }
          if (width > 0 && height > 0 && (status != MStatus::kFailure) )
          {
              // If not power of two and NPOT is not supported, then we need
              // to resize the original system pixmap before binding.
              //
              if (!glTextureNonPowerOfTwo || resizeToBeSquare )
              {
                  if (width > 2 && height > 2)
                  {
                      unsigned int p2Width, p2Height;
                      bool widthPowerOfTwo  = textureInitPowerOfTwo(width,  p2Width);
                      bool heightPowerOfTwo = textureInitPowerOfTwo(height, p2Height);
                      if(!widthPowerOfTwo || !heightPowerOfTwo || resizeToBeSquare)
                      {
                          width = p2Width;
                          height = p2Height;
                          img.resize( p2Width, p2Height, false /* preserverAspectRatio */);
                      }
                  }
              }

              //MGlobal::displayInfo( MString("Loading ") + faceNames[cf]+ " face (" + width + "," + height + ")");
              glTexImage2D( targets[cf], 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels());
              GL_CHECK;
          }
      }
      else {
        MObject data;
        plug.getValue( data);
        MFnNumericData val(data);
        val.getData( rgb[0], rgb[1], rgb[2]);
        glTexImage2D( targets[cf], 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_FLOAT, rgb);
      }
    }

    //
    //  Bind data such as texture objects for samplers.
    //
    void glslShaderNode::BindSamplerData() {

      //iterate over all samplers
      for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) {

        //only update if it has been marked dirty
        if (it->dirty) {
          it->dirty = false;

          //get the plug and associated info
          MPlug plug(thisMObject(), it->attrib);
          MObject data;
          plug.getValue(data);
          MFnNumericData val(data);
          float rgb[3];

          if (it->texName == 0) {
            //need to gen a new name, one has not been created
            glGenTextures( 1, &(it->texName));
            m_shader->updateSampler( it->pNum, it->texName);
            GL_CHECK;
          }

          switch (m_shader->samplerType(it->pNum) ) {

            case shader::st1D:
              //just load a 1D texture from the plug value until we handle files directly
              val.getData( rgb[0], rgb[1], rgb[2]);
              glBindTexture( GL_TEXTURE_1D, it->texName);
              glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, it->minFilter);
              glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, it->magFilter);
              glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, it->wrapMode);
              glTexParameterf( GL_TEXTURE_1D, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso);
              if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) {
                glTexParameteri( GL_TEXTURE_1D, GL_GENERATE_MIPMAP, GL_TRUE);
              }
              else {
                glTexParameteri( GL_TEXTURE_1D, GL_GENERATE_MIPMAP, GL_FALSE);
              }
              glTexImage1D( GL_TEXTURE_1D, 0, GL_RGB8, 1, 0, GL_RGB, GL_FLOAT, rgb);
              GL_CHECK;
              break;

            case shader::st1DShadow:
              //are shadow maps accessible in Maya?
              break;

            case shader::st2D:
              {
                bool done = false;
                //set up the texture stuff
                glBindTexture( GL_TEXTURE_2D, it->texName);
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, it->minFilter);
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, it->magFilter);
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, it->wrapMode);
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, it->wrapMode);
                glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso);
                if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) {
                  glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
                }
                else {
                  glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
                }
                MObject fTex = findFileTextureNode( plug);

                if (fTex != MObject::kNullObj) {
                    MImage img;
                    unsigned int width, height;

                    img.readFromTextureNode(fTex);
                    MStatus status = img.getSize( width, height);
                    if (width > 0 && height > 0 && (status != MStatus::kFailure) )
                    {
                        // If not power of two and NPOT is not supported, then we need
                        // to resize the original system pixmap before binding.
                        //
                        if (!glTextureNonPowerOfTwo)
                        {
                            if (width > 2 && height > 2)
                            {
                                unsigned int p2Width, p2Height;
                                bool widthPowerOfTwo  = textureInitPowerOfTwo(width,  p2Width);
                                bool heightPowerOfTwo = textureInitPowerOfTwo(height, p2Height);
                                if(!widthPowerOfTwo || !heightPowerOfTwo)
                                {
                                    width = p2Width;
                                    height = p2Height;
                                    img.resize( p2Width, p2Height, false /* preserverAspectRatio */);
                                }
                            }
                        }
                        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels());
                        GL_CHECK;
                        done = true;
                    }
                }

                if (!done) {
                  //just read it from the plug
                  val.getData( rgb[0], rgb[1], rgb[2]);
                  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_FLOAT, rgb);
                }
                GL_CHECK;
              }
              break;

            case shader::stCube:
              {
                //search for envCube node, then traverse off the 6 faces
                glBindTexture( GL_TEXTURE_CUBE_MAP, it->texName);
                glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, it->minFilter);
                glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, it->magFilter);
                glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, it->wrapMode);
                glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, it->wrapMode);
                glTexParameterf( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso);
                if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) {
                  glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE);
                }
                else {
                  glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_FALSE);
                }
                MObject cube = findEnvCubeNode( plug);
                if ( cube != MObject::kNullObj) {
                  MFnDependencyNode dn(cube);
                  loadCubeFace( cfLeft, cube, dn);
                  loadCubeFace( cfRight, cube, dn);
                  loadCubeFace( cfTop, cube, dn);
                  loadCubeFace( cfBottom, cube, dn);
                  loadCubeFace( cfFront, cube, dn);
                  loadCubeFace( cfBack, cube, dn);
                }
              }
              break;

            case shader::st2DShadow:
            case shader::st3D:
              //need to handle file loading directly for 3D textures
              break;
            default:
              break;
          };
        }
      }
    }

    //
    //
    //
    void glslShaderNode::BindUniformData() {

      for ( std::vector<uniformAttrib>::iterator it = m_uniformAttribList.begin(); it < m_uniformAttribList.end(); it++) {

        // [ANIM_UNIFORM_FIX2]
        // To allow animation to work, uniform data has been
        // left to be dirty all of the time, as internal attributes
        // do not update properly during playback.
        //
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        if (it->dirty) {
    #endif
          it->dirty = false;

          MPlug plug(thisMObject(), it->attrib);
          MObject data;
          plug.getValue(data);
          MFnNumericData val(data);
          float fTemp[4];
          int iTemp[4];
          bool bTemp[4];

          switch (m_shader->uniformType(it->pNum)) {

            case shader::dtBool:
              plug.getValue(bTemp[0]);
              m_shader->updateUniformBool( it->pNum, bTemp[0]);
              break;

            case shader::dtBVec2:
            case shader::dtBVec3:
            case shader::dtBVec4:
              //no Maya native type, these are unsupported for now
              break;

            case shader::dtInt:
              plug.getValue(iTemp[0]);
              m_shader->updateUniformInt( it->pNum, iTemp[0]);
              break;

            case shader::dtIVec2:
              val.getData( iTemp[0], iTemp[1]);
              m_shader->updateUniformIVec( it->pNum, iTemp);
              break;

            case shader::dtIVec3:
              val.getData( iTemp[0], iTemp[1], iTemp[2]);
              m_shader->updateUniformIVec( it->pNum, iTemp);
              break;

            case shader::dtIVec4:
              //no Maya native type, these are unsupported for now
              break;

            case shader::dtFloat:
              plug.getValue(fTemp[0]);
              m_shader->updateUniformFloat( it->pNum, fTemp[0]);
              break;

            case shader::dtVec2:
              val.getData( fTemp[0], fTemp[1]);
              m_shader->updateUniformVec( it->pNum, fTemp);
              break;

            case shader::dtVec3:
              val.getData( fTemp[0], fTemp[1], fTemp[2]);
              m_shader->updateUniformVec( it->pNum, fTemp);
              break;

            case shader::dtVec4:
              {
                MPlug plug2(thisMObject(), it->attrib2);
                val.getData( fTemp[0], fTemp[1], fTemp[2]);
                plug2.getValue( fTemp[3]);
                m_shader->updateUniformVec( it->pNum, fTemp);
              }
              break;

            case shader::dtMat4:
              {
                MFnMatrixData mValue(data);
                MMatrix matrix = mValue.matrix();
                m_shader->updateUniformMat( it->pNum, (double*)matrix.matrix);
              }
              break;

            case shader::dtMat2:
            case shader::dtMat3:

              //unsupported for now, due to Maya's interface presently lacking support
              break;
            default:
              break;
          };
        }
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
      }
    #endif
    }

    // Method to override to let Maya know this shader is batchable.
    //
    bool glslShaderNode::supportsBatching() const
    {
        return true;
    }

    //
    // Old style drawing commands like the HW renderer needs
    //
    MStatus glslShaderNode::glBind(const MDagPath& shapePath) {

        // Rebuild shader, if dirty
        // This is only required pre 7.0, and should not be part of
        // the plugin anymore unless we decide to ship with 6.5.
        //

      // Initialize GL extesions
      if (!InitializeExtensions()) {
        MStatus strStat;
        MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeAshliFailedFindExtensions, strStat));
        return MStatus::kFailure;
      }
        GL_CHECK;

        //attempt to clean up any delayed deletions
        ResourceManager::recover();

        // Build default shader
        if (!sDefShader)
            sDefShader = new defaultShader;
        GL_CHECK;

      //
      //bind any dirty data

      //should add an extra global dirty flag
      if (m_shader) {
        if (!m_shader->build())
          MGlobal::displayError(MString(m_shader->errorString()));

        BindUniformData();

        //now the samplers
        BindSamplerData();
      }

      // Disable these to avoid possible problems with shader programs
      // going into software
      glDisable(GL_POINT_SMOOTH);
      glDisable( GL_LINE_SMOOTH);

      //check for active shader, if none, fall back to the default shader
      if ((m_shader != NULL) && (m_shader->valid()))
      {
        m_passCount = m_shader->passCount();

        if (!m_shader->build()) {
          m_passCount = 0;
        }

        if( m_passCount == 1)
        {
            m_shader->setPass( 0);
            m_shader->bind();
        }
      }
      else
      {
        m_passCount = -1;
        m_maxSetNum = 1; // We need the default UV
        sDefShader->bind();
      }

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnable(GL_VERTEX_PROGRAM_TWO_SIDE);

      GL_CHECK;

      return MS::kSuccess;
    }

    //
    //
    //
    MStatus glslShaderNode::glUnbind(const MDagPath& shapePath) {

        for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++)
            glDisableVertexAttribArray( it->handle);

        for( int ii = 0; ii < m_maxSetNum; ii++)
            m_glState.disableTexCoord( ii);
        m_glState.activeTexture( 0);
        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisable(GL_VERTEX_PROGRAM_TWO_SIDE);

        // If we're not using multi-pass, disable the effect here
        if( m_passCount < 0)
            sDefShader->unbind();
        else if( m_passCount == 1)
            m_shader->unbind();

      // Dirty our attribute list so it gets re-built next time
      // this shader is used
      m_parsedUserAttribList = false;

      return MS::kSuccess;
    }

    //
    //
    //
    void glslShaderNode::updateBoundAttributes( const MDagPath& shapePath) {
      //
      // This code needs to get the current matrices
      //  the world matrix is easy (dagPath)
      //  The view matrix seems like it can come from the current M3DView
      //  I can't see any good way to get the projection matrix other than reading it
      //
      MMatrix world;
      MMatrix temp;
      MMatrix proj;
      MMatrix view;
      MMatrix worldview;

      //get the matrices
      if (shapePath.isValid())
          world = shapePath.inclusiveMatrix();
      else
          world.setToIdentity();
      glGetDoublev( GL_PROJECTION_MATRIX, (double*)proj.matrix);
      glGetDoublev( GL_MODELVIEW_MATRIX, (double*)worldview.matrix);
      view = world.inverse() * worldview;

      for (std::vector<boundUniform>::iterator it = m_boundUniformList.begin(); it < m_boundUniformList.end(); it++) {
        switch ( it->usage) {

          case shader::smWorld:
            m_shader->updateUniformMat( it->pNum, (double*)world.matrix);
            break;

          case shader::smView:
            m_shader->updateUniformMat( it->pNum, (double*)view.matrix);
            break;

          case shader::smProjection:
            m_shader->updateUniformMat( it->pNum, (double*)proj.matrix);
            break;

          case shader::smWorldView:
            m_shader->updateUniformMat( it->pNum, (double*)worldview.matrix);
            break;

          case shader::smViewProjection:
            temp = view * proj;
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldViewProjection:
            temp = worldview * proj;
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldI:
            temp = world.inverse();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smViewI:
            temp = view.inverse();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smProjectionI:
            temp = proj.inverse();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldViewI:
            temp = worldview.inverse();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smViewProjectionI:
            temp = view * proj;
            temp = temp.inverse();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldViewProjectionI:
            temp = worldview * proj;
            temp = temp.inverse();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldT:
            temp = world.transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smViewT:
            temp = view.transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smProjectionT:
            temp = proj.transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldViewT:
            temp = worldview.transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smViewProjectionT:
            temp = view * proj;
            temp = temp.transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldViewProjectionT:
            temp = worldview * proj;
            temp = temp.transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldIT:
            temp = world.inverse().transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smViewIT:
            temp = view.inverse().transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smProjectionIT:
            temp = proj.inverse().transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldViewIT:
            temp = worldview.inverse().transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smViewProjectionIT:
            temp = view * proj;
            temp = temp.inverse().transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smWorldViewProjectionIT:
            temp = worldview * proj;
            temp = temp.inverse().transpose();
            m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
            break;

          case shader::smViewPosition:
            {
              float vec[4];
              temp = view.inverse();

              vec[0] = (float)temp[3][0];
              vec[1] = (float)temp[3][1];
              vec[2] = (float)temp[3][2];
              vec[3] = (float)temp[3][3];

              m_shader->updateUniformVec( it->pNum, vec);
            }
            break;

          case shader::smTime:
            // need to find a good way to get the current animation time
            //  the cgfx plugin uses:
            //    (float)timeGetTime() * 1000.0f
            // This has two problems
            //  1. one it does not correspond to Maya time
            //  2. it has portability problems
            m_shader->updateUniformFloat( it->pNum, 0.0f);
            break;

          case shader::smViewportSize:
            {
              float vp[4];
              glGetFloatv( GL_VIEWPORT, vp);
              m_shader->updateUniformVec( it->pNum, &vp[2]);
            }
            break;

          default:
            break;
        };
      }
    }

    //
    //  Configure the texture coordinate sets and color sets for rendering. This
    // function sets up all the vertex arry pointers or current values, if a set
    // is not available.
    void glslShaderNode::configureTexCoords( int normalCount, const float ** normalArrays,
                                            int texCoordCount,
                                            const float ** texCoordArrays,
                                            int colorCount,
                                            const float ** colorArrays)
    {

      //printf("*********** configureTexCoords [normal count = %d]\n", normalCount);

      int ii;

      //bind in the naked texture sets
      for (ii=0; ii<m_maxSetNum; ii++) {

        //handle the special numbers
        if (m_setNums[ii] == -1) {
          // no name is specified, send a default texture coord of (0,0)
          glMultiTexCoord2f( GL_TEXTURE0 + ii, 0.0f, 0.0f);
          m_glState.disableTexCoord( ii);
          continue;
        }
        else if (m_mayaType[ii] == kNormal)  {

          //this set has requested a normal
          if ( (normalCount > 0) && normalArrays[0]) {
            //there is a tangent
            m_glState.enableAndActivateTexCoord( ii);
            glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[0]);
          }
          else {
            //there is no tangent available
            glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 0.0f, 1.0f);
            m_glState.disableTexCoord( ii);
          }
          continue;
        }
        else if (m_mayaType[ii] == kTangent)  {

          //this set has requested a tangent
          if ( m_numNormals > 1  && normalCount > 1)
          {
              int offset = 0;
              if (m_numNormals > 2)
                offset = ( m_setNums[ii] * 3 ) + 1;
              else
                offset = ( m_setNums[ii] * 2 ) + 1;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 2)
                      offset = 1;
                  else
                      offset = -1;
              }

              //printf("TANGENT%d : Access tangents via collection id(%d) = offset %d\n",
            //      ii, m_setNums[ii], offset );

              if ((offset > 0) && normalArrays[offset]) {
                  //there is a tangent
                  m_glState.enableAndActivateTexCoord( ii);
                  glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
              }
              else {
                  //there is no tangent available
                  glMultiTexCoord3f( GL_TEXTURE0 + ii, 1.0f, 0.0f, 0.0f);
                  m_glState.disableTexCoord( ii);
              }
          }
          else
          {
              //there is no tangent available
              glMultiTexCoord3f( GL_TEXTURE0 + ii, 1.0f, 0.0f, 0.0f);
              m_glState.disableTexCoord( ii);
          }
          continue;
        }
        else if (m_mayaType[ii] == kBinormal)  {

          //this set has requested a binormal
          if ( m_numNormals > 2 && normalCount > 1)
          {
              int offset = ( m_setNums[ii] * 3 ) + 2;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 3)
                      offset = 2;
                  else
                      offset = -1;
              }

              //printf("BINORMAL%d : Access binormals via collection id(%d) = offset %d\n",
                //  ii, m_setNums[ii], offset );

              if ((offset > 0) && normalArrays[offset] )
              {
                  //there is a binormal
                  m_glState.enableAndActivateTexCoord( ii);
                  glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
              }
              else
              {
                  //there is no binormal available
                  glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f);
                  m_glState.disableTexCoord( ii);
              }
          }
          else
          {
            //there is no binormal available
            glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f);
            m_glState.disableTexCoord( ii);
          }
          continue;
        }
        else if (m_mayaType[ii] == kColorSet) {

          //this set has requested a color
          if ( (m_setNums[ii] < colorCount) && colorArrays[m_setNums[ii]]) {
            //there is a color
            m_glState.enableAndActivateTexCoord( ii);
            glTexCoordPointer( 4, GL_FLOAT, 0, colorArrays[m_setNums[ii]]);
          }
          else {
            //there is no binormal available
            glMultiTexCoord4f( GL_TEXTURE0 + ii, 0.0f, 0.0f, 0.0f, 1.0f);
            m_glState.disableTexCoord( ii);
          }
          continue;
        }

        //now deal with regular sets
        if ( (m_setNums[ii] < texCoordCount) && texCoordArrays[m_setNums[ii]]) {
          //this set exists
          m_glState.enableAndActivateTexCoord( ii);
          glTexCoordPointer( 2, GL_FLOAT, 0, texCoordArrays[m_setNums[ii]]);
        }
        else {
          //the set does not exist
          glMultiTexCoord2f( GL_TEXTURE0 + ii, 0.0f, 0.0f);
          m_glState.disableTexCoord( ii);
        }
      }

      //bind in the naked color sets

      //primary color first
        glColor4f( 1.0f, 1.0f, 1.0f, 1.0f);
        glDisableClientState( GL_COLOR_ARRAY);

      //handle the special numbers
      if (m_colorSetNums[0] == -1) {
        // no name is specified, send a default color of (1,1,1,1)
        glColor4f( 1.0f, 1.0f, 1.0f, 1.0f);
        glDisableClientState( GL_COLOR_ARRAY);
      }
      else if (m_colorMayaType[0] == kNormal)  {

          //this set has requested a normal
        if ( (normalCount > 0) && normalArrays[0]) {
          //there is a tangent
          glColorPointer( 3, GL_FLOAT, 0, normalArrays[0]);
          glEnableClientState( GL_COLOR_ARRAY);
        }
        else {
          //there is no normal available
          glColor4f( 0.0f, 0.0f, 1.0f, 1.0f);
          glDisableClientState( GL_COLOR_ARRAY);
        }
      }
      else if (m_colorMayaType[0] == kTangent)  {

          //this set has requested a tangent
          if ( m_numNormals > 1 && normalCount > 1)
          {
              int offset = 0;
              if (m_numNormals > 2)
                  offset = ( m_colorSetNums[0] * 3 ) + 1;
              else
                  offset = ( m_colorSetNums[0] * 2 ) + 1;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 2)
                      offset = 1;
                  else
                      offset = -1;
              }

              //printf("COLOR0: Access tangents via collection id(%d) = offset %d\n",
                //  m_setNums[ii], offset );

              if ((offset > 0) && normalArrays[offset]) {
                  //there is a tangent
                  glColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
                  glEnableClientState( GL_COLOR_ARRAY);
              }
              else {
                  //there is no tangent available
                  glColor4f( 1.0f, 0.0f, 0.0f, 1.0f);
                  glDisableClientState( GL_COLOR_ARRAY);
              }
          }
          else {
              //there is no tangent available
              glColor4f( 1.0f, 0.0f, 0.0f, 1.0f);
              glDisableClientState( GL_COLOR_ARRAY);
          }
      }
      else if (m_colorMayaType[0] == kBinormal)  {

          //this set has requested a binormal
          if ( m_numNormals > 2 && normalCount > 1)
          {
              int offset = ( m_colorSetNums[0] * 3 ) + 2;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 3)
                      offset = 2;
                  else
                      offset = -1;
              }

              //printf("COLOR0: Access binormals via collection id(%d) = offset %d\n",
                //  m_setNums[ii], offset );

              if ((offset > 0) && normalArrays[offset] )
              {
                  //there is a binormal
                  glColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
                  glEnableClientState( GL_COLOR_ARRAY);
              }
              else
              {
                  //there is no binormal available
                  glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f);
                  m_glState.disableTexCoord( ii);
              }
          }
          else
          {
              //there is no binormal available
              glColor4f( 0.0f, 1.0f, 0.0f, 1.0f);
              glDisableClientState( GL_COLOR_ARRAY);
          }
      }
      // Route color set through color 0
      else if (m_colorMayaType[0] == kColorSet) {
        //this set has requested a color
        if ( (m_colorSetNums[0] < colorCount) && colorArrays[m_colorSetNums[0]]) {
          //there is a color
          glColorPointer( 4, GL_FLOAT, 0, colorArrays[m_colorSetNums[0]]);
          glEnableClientState( GL_COLOR_ARRAY);
        }
        else {
          //there is no color available
          glColor4f( 0.0f, 0.0f, 0.0f, 1.0f);
          glDisableClientState( GL_COLOR_ARRAY);
        }
      }
      // Route a uv set through color 0
      else if (m_colorMayaType[0] == kUvSet) {
        //this set has requested a uvset
        if ( (m_colorSetNums[0] < texCoordCount) && texCoordArrays[m_colorSetNums[0]]) {
          // For now Maya only supports 2 compont tex coords.
          glColorPointer( 2, GL_FLOAT, 0, texCoordArrays[m_colorSetNums[0]]);
          glEnableClientState( GL_COLOR_ARRAY);
        }
        else {
          //there is no color available
          glColor4f( 1.0f, 0.0f, 0.0f, 1.0f);
          glDisableClientState( GL_COLOR_ARRAY);
        }
      }

      //secondary color
      if (glSecondaryColorSupport) {

        glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
        glDisableClientState( GL_SECONDARY_COLOR_ARRAY);

        //handle the special numbers
        if (m_colorSetNums[1] == -1) {
            // no name is specified, send a default color of (1,1,1)
            glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
            glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
        }
        else if (m_colorMayaType[1] == kNormal)  {
          //this set has requested a normal
          if ( (normalCount > 0) && normalArrays[0]) {
            //there is a tangent
            glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[0]);
            glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
          }
          else {
            //there is no normal available
            glSecondaryColor3f( 0.0f, 0.0f, 1.0f);
            glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
          }
        }
        else if (m_colorMayaType[1] == kTangent)  {

          //this set has requested a tangent
          if ( m_numNormals > 1 && normalCount > 1)
          {
              int offset = 0;
              if (m_numNormals > 2)
                  offset = ( m_colorSetNums[1] * 3 ) + 1;
              else
                  offset = ( m_colorSetNums[1] * 2 ) + 1;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 2)
                      offset = 1;
                  else
                      offset = -1;
              }

              //printf("COLOR1: Access tangents via collection id(%d) = offset %d\n",
                //  m_setNums[ii], offset );

              if ((offset > 0) && normalArrays[offset]) {
                  //there is a tangent
                  glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
                  glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
              }
              else {
                  //there is no tangent available
                  glSecondaryColor3f( 1.0f, 0.0f, 0.0f);
                  glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
              }
          }
          else {
              //there is no tangent available
              glSecondaryColor3f( 1.0f, 0.0f, 0.0f);
              glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
          }
        }
        else if (m_colorMayaType[1] == kBinormal)  {

          //this set has requested a binormal
          if ( m_numNormals > 2 && normalCount > 1 )
          {
              int offset = ( m_colorSetNums[1] * 3 ) + 2;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 3)
                      offset = 2;
                  else
                      offset = -1;
              }

              //printf("COLOR1 : Access binormals via collection id(%d) = offset %d\n",
                //  m_setNums[ii], offset );

              if ((offset > 0) && normalArrays[offset] )
              {
                  //there is a binormal
                  glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
                  glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
              }
              else
              {
                  //there is no binormal available
                  glSecondaryColor3f( 0.0f, 1.0f, 0.0f);
                  glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
              }
          }
          else
          {
              //there is no binormal available
              glSecondaryColor3f( 0.0f, 1.0f, 0.0f);
              glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
          }
        }
        // Route a color set through color 1
        else if (m_colorMayaType[1] == kColorSet) {
          //this set has requested a color
          if ( (m_colorSetNums[1] < colorCount) && colorArrays[m_colorSetNums[1]]) {
            //there is a color
            glSecondaryColorPointer( 4, GL_FLOAT, 0, colorArrays[m_colorSetNums[1]]);
            glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
          }
          else {
            //there is no color available
            glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
            glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
          }
        }
    #if 0
        // Route a uv set through color 1
        else if (m_colorMayaType[1] == kUvSet) {
            //this set has requested a uvset
            if ( (m_colorSetNums[1] < texCoordCount) && texCoordArrays[m_colorSetNums[1]]) {
                // For now Maya only supports 2 compont tex coords.
                glSecondaryColorPointer( 2, GL_FLOAT, 0, texCoordArrays[m_colorSetNums[1]]);
                glEnableClientState( GL_COLOR_ARRAY);
            }
            else {
                //there is no color available
                glColor4f( 0.0f, 0.0f, 0.0f, 1.0f);
                glDisableClientState( GL_COLOR_ARRAY);
            }
        }
    #endif
      }

      //handle the generic attribute sets
      for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
        //I need to find a better place for this then here
        it->handle = m_shader->attributeHandle(it->pNum);
        if (it->set == -1) {
          glDisableVertexAttribArray( it->handle);
          glVertexAttrib2f( it->handle, 0.0f, 0.0f);
          continue;
        }
        else if (it->mtype == kNormal) {
          //this set has requested a tangent
          if (( normalCount > 0) && normalArrays[0]) {
            //there is a tangent
            glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[0]);
            glEnableVertexAttribArray( it->handle);
          }
          else {
            //there is no tangent available
            glVertexAttrib2f( it->handle, 0.0f, 0.0f);
            glDisableVertexAttribArray( it->handle);
          }
          continue;
        }
        else if (it->mtype == kTangent) {

          //this set has requested a tangent
          if ( m_numNormals > 1 && normalCount > 1)
          {
              int offset = 0;
              if (m_numNormals > 2)
                  offset = ( it->set * 3 ) + 1;
              else
                  offset = ( it->set * 2 ) + 1;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 2)
                      offset = 1;
                  else
                      offset = -1;
              }

              //printf("VTX_ATTRIB: Access tangents via collection id(%d) = offset %d\n",
                //  it->set, offset );

              if ((offset > 0) && normalArrays[offset]) {
                  //there is a tangent
                  glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[offset]);
                  glEnableVertexAttribArray( it->handle);
              }
              else {
                  //there is no tangent available
                  glVertexAttrib2f( it->handle, 0.0f, 0.0f);
                  glDisableVertexAttribArray( it->handle);
              }
          }
          else {
              //there is no tangent available
              glVertexAttrib2f( it->handle, 0.0f, 0.0f);
              glDisableVertexAttribArray( it->handle);
          }
          continue;
        }
        else if (it->mtype == kBinormal) {

          //this set has requested a binormal
          if ( m_numNormals > 2 && normalCount > 1)
          {
              int offset = ( it->set * 3 ) + 2;

              // Can occur with swatch rendering.
              if (offset > normalCount)
              {
                  if (normalCount >= 3)
                      offset = 2;
                  else
                      offset = -1;
              }

              //printf("VTX_ATTRIB : Access binormals via collection id(%d) = offset %d\n",
                //  it->set, offset );

              if ((offset > 0) && normalArrays[offset] )
              {
                  //there is a binormal
                  glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[offset]);
                  glEnableVertexAttribArray( it->handle);
              }
              else
              {
                  //there is no binormal available
                  glVertexAttrib2f( it->handle, 0.0f, 0.0f);
                  glDisableVertexAttribArray( it->handle);
              }
          }
          else
          {
            //there is no binormal available
            glVertexAttrib2f( it->handle, 0.0f, 0.0f);
            glDisableVertexAttribArray( it->handle);
          }
          continue;
        }
        else if (it->mtype == kColorSet) {
          //this set has requested a color
          if (( it->set < colorCount ) && colorArrays[it->set]) {
            //there is a color
            glVertexAttribPointer( it->handle, 4, GL_FLOAT, GL_FALSE, 0, colorArrays[it->set]);
            glEnableClientState( it->handle );
          }
          else {
            //there is no color available
            glVertexAttrib4f( it->handle, 0.0f, 0.0f, 0.0f, 1.0f);
            glDisableVertexAttribArray( it->handle);
          }
          continue;
        }

        //now deal with regular sets
        if (( it->set < texCoordCount ) && texCoordArrays[it->set]) {
          //this set exists
          glVertexAttribPointer( it->handle, 2, GL_FLOAT, GL_FALSE, 0, texCoordArrays[it->set]);
          glEnableVertexAttribArray( it->handle);
        }
        else {
          //the set does not exist
          glVertexAttrib2f( it->handle, 0.0f, 0.0f);
          glDisableVertexAttribArray( it->handle);
        }
      }

      m_glState.activeTexture( 0);
    }

    //
    //
    //
    MStatus glslShaderNode::glGeometry( const MDagPath& shapePath, 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 (!(glShadingLanguageSupport && glMultiTextureSupport && glTexture3DSupport && glAsmProgramSupport))
        return MStatus::kFailure;

      // If we're not using the default shader, setup any shape specific
      // shader parameters
      if( m_passCount > 0)
        updateBoundAttributes(shapePath);

      // this is inefficient for the moment
      int passCount = m_passCount >= 0 ? m_passCount : 1;
      for( int jj=0; jj < passCount; jj++)
      {
        // If this effect has more than 1 pass, bind the
        // shader per-pass. If it's a single pass effect,
        // the pass will have been bound in glBind()
        if( passCount > 1)
        {
            m_shader->setPass( jj);
            m_shader->bind();
        }

        // Setup any shape dependent uniforms (e.g. matrices)
        if( m_passCount > 0)
        {
            m_shader->setShapeDependentState();
        }
        else
        {
            sDefShader->setShapeDependentState();
        }

        //all of the following should come from shader attribute querying
        // might want to add VBO support for multiple pass shaders
        glVertexPointer( 3, GL_FLOAT, 0, vertexArray);

        if (normalCount && normalArrays[0] ) {
          glNormalPointer( GL_FLOAT, 0, &normalArrays[0][0]);
          glEnableClientState(GL_NORMAL_ARRAY);
        }
        else
        {
          glDisableClientState(GL_NORMAL_ARRAY);
        }

        configureTexCoords( normalCount, normalArrays, texCoordCount, texCoordArrays,
                            colorCount, colorArrays );

        GL_CHECK;

        //should probably switch to draw range elements
        glDrawElements( prim, indexCount, GL_UNSIGNED_INT, indexArray);

        // If this effect has more than 1 pass, unbind the
        // shader per-pass (if it's a single pass effect,
        // the effect will be bound once in glBind()
        if( m_passCount > 1)
        {
          m_shader->unbind();
        }

        GL_CHECK;
      }// for num passes

      if( m_passCount > 1)
        m_shader->setPass(0);

      return MS::kSuccess;
    }

    //
    // If the the attribute is a shader attribute
    // mark it dirty, and pass it to the parent
    MStatus
    glslShaderNode::connectionMade( const MPlug& plug,
                                   const MPlug& otherPlug,
                                   bool asSrc )
    {

        //need to grab the root in case we are dealing with a vector
        const MPlug &root = plug.isChild() ? plug.parent() : plug;

        //MGlobal::displayInfo(MString("Connection made: ") + root.name());

        //
        //  Is this time critical enough to use something like a map, instead or linearly searching all
        // attribute? Present expectation is for shaders to have significantly fewer than 100 attributes.
        {
            for ( std::vector<uniformAttrib>::iterator it = m_uniformAttribList.begin(); it != m_uniformAttribList.end(); it++) {
                if ( root == it->attrib) {
                    it->dirty = true;
                    //allow the set to proceed using the default method
                    break;
                }
            }
        }

        {
            for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it != m_samplerAttribList.end(); it++) {
                if ( root == it->attrib) {
                    it->dirty = true;
                    //allow the set to proceed using the default method
                    break;
                }
            }
        }

        // Pass to parent class. Must do this to ensure keyframe
        // animation works !
        return MS::kUnknownParameter;
    }

    //
    // If the the attribute is a shader attribute
    // mark it dirty, and pass it to the parent
    MStatus
    glslShaderNode::connectionBroken( const MPlug& plug,
                                     const MPlug& otherPlug,
                                     bool asSrc )
    {
        //need to grab the root in case we are dealing with a vector
        const MPlug &root = plug.isChild() ? plug.parent() : plug;

        //MGlobal::displayInfo(MString("Connection broken: ") + root.name());

        //
        //  Is this time critical enough to use something like a map, instead or linearly searching all
        // attribute? Present expectation is for shaders to have significantly fewer than 100 attributes.
        {
            for ( std::vector<uniformAttrib>::iterator it = m_uniformAttribList.begin(); it < m_uniformAttribList.end(); it++) {
                if ( root == it->attrib) {
                    it->dirty = true;
                    //allow the set to proceed using the default method
                    break;
                }
            }
        }

        {
            for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) {
                if ( root == it->attrib) {
                    it->dirty = true;
                    //allow the set to proceed using the default method
                    break;
                }
            }
        }

        // Pass to parent class. Must do this to ensure keyframe
        // animation works !
        return MS::kUnknownParameter;
    }

    //
    //
    //
    /* virtual */
    bool glslShaderNode::getInternalValueInContext( const MPlug &plug,
                                                   MDataHandle &handle,
                                                   MDGContext& )
    {
      bool retVal = false;

      if ( plug == sShader) {
        handle.set(m_shaderName);
        retVal = true;
      }
      else if ( plug == sTechnique) {
        handle.set(m_techniqueName);
        retVal = true;
      }
      else if ( plug == sTechniqueList )
      {
        handle.set(m_techniqueList);
        retVal = true;
      }
      else if ( plug == sShaderPath )
      {
        handle.set(m_shaderPath);
        retVal = true;
      }
      else {
        int ii;
        for (ii=0; ii<8; ii++) {
          if ( plug == sNakedTexCoords[ii]) {
            handle.set(m_nakedTexSets[ii]);
            retVal = true;
            break;
          }
        }

        for (ii=0; ii<2; ii++) {
          if (plug == sNakedColors[ii]) {
            handle.set(m_nakedColorSets[ii]);
            retVal = true;
            break;
          }
        }

        //handle the generic attribute sets
        for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
          if (plug == it->attrib) {
            handle.set(it->setName);
            retVal = true;
            break;
          }
        }
      }

      return retVal;
    }

    //
    //
    //
    /* virtual */
    bool glslShaderNode::setInternalValueInContext( const MPlug &plug,
                                                   const MDataHandle &handle,
                                                   MDGContext & )
    {
      bool retVal = false;

      //if reading file, short circuit things, we'll clean up post load
      bool reading = MFileIO::isReadingFile();
      if (reading)
          m_shader = NULL;

      //MGlobal::displayInfo(MString("Plug update: ") + plug.name());

      if ( plug == sShader) {
        m_shaderName = handle.asString();
        m_shaderPath = m_shaderName;
        if (locateFile( m_shaderName, m_shaderPath)) {
          // Set shader name to be full path so that we
          // can access a fully qualified name from the UI (e.g.
          // for reloading, viewing and editing the fx file.
          //
          m_shaderDirty = true;
          if (!reading) {

              if (MGlobal::mayaState() == MGlobal::kBatch)
                rebuildShader();
            else
            {
                MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
                if (pRenderer)
                {
                    const MString & backEndStr = pRenderer->backEndString();
                    MStatus status = pRenderer->makeResourceContextCurrent( backEndStr );
                    if (status != MStatus::kSuccess) {
                        MStatus strStat;
                        MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedResourceContext, strStat));
                        MGlobal::displayError(status.errorString());
                    }
                    else
                        rebuildShader();
                }
            }
          }
        }
        else {
          MGlobal::displayError(MString("Couldn't find shader file: '") + m_shaderPath + "'");
        }
        retVal = true;
      }
      else if ( plug == sTechnique) {
        MString temp = handle.asString();
        // We are in file read and m_shader has not been built yet,
        // so don't print out an error message here.
        //
        if (reading)
        {
            m_techniqueName = temp;
            retVal = true;
        }
        else
        {
        if (m_shader) {
          for (int ii=0; ii<m_shader->techniqueCount(); ii++) {
            if (temp == m_shader->techniqueName(ii)) {
              m_shader->setTechnique(ii);
              retVal = true;
              break;
            }
          }
        }

        if (retVal)
          m_techniqueName = temp;
        else
            {
                MGlobal::displayWarning(MString("Unknown technique: ") + temp +
                MString(" for shader: ") + m_shaderPath.asChar());
            }
        }
        retVal = true;
      }
      else if ( plug == sTechniqueList )
      {
          // Do nothing
      }
      else {

        int ii;

        //handle the naked texture coord sets
        for (ii=0; ii<8; ii++) {
          if (plug == sNakedTexCoords[ii]) {
            m_nakedTexSets[ii] = handle.asString();
            //MGlobal::displayInfo( MString("Setting Tex ") + ii + " to " + m_nakedTexSets[ii]);
            return true;
          }
        }

        for (ii=0; ii<2; ii++) {
          if (plug == sNakedColors[ii]) {
            m_nakedColorSets[ii] = handle.asString();
            //MGlobal::displayInfo( MString("Setting Color ") + ii + " to " + m_nakedColorSets[ii]);
            return true;
          }
        }

        //handle the generic attribute sets
        for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
          if (plug == it->attrib) {
            it->setName = handle.asString();
            return true;
          }
        }

        if (!reading) {
          //process dynamic attributes

          //need to grab the root in case we are dealing with a vector
          const MPlug &root = plug.isChild() ? plug.parent() : plug;

          //MGlobal::displayInfo(MString("Root name: ") + root.name());

          //
          //  Is this time critical enough to use something like a map, instead or linearly searching all
          // attribute? Present expectation is for shaders to have significantly fewer than 100 attributes.
          for ( std::vector<uniformAttrib>::iterator it2 = m_uniformAttribList.begin(); it2 < m_uniformAttribList.end(); it2++) {
            if ( ( root == it2->attrib) || ( root == it2->attrib2) ){
                it2->dirty = true;
              //allow the set to proceed using the default method
              break;
            }
          }

          for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) {
            if (root == it->attrib) {
              it->dirty = true;
              //allow the set to proceed using the default method
              break;
            }
            if (root == it->anisoAttrib) {
              it->dirty = true;
              it->maxAniso = handle.asFloat();

              //allow the set to proceed using the default method
              break;
            }
            if (root == it->magFilterAttrib) {
              it->dirty = true;
              switch (handle.asShort()) {
                case 0:
                  it->magFilter = GL_NEAREST;
                  break;
                case 1:
                  it->magFilter = GL_LINEAR;
                  break;
                default:
                  //this should be impossible
                  it->magFilter = GL_LINEAR;
                  break;
              };

              //allow the set to proceed using the default method
              break;
            }
            if (root == it->minFilterAttrib) {
              it->dirty = true;
              switch (handle.asShort()) {
                case 0:
                  it->minFilter = GL_NEAREST;
                  break;
                case 1:
                  it->minFilter = GL_LINEAR;
                  break;
                case 2:
                  it->minFilter = GL_LINEAR_MIPMAP_NEAREST;
                  break;
                case 3:
                  it->minFilter = GL_LINEAR_MIPMAP_LINEAR;
                  break;
                default:
                  //this should be impossible
                  it->minFilter = GL_LINEAR;
                  break;
              };

              //allow the set to proceed using the default method
              break;
            }
            if (root == it->wrapAttrib) {
              it->dirty = true;
              switch (handle.asShort()) {
                case 0:
                  it->wrapMode = GL_REPEAT;
                  break;
                case 1:
                  it->wrapMode = GL_CLAMP_TO_EDGE;
                  break;
                case 2:
                  it->wrapMode = GL_MIRRORED_REPEAT;
                  break;
                default:
                  //this should be impossible
                  it->wrapMode = GL_REPEAT;
                  break;
              };

              //allow the set to proceed using the default method
              break;
            }
          }
        }
      }

      GL_CHECK;

      return retVal;
    }

    void glslShaderNode::copyInternalData( MPxNode* pMPx )
    {
        glslShaderNode* pNode = dynamic_cast<glslShaderNode*>( pMPx );

        m_shader = NULL;
        m_shaderDirty = false;

        // These get rebuilt
        m_uniformAttribList.clear();
        m_samplerAttribList.clear();
        m_attributeAttribList.clear();
        m_boundUniformList.clear();

        // Reset temporaries
        m_parsedUserAttribList = false;
        m_numUVsets = 0;
        m_uvSetNames.clear();
        m_numColorSets = 0;
        m_colorSetNames.clear();
        m_numNormals = 3;
        m_maxSetNum = 0;

        if( pNode )
        {
            m_maxSetNum = pNode->m_maxSetNum;

            for(int ii=0; ii<8; ii++)
            {
                m_nakedTexSets[ii] = pNode->m_nakedTexSets[ii];
                m_setNums[ii] = pNode->m_setNums[ii];
                m_mayaType[ii] = pNode->m_mayaType[ii];
            }

            m_nakedColorSets[0] = pNode->m_nakedColorSets[0];
            m_nakedColorSets[1] = pNode->m_nakedColorSets[1];

            m_colorSetNums[0] = pNode->m_colorSetNums[0];
            m_colorSetNums[1] = pNode->m_colorSetNums[1];

            m_colorMayaType[0] = pNode->m_colorMayaType[0];
            m_colorMayaType[1] = pNode->m_colorMayaType[1];

            m_shaderName = pNode->m_shaderName;
            m_shaderPath = m_shaderName;
            m_techniqueList = "";
            m_techniqueName = "";

            if (locateFile( m_shaderName, m_shaderPath))
            {
                // Must be done before rebuild to set the correct technique
                m_techniqueName = pNode->m_techniqueName;
                m_shaderDirty = true;

                MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
                if (pRenderer)
                {
                    const MString & backEndStr = pRenderer->backEndString();
                    MStatus status = pRenderer->makeResourceContextCurrent( backEndStr );
                    if (status != MStatus::kSuccess) {
                        MStatus strStat;
                        MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedResourceContext, strStat));
                        MGlobal::displayError(status.errorString());
                    }
                    else
                        rebuildShader();
                }
                else
                    rebuildShader();
            }
        }
    }

    //
    //attribute request interface
    //
    int glslShaderNode::colorsPerVertex() {
      return 1;
    }

    //
    //
    //
    int glslShaderNode::texCoordsPerVertex() {
        return 0;
    }

    #if MAYA_API_VERSION >= 700
      //  There are two versions of parseUserAttributeList, so the plug-in
      // can work on multiple revisions of Maya. The version for 700 and
      // above relies on the presence of the named color sets, and a fix
      // to a bug in the Maya API.

    //
    //
    //
    void glslShaderNode::parseUserAttributeList()
    {
      if (m_parsedUserAttribList)
        return;

      // Reset shared data which is used by getColorSetNames() and
      // getTexCoordSetNames
      //
      m_numUVsets = 0;
      m_numColorSets = 0;
      m_uvSetNames.clear();
      m_colorSetNames.clear();

      m_numNormals = 3;

      MStringArray uvSetNames;
      MStringArray colorSetNames;

      bool haveNurbs = false;
      // "Magic" name for nurbs uvs
      MString nurbsDefaultUV("Nurbs Tri UVs");
      const MDagPath & path = currentPath();
      if (path.hasFn( MFn::kMesh ) )
      {

        // Check the # of uvsets
        MFnMesh fnMesh( path.node() );
        if (fnMesh.numUVSets())
          fnMesh.getUVSetNames(uvSetNames);

        // Check the # of color sets
        if (fnMesh.numColorSets())
          fnMesh.getColorSetNames(colorSetNames);
      }
      else // is a NURBS
      {
        haveNurbs = true;
        uvSetNames.append(nurbsDefaultUV);

        // There is no such thing as color sets no nurbs
        // for now, so don't add any "default"
      }

      //
      // The code here needs to work as follows:
      //  1. Add nurbs uv set if it is a nurbs.
      //  2. Iterate the list of requested maps
      //  3. Check for special maps (tangent/binormal)
      //  4. If the map is not yet added, add it
      //  5. Record the index

      int ii = 0;
      for (; ii<8; ii++)
      {
        m_setNums[ii] = -1;
        m_mayaType[ii] = kNonMaya;
      }

      // Fixed splitting character to find qualifiers for inputs.
      // For now only used to scope the tangent, and binormal to
      // a given tangent or binormal set.
      //
      const char _splitChar = ':';

      ii = 0;
      for (; ii<8; ii++)
      {
          MStringArray splitStrings;
          MString comparisonString = m_nakedTexSets[ii];
          if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) &&
               splitStrings.length() > 1)
          {
              comparisonString = splitStrings[0];
          }

          // Special case nurbs. If "map1" is specified then
          // append the "special" uv set name for nurbs instead
          if (haveNurbs && comparisonString == "map1")
          {
              m_setNums[ii] = m_uvSetNames.length();
              m_uvSetNames.append(nurbsDefaultUV);
              m_mayaType[ii] = kUvSet;
              continue;
          }
          else if (comparisonString == "normal") {
              m_setNums[ii] = 0; // Normal is currently always for set 0
              m_mayaType[ii] = kNormal;
              continue;
          }
          else if (comparisonString == "tangent") {
              m_setNums[ii] = 0; // Tangent is currently always for set 0
              m_mayaType[ii] = kTangent;
              continue;
          }
          // Note that for NURBS there is currently an API bug
          // in that you cannot get the binormals even when you
          // ask for them.
          else if (comparisonString == "binormal") {
              m_setNums[ii] = 0; // Binormal is currently always for set 0
              m_mayaType[ii] = kBinormal;
              continue;
          }
          else if (comparisonString == "") {
              m_setNums[ii] = -1;
              m_mayaType[ii] = kNonMaya;
              continue;
          }
          else {
              // Test out uv set name matches first
              bool matchUVSet = false;
              unsigned int numNames = uvSetNames.length();
              if (numNames)
              {
                  unsigned int jj = 0;
                  for (jj=0; jj<numNames; jj++) {
                      if (comparisonString == uvSetNames[jj])
                      {
                          // Track name, number and type
                          m_setNums[ii] = m_uvSetNames.length();
                          m_uvSetNames.append( uvSetNames[jj] );
                          m_mayaType[ii] = kUvSet;
                          matchUVSet = true;
                          break;
                      }
                  }
              }

              // Test out color set name matches second
              if (!matchUVSet)
              {
                  numNames = colorSetNames.length();
                  if (numNames)
                  {
                      unsigned int jj = 0;
                      for (jj=0; jj<numNames ; jj++) {
                          if (comparisonString == colorSetNames[jj])
                          {
                              // Track name, number and type
                              m_setNums[ii] = m_colorSetNames.length();
                              m_colorSetNames.append( colorSetNames[jj] );
                              m_mayaType[ii] = kColorSet;
                              break;
                          }
                      }
                  }
              }
          }
      }

      //now handle the colors
      //
      // Colors need to be either 3 or 4 components, so they do not
      // draw from the texture coord sets.
      ii = 0;
      for (; ii<2; ii++)
      {
          m_colorSetNums[ii] = -1;
          m_colorMayaType[ii] = kNonMaya;
      }
      ii = 0;
      for (; ii<2; ii++)
      {
          MStringArray splitStrings;
          MString comparisonString = m_nakedColorSets[ii];
          if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) &&
              splitStrings.length() > 1)
          {
              comparisonString = splitStrings[0];
          }

          if (comparisonString == "normal") {
              m_colorSetNums[ii] = 0; // Normal is currently always for set 0
              m_colorMayaType[ii] = kNormal;
              continue;
          }
          else if (comparisonString == "tangent") {
              m_colorSetNums[ii] = 0; // Tangent is currently always for set 0
              m_colorMayaType[ii] = kTangent;
              continue;
          }
          // Note that for NURBS there is currently an API bug
          // in that you cannot get the binormals even when you
          // ask for them.
          else if (comparisonString == "binormal") {
              m_colorSetNums[ii] = 0; // Binormal is currently always for set 0
              m_colorMayaType[ii] = kBinormal;
              continue;
          }
          else if (comparisonString == "") {
              m_colorSetNums[ii] = -1;
              m_colorMayaType[ii] = kNonMaya;
              continue;
          }
          else {

              // Test out color set name matches first.
              // Basically the opposite of uv set lookup.
              //
              bool matchUVSet = false;

              // Test out color set name matches
              unsigned int numNames = colorSetNames.length();
              if (numNames)
              {
                  unsigned int jj = 0;
                  for (jj=0; jj<numNames ; jj++) {
                      if (comparisonString == colorSetNames[jj])
                      {
                          // Track name, number and type
                          m_colorSetNums[ii] = m_colorSetNames.length();
                          m_colorSetNames.append( colorSetNames[jj] );
                          m_colorMayaType[ii] = kColorSet;
                          matchUVSet = true;
                          break;
                      }
                  }
              }

    #ifdef _INTERACTIVE_SHADING_WORKS_FOR_ROUTING_TEXCOORDS_TO_COLOUR
              // Currently this crashes when not in hi-quality mode
              // so disable.
              numNames = uvSetNames.length();
              if (numNames)
              {
                  unsigned int jj = 0;
                  for (jj=0; jj<numNames; jj++) {
                      if (comparisonString == uvSetNames[jj])
                      {
                          // Track name, number and type
                          m_colorSetNums[ii] = m_uvSetNames.length();
                          m_uvSetNames.append( uvSetNames[jj] );
                          m_colorMayaType[ii] = kUvSet;
                          break;
                      }
                  }
              }
    #endif

          }
      }

      // now handle the generic attributes
      // unsigned int setNum = 0;
      for (std::vector<attributeAttrib>::iterator it = m_attributeAttribList.begin();
          it<m_attributeAttribList.end(); it++)
      {
          MStringArray splitStrings;
          MString comparisonString( it->setName );
          if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) &&
               splitStrings.length() > 1)
          {
              comparisonString = splitStrings[0];
          }

          if (comparisonString == "normal") {
              it->mtype = kNormal;
              it->set = 0;
              continue;
          }
          else if (comparisonString == "tangent") {
              it->mtype = kTangent;
              it->set = 0;
              continue;
          }
          else if (comparisonString == "binormal") {
              it->mtype = kBinormal;
              it->set = 0;
              continue;
          }
          else if (comparisonString == "") {
              it->set = -1;
              continue;
          }
          else {
              // Test out uv set name matches first
              bool matchUVSet = false;
              unsigned int numNames = uvSetNames.length();
              if (numNames)
              {
                  unsigned int jj = 0;
                  for (jj=0; jj<numNames; jj++)
                  {
                      // Track name, number and type
                      if (comparisonString == uvSetNames[jj])
                      {
                          it->set = m_uvSetNames.length();
                          m_uvSetNames.append( uvSetNames[jj] );
                          it->mtype = kUvSet;
                          matchUVSet = true;
                          break;
                      }
                  }
              }

              // Test out color set name matches second
              if (!matchUVSet)
              {
                  numNames = colorSetNames.length();
                  if (numNames)
                  {
                      unsigned int jj = 0;
                      for (jj=0; jj<numNames ; jj++) {
                          if (m_nakedTexSets[ii] == colorSetNames[jj])
                          {
                              // Track name, number and type
                              m_colorSetNames.append( colorSetNames[jj] );
                              it->set = m_colorSetNames.length();
                              it->mtype = kColorSet;
                              break;
                          }
                      }
                  }
              }
          }
      }

      // Do a post-scan to see if a specific uvset is specified for tangent
      // and binormal in the uv set entries.
      //
      //printf("----------------------- POST SCAN TEXCOORDS --------------------\n");
      unsigned int numUVSets = uvSetNames.length();
      if (numUVSets)
      {
          for (ii=0; ii<8; ii++)
          {

              if (m_mayaType[ii] == kTangent || m_mayaType[ii] == kBinormal)
              {
                  MStringArray splitStrings;
                  MString origString( m_nakedTexSets[ii] );

                  //printf("Attempt to split the TEXCOORD string[%d] = %s.\n",
                    //      ii, origString.asChar());

                  if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) &&
                      splitStrings.length() > 1)
                  {
                      // Look for a match of existing uvset names

                      //printf("--- Managed to split the string[%d]. Look for uvset %s\n",
                        //  ii, splitStrings[1].asChar());
                      unsigned int jj = 0;
                      for (jj=0; jj<numUVSets; jj++)
                      {
                          if (uvSetNames[jj] == splitStrings[1])
                          {
                              int foundSet = -1;
                              for (unsigned int kk=0; kk<m_uvSetNames.length(); kk++)
                              {
                                  if (splitStrings[1] == m_uvSetNames[kk])
                                  {
                                      foundSet = kk;
                                      break;
                                  }
                              }

                              if (foundSet > 0)
                              {
                                  // Set number is index into uvset name array.
                                  m_setNums[ii] = foundSet;
                              }
                              else
                              {
                                  // Append a new uvset name, and point to end of
                                  // uvset name array.
                                  m_setNums[ii] = m_uvSetNames.length();
                                  m_uvSetNames.append( splitStrings[1] );
                              }

                              //printf("---- Add uvset Index=%d. uvset name %s\n", m_setNums[ii],
                                //  splitStrings[1].asChar());

                              break;
                          }
                      }
                  }
              }
          }
      }

      // Do a post-scan to see if a specific uvset is specified for tangent
      // and binormal in the color set entries.
      //
    //  printf("----------------------- POST SCAN COLORS --------------------\n");
      if (numUVSets)
      {
          for (ii=0; ii<2; ii++)
          {
              if (m_colorMayaType[ii] == kTangent || m_colorMayaType[ii] == kBinormal)
              {
                  MStringArray splitStrings;
                  MString origString( m_nakedColorSets[ii] );

                  //printf("Attempt to split the COLOR string[%d] = %s.\n",
                    //      ii, origString.asChar());

                  if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) &&
                      splitStrings.length() > 1)
                  {
                      // Look for a match of existing uvset names
                      unsigned int jj = 0;
                      for (jj=0; jj<numUVSets; jj++)
                      {
                          if (uvSetNames[jj] == splitStrings[1])
                          {
                              int foundSet = -1;
                              for (unsigned int kk=0; kk<m_uvSetNames.length(); kk++)
                              {
                                  if (splitStrings[1] == m_uvSetNames[kk])
                                  {
                                      foundSet = kk;
                                      break;
                                  }
                              }

                              if (foundSet > 0)
                              {
                                  // Set number is index into uvset name array.
                                  m_colorSetNums[ii] = foundSet;
                              }
                              else
                              {
                                  // Append a new uvset name, and point to end of
                                  // uvset name array.
                                  m_colorSetNums[ii] = m_uvSetNames.length();
                                  m_uvSetNames.append( splitStrings[1] );
                              }

                              //printf("---- Add uvset Index=%d. uvset name %s\n", m_colorSetNums[ii],
                                //  splitStrings[1].asChar());

                              break;
                          }
                      }
                  }
              }
          }
      }

      // Do a post-scan to see if a specific uvset is specified for tangent
      // and binormal in the generic entries.
      //
      if (numUVSets)
      {
          for (std::vector<attributeAttrib>::iterator it2 = m_attributeAttribList.begin();
              it2<m_attributeAttribList.end(); it2++)
          {
              if (it2->mtype == kTangent || it2->mtype == kBinormal)
              {
                  MStringArray splitStrings;
                  MString origString( it2->setName );
                  if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) &&
                      splitStrings.length() > 1)
                  {
                      // Look for a match of existing uvset names
                      unsigned int jj = 0;
                      for (jj=0; jj<numUVSets; jj++)
                      {
                          if (uvSetNames[jj] == splitStrings[1])
                          {
                              int foundSet = -1;
                              for (unsigned int kk=0; kk<m_uvSetNames.length(); kk++)
                              {
                                  if (splitStrings[1] == m_uvSetNames[kk])
                                  {
                                      foundSet = kk;
                                      break;
                                  }
                              }

                              if (foundSet > 0)
                              {
                                  // Set number is index into uvset name array.
                                  it2->set = foundSet;
                              }
                              else
                              {
                                  // Append a new uvset name, and point to end of
                                  // uvset name array.
                                  it2->set = m_uvSetNames.length();
                                  m_uvSetNames.append( splitStrings[1] );
                              }

                              //printf("---- uvset Index=%d. uvset name %s\n", it->set,
                                //  splitStrings[1].asChar());

                              break;
                          }
                      }
                  }
              }
          }
      }

      m_numUVsets = m_uvSetNames.length();
      m_numColorSets = m_colorSetNames.length();

        m_maxSetNum = 0;
        for ( ii = 0; ii<8; ii++)
            if( m_mayaType[ii] != kNonMaya)
                m_maxSetNum = ii + 1;

      m_parsedUserAttribList = true;
    }

    #else // if MAYA_7

    //
    //
    //
    void glslShaderNode::parseUserAttributeList()
    {
      if (m_parsedUserAttribList)
        return;

      // Reset shared data which is used by getColorSetNames() and
      // getTexCoordSetNames
      //
      m_numUVsets = 0;
      m_numColorSets = 0;
      m_uvSetNames.clear();
      m_colorSetNames.clear();

      //
      //  Always requesting a default set of map1 first, works around
      // an annyoing bug seen in Maya 6.5.
      //
      m_uvSetNames.append("map1");

      int ii = 0;
      for (; ii<8; ii++)
      {
        m_setNums[ii] = -1;
        m_mayaType[ii] = kNonMaya;
      }
      ii = 0;
      for (; ii<8; ii++)
      {
        // Special case, grab the special set from set 0
        if ( m_nakedTexSets[ii] == "map1")
        {
          m_setNums[ii] = 0;
          m_mayaType[ii] = kUvSet;
          continue;
        }
        else if (m_nakedTexSets[ii] == "normal") {
          m_setNums[ii] = 0; // Normal is currently always for set 0
          m_mayaType[ii] = kNormal;
          continue;
        }
        else if (m_nakedTexSets[ii] == "tangent") {
          m_setNums[ii] = 0; // Tangent is currently always for set 0
          m_mayaType[ii] = kTangent;
          continue;
        }
        // Note that for NURBS there is currently an API bug
        // in that you cannot get the binormals even when you
        // ask for them.
        else if (m_nakedTexSets[ii] == "binormal") {
          m_setNums[ii] = 0; // Binormal is currently always for set 0
          m_mayaType[ii] = kBinormal;
          continue;
        }
        else if (m_nakedTexSets[ii] == "") {
          m_setNums[ii] = -1;
          m_mayaType[ii] = kNonMaya;
          continue;
        }
        else {
          //just append the set
          m_setNums[ii] = m_uvSetNames.length();
          m_uvSetNames.append( m_nakedTexSets[ii] );
          m_mayaType[ii] = kUvSet;
        }
      }

      //now handle the generic attributes
      unsigned int setNum = 0;
      for (std::vector<attributeAttrib>::iterator it = m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
        if (it->setName == "normal") {
          it->mtype = kNormal;
          it->set = 0;
          continue;
        }
        else if (it->setName == "tangent") {
          it->mtype = kTangent;
          it->set = 0;
          continue;
        }
        else if (it->setName == "binormal") {
          it->mtype = kBinormal;
          it->set = 0;
          continue;
        }
        else if (it->setName == "") {
          it->set = -1;
          continue;
        }
        else {
          //just append the set
          it->set = m_uvSetNames.length();
          m_uvSetNames.append( it->setName );
          it->mtype = kUvSet;

        }
      }

      m_numUVsets = m_uvSetNames.length();
      m_numColorSets = m_colorSetNames.length();
        m_maxSetNum = 0;
        for ( ii = 0; ii<8; ii++)
            if( m_mayaType[ii] != kNonMaya)
                m_maxSetNum = ii + 1;

      m_parsedUserAttribList = true;
    }

    #endif // if MAYA_7

    //
    //
    //
    int glslShaderNode::getColorSetNames(MStringArray& names)
    {
        if (!m_parsedUserAttribList)
            parseUserAttributeList();

        names = m_colorSetNames;
        return m_numColorSets;
    }

    //
    //
    //
    int glslShaderNode::getTexCoordSetNames( MStringArray &names)
    {
        if (!m_parsedUserAttribList)
            parseUserAttributeList();

        names = m_uvSetNames;
        return m_numUVsets;
    }

    //
    //
    //
    int glslShaderNode::normalsPerVertex() {

      // this can be revisited, now that we have dynamic attributes
      m_numNormals = 3;
      return m_numNormals;
    }

    //
    //
    //
    void glslShaderNode::configureUniformAttrib( MString &name, int pNum, shader::DataType type, shader::Semantic sm, float *defVal,
                                                MFnDependencyNode &dn, MDGModifier &mod) {
      uniformAttrib ua;
      MFnNumericAttribute nAttrib;
      MFnTypedAttribute tAttrib;
      MStatus stat;

      //set the generic attribute values
      ua.name = name;
      ua.pNum = pNum;
      ua.dirty = true;
      ua.type = type;
      ua.attrib = MObject::kNullObj;
      ua.attrib2 = MObject::kNullObj;

      // [ANIM_UNIFORM_FIX1] :
      // Note that all uniforms are not set as being internal as this
      // makes animation playback unreliable. [See related code for binding
      // unforms [ANIM_UNIFORM_FIX2]]

      //obtain relavent information
      float min=0.0f, max=0.0f;
      bool color = shader::isColor(sm);
      bool clamped = shader::isClamped(sm);
      shader::getLimits( sm, min, max);

      switch (ua.type) {

        case shader::dtFloat:
          if ( !dn.hasAttribute( name )) {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::kFloat);
            if (clamped) {
              nAttrib.setMin(min);
              nAttrib.setMax(max);
            }
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            if (defVal)
              nAttrib.setDefault( defVal[0]);
            else
              nAttrib.setDefault( -1.0 );
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtVec2:
          if ( !dn.hasAttribute( name )) {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::k2Float);
            if (clamped) {
              nAttrib.setMin( min, min);
              nAttrib.setMax( max, max);
            }
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            if (defVal)
              nAttrib.setDefault( defVal[0], defVal[1] );
            else
              nAttrib.setDefault( -1.0, -1.0 );
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtVec3:
          if ( !dn.hasAttribute( name )) {
            if (color) {
              ua.attrib = nAttrib.createColor( name, name);
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
              nAttrib.setInternal(true);
    #endif
              nAttrib.setKeyable( true );
              if (defVal)
                nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
            }
            else {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Float);
              if (clamped) {
                nAttrib.setMin( min, min, min);
                nAttrib.setMax( max, max, max);
              }
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            if (defVal)
              nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
            else
              nAttrib.setDefault( -1.0, -1.0, -1.0 );
            }
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtVec4:
          if ( !dn.hasAttribute( name )) {
            if (color) {
              ua.attrib = nAttrib.createColor( name, name);
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
              nAttrib.setInternal(true);
    #endif
              nAttrib.setKeyable( true );
              if (defVal)
                nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
              stat = mod.addAttribute( thisMObject(), ua.attrib );
              ua.attrib2 = nAttrib.create( name + "Alpha", name + "Alpha", MFnNumericData::kFloat);
              if (clamped) {
                nAttrib.setMin( 0.0f);
                nAttrib.setMax( 1.0f);
              }
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
              nAttrib.setInternal(true);
    #endif
              nAttrib.setKeyable( true );
              if (defVal)
                nAttrib.setDefault( defVal[3]);
              else
                nAttrib.setDefault( 1.0 );
              stat = mod.addAttribute( thisMObject(), ua.attrib2 );
            }
            else {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Float);
              if (clamped) {
                nAttrib.setMin( min, min, min);
                nAttrib.setMax( max, max, max);
              }
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            if (defVal)
              nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
            else
              nAttrib.setDefault( -1.0, -1.0, -1.0 );
            stat = mod.addAttribute( thisMObject(), ua.attrib );
            ua.attrib2 = nAttrib.create( name + "W", name + "W", MFnNumericData::kFloat);
              if (clamped) {
                nAttrib.setMin( min);
                nAttrib.setMax( max);
              }
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            if (defVal)
              nAttrib.setDefault( defVal[3]);
            else
              nAttrib.setDefault( -1.0 );
            stat = mod.addAttribute( thisMObject(), ua.attrib2 );
          }
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
            ua.attrib2 = dn.attribute( name + "W", &stat);
          }
          break;

        case shader::dtBool:
          if ( !dn.hasAttribute( name )) {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::kBoolean);
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            nAttrib.setDefault( true );  //Will this work?
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtInt:
          if ( !dn.hasAttribute( name )) {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::kInt);
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            if (defVal)
              nAttrib.setDefault( (int)defVal[0]);
            else
              nAttrib.setDefault( -1 );
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtIVec2:
          if ( !dn.hasAttribute( name )) {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::k2Int);
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            nAttrib.setDefault( -1, -1 );
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtIVec3:
          if ( !dn.hasAttribute( name )) {
            ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Int);
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            nAttrib.setInternal(true);
    #endif
            nAttrib.setKeyable( true );
            nAttrib.setDefault( -1, -1, -1 );
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtMat4:
          if ( !dn.hasAttribute( name )) {
            MFnMatrixAttribute mAttrib;
            ua.attrib = mAttrib.create( name, name, MFnMatrixAttribute::kFloat);
    #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
            mAttrib.setInternal(true);
    #endif
            mAttrib.setKeyable( true );
            //How do I set default????
            stat = mod.addAttribute( thisMObject(), ua.attrib );
          }
          else {
            //just grab it, need to make sure it is correct
            ua.attrib = dn.attribute(name, &stat);
          }
          break;

        case shader::dtBVec2:
        case shader::dtBVec3:
        case shader::dtIVec4:
        case shader::dtBVec4:
        case shader::dtMat2:
        case shader::dtMat3:
          //not yet supported, primarly due to no native Maya type
          return;
        default:
          MGlobal::displayWarning(MString("Unknown uniform parameter type for: ") + name);
      };
      if (stat != MStatus::kSuccess) {
        MStatus strStat;
        MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
        error.format(error, name);
        MGlobal::displayError(error);
        MGlobal::displayError(stat.errorString());
      }
      m_uniformAttribList.push_back(ua); //will this cause reference counting problems?
    }

    //
    //
    //
    void glslShaderNode::configureAttribs() {
      MDGModifier mod;

      {
          for (std::vector<uniformAttrib>::iterator it=m_uniformAttribList.begin(); it<m_uniformAttribList.end(); it++) {
              it->pNum = -1; //mark it as unused
          }
      }

      {
          for (std::vector<samplerAttrib>::iterator it=m_samplerAttribList.begin(); it<m_samplerAttribList.end(); it++) {
              it->pNum = -1; //mark it as unused
          }
      }

      {
          for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
              it->pNum = -1; //mark it as unused
          }
      }

      //no need to worry about reattachment with these, they don't create attributes
      m_boundUniformList.clear();

      MFnDependencyNode dn(thisMObject());

      int ii;

      if (m_shader && m_shader->valid()) {

        //find the uniforms that the new shader uses
        for (ii=0; ii<m_shader->uniformCount(); ii++) {
          MString name(m_shader->uniformName(ii));

          //determine if it is bound or not
          if ( shader::isBound(m_shader->uniformSemantic(ii))) {

            //handle bound uniforms

            //add a new one to the list
            boundUniform bu;
            bu.name = name;
            bu.usage = m_shader->uniformSemantic(ii);
            bu.pNum = ii;

            m_boundUniformList.push_back(bu);
          }
          else {
            //handle uniform attributes (tweakables)
            //iterate the list to find it
            std::vector<uniformAttrib>::iterator it=m_uniformAttribList.begin();

            //might want to store this stuff in a map instead
            while (it < m_uniformAttribList.end()) {
              if (it->name == name)
                break; //found it
              it++; // look at the next
            }

            //see if we found the correct attribute
            if (it !=m_uniformAttribList.end()) {
              // make sure we have the same type
              if ( it->type == m_shader->uniformType(ii)) {

                //record the handle, and mark it as dirty, so we update it
                it->pNum = ii;
                it->dirty = true;

                //process the next attribute
                continue;
              }
              else {
                //need to delete the old one here, otherwise we can't create the new one
                mod.removeAttribute( thisMObject(), it->attrib);
                it->attrib = MObject::kNullObj;

                //check to see if it was a vec4, and kill the last component
                if (it->attrib2 != MObject::kNullObj) {
                  mod.removeAttribute( thisMObject(), it->attrib2);
                  it->attrib2 = MObject::kNullObj;
                }
              }
            }

            //if there was no appropriate attribute create a new one and add it
            configureUniformAttrib( name, ii, m_shader->uniformType(ii), m_shader->uniformSemantic(ii),
                                    m_shader->uniformDefault(ii), dn, mod);

            GL_CHECK;
          }
        }

        //samplers
        for (ii=0; ii<m_shader->samplerCount(); ii++) {
          MString name(m_shader->samplerName(ii));

          //iterate the list to find it
          std::vector<samplerAttrib>::iterator it=m_samplerAttribList.begin();

          //might want to store this stuff in a map instead
          while (it < m_samplerAttribList.end()) {
            if (it->name == name)
              break; //found it

            it++; // look at the next
          }

          if (it !=m_samplerAttribList.end()) {
            //if found, set the pNum to point to this one
            it->pNum = ii;
            it->dirty = false;
            m_shader->updateSampler( ii, it->texName);
          }
          else {
            //if not found, create a new one and add it
            samplerAttrib sa;
            MFnNumericAttribute nAttrib;
            MFnEnumAttribute eAttrib;
            MStatus stat;

            sa.name = name;
            sa.pNum = ii;
            sa.dirty = true;
            sa.texName = 0;
            sa.minFilter = GL_LINEAR;
            sa.magFilter = GL_LINEAR;
            sa.wrapMode = GL_REPEAT;
            sa.maxAniso = 1.0f;

            if ( !dn.hasAttribute( name )) {
              sa.attrib = nAttrib.createColor( name, name);
              nAttrib.setInternal(true);
              stat = mod.addAttribute( thisMObject(), sa.attrib);
            }
            else {
              sa.attrib = dn.attribute(name, &stat);
            }
            if (stat != MStatus::kSuccess) {
                MStatus strStat;
                MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
                error.format(error, name);
                MGlobal::displayError(error);
                MGlobal::displayError(stat.errorString());
            }
            if ( !dn.hasAttribute( name +"MinFilter")) {
              sa.minFilterAttrib = eAttrib.create( name+"MinFilter", name+"MinFilter");
              eAttrib.addField( "Nearest", 0);
              eAttrib.addField( "Linear", 1);
              eAttrib.addField( "Linear, Mipmaped", 2);
              eAttrib.addField( "Trilinear", 3);
              eAttrib.setDefault( 1);
              eAttrib.setInternal(true);

              stat = mod.addAttribute( thisMObject(), sa.minFilterAttrib);
            }
            else {
              sa.minFilterAttrib = dn.attribute(name+"MinFilter", &stat);
              //need to query the filter attribute
              MPlug plug(thisMObject(), sa.minFilterAttrib);
              short val;
              plug.getValue(val);
              switch (val) {
                case 0:
                  sa.minFilter = GL_NEAREST;
                  break;
                case 1:
                  sa.minFilter = GL_LINEAR;
                  break;
                case 2:
                  sa.minFilter = GL_LINEAR_MIPMAP_NEAREST;
                  break;
                case 3:
                  sa.minFilter = GL_LINEAR_MIPMAP_LINEAR;
                  break;
                default:
                  //this should be impossible
                  sa.minFilter = GL_LINEAR;
                  break;
              };
            }
            if (stat != MStatus::kSuccess) {
                MStatus strStat;
                MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
                error.format(error, name + "MinFilter");
                MGlobal::displayError(error);
                MGlobal::displayError(stat.errorString());
            }
            if ( !dn.hasAttribute( name +"MagFilter")) {
              sa.magFilterAttrib = eAttrib.create( name+"MagFilter", name+"MagFilter");
              eAttrib.addField( "Nearest", 0);
              eAttrib.addField( "Linear", 1);
              eAttrib.setDefault( 1);
              eAttrib.setInternal(true);

              stat = mod.addAttribute( thisMObject(), sa.magFilterAttrib);
            }
            else {
              sa.magFilterAttrib = dn.attribute(name+"MagFilter", &stat);
              //need to query the filter attribute
              MPlug plug(thisMObject(), sa.magFilterAttrib);
              short val;
              plug.getValue(val);
              switch (val) {
                case 0:
                  sa.magFilter = GL_NEAREST;
                  break;
                case 1:
                  sa.magFilter = GL_LINEAR;
                  break;
                default:
                  //this should be impossible
                  sa.magFilter = GL_LINEAR;
                  break;
              };
            }
            if (stat != MStatus::kSuccess) {
                MStatus strStat;
                MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
                error.format(error, name + "MagFilter");
                MGlobal::displayError(error);
                MGlobal::displayError(stat.errorString());
            }
            if ( !dn.hasAttribute( name+"Wrap" )) {
              sa.wrapAttrib = eAttrib.create( name+"Wrap", name+"Wrap");
              eAttrib.addField( "Repeat", 0);
              eAttrib.addField( "Clamp", 1);
              eAttrib.addField( "Mirror", 2);
              eAttrib.setDefault( 0);
              eAttrib.setInternal(true);

              stat = mod.addAttribute( thisMObject(), sa.wrapAttrib);
            }
            else {
              sa.wrapAttrib = dn.attribute(name+"Wrap", &stat);
              //need to query the filter attribute
              MPlug plug(thisMObject(), sa.wrapAttrib);
              short val;
              plug.getValue(val);
              switch (val) {
                case 0:
                  sa.wrapMode = GL_REPEAT;
                  break;
                case 1:
                  sa.wrapMode = GL_CLAMP_TO_EDGE;
                  break;
                case 2:
                  sa.wrapMode = GL_MIRRORED_REPEAT;
                  break;
                default:
                  //this should be impossible
                  sa.wrapMode = GL_REPEAT;
                  break;
              };
            }
            if (stat != MStatus::kSuccess) {
                MStatus strStat;
                MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
                error.format(error, name + "Wrap");
                MGlobal::displayError(error);
                MGlobal::displayError(stat.errorString());
            }
            if ( !dn.hasAttribute( name+"MaxAniso" )) {
              sa.anisoAttrib = nAttrib.create( name+"MaxAniso", name+"MaxAniso", MFnNumericData::kFloat, 1.0);

              nAttrib.setInternal(true);
              nAttrib.setMin( 1.0);
              nAttrib.setMax( 16.0);
              stat = mod.addAttribute( thisMObject(), sa.anisoAttrib);
            }
            else {
              sa.anisoAttrib = dn.attribute(name+"MaxAniso", &stat);
              //need to query the filter attribute
              MPlug plug(thisMObject(), sa.anisoAttrib);
              float val;
              plug.getValue(val);
              sa.maxAniso = val;
            }
            if (stat != MStatus::kSuccess) {
                MStatus strStat;
                MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
                error.format(error, name + "MaxAniso");
                MGlobal::displayError(error);
                MGlobal::displayError(stat.errorString());
            }
            m_samplerAttribList.push_back(sa);
            GL_CHECK;
          }
        }

        //attributes
        for (ii=0; ii<m_shader->attributeCount(); ii++) {
          MString name(m_shader->attributeName(ii));

          //iterate the list to find it
          std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin();

          //might want to store this stuff in a map instead
          while (it < m_attributeAttribList.end()) {
            if (it->name == name)
              break; //found it

            it++; // look at the next
          }

          if (it !=m_attributeAttribList.end()) {
            //if found, set the pNum to point to this one
            it->pNum = ii;
            it->handle = m_shader->attributeHandle( ii);
            it->set = -1;
          }
          else {
            MFnTypedAttribute tAttrib;
            attributeAttrib aa;
            MStatus stat;

            aa.name = name;
            aa.setName = name;
            aa.pNum = ii;
            aa.handle = m_shader->attributeHandle(ii);
            aa.set = -1; //no set
            aa.mtype = kNonMaya;

            if ( !dn.hasAttribute( name )) {
              aa.attrib = tAttrib.create( name, name, MFnData::kString);

              tAttrib.setInternal(true);
              tAttrib.setKeyable( true );
              stat = mod.addAttribute( thisMObject(), aa.attrib);
            }
            else {
              aa.attrib = dn.attribute(name, &stat);
            }
            m_attributeAttribList.push_back(aa);
          }
        }
      }

      for (ii=0; ii< (int)m_uniformAttribList.size(); ) {
        if (m_uniformAttribList[ii].pNum == -1) {

          //attrib is unused, remove it
          if (m_uniformAttribList[ii].attrib != MObject::kNullObj ) {
            mod.removeAttribute( thisMObject(), m_uniformAttribList[ii].attrib);
            m_uniformAttribList[ii].attrib = MObject::kNullObj;
          }

          if ( m_uniformAttribList[ii].attrib2 != MObject::kNullObj ) {
            mod.removeAttribute( thisMObject(), m_uniformAttribList[ii].attrib2);
            m_uniformAttribList[ii].attrib2 = MObject::kNullObj;
          }

          m_uniformAttribList.erase(m_uniformAttribList.begin()+ii);
        }
        else {
          //move to the next item
          ii++;
        }
      }

      for (ii=0; ii<(int)m_samplerAttribList.size(); ) {
        MStatus stat, stat2, stat3, stat4, stat5;
        if (m_samplerAttribList[ii].pNum == -1) {
          //attrib is unused, disconnect it and remove it

          if ( m_samplerAttribList[ii].attrib != MObject::kNullObj )
            stat = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].attrib);

          if ( m_samplerAttribList[ii].magFilterAttrib != MObject::kNullObj )
            stat2 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].magFilterAttrib);

          if ( m_samplerAttribList[ii].minFilterAttrib != MObject::kNullObj )
            stat3 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].minFilterAttrib);

          if ( m_samplerAttribList[ii].wrapAttrib != MObject::kNullObj )
            stat4 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].wrapAttrib);

          if ( m_samplerAttribList[ii].anisoAttrib != MObject::kNullObj )
            stat5 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].anisoAttrib);

          if (stat != MStatus::kSuccess) {
                MStatus strStat;
                MString error = MStringResource::getString(rGLSLShaderNodeFailedDestroyAttribute, strStat);
                error.format(error, m_samplerAttribList[ii].name);
                MGlobal::displayError(error);
                MGlobal::displayError(MString("  ") + stat.errorString());
          }

          //I don't think we have a context for certain here
          ResourceManager::destroyTexture( m_samplerAttribList[ii].texName);

          m_samplerAttribList[ii].attrib = MObject::kNullObj;
          m_samplerAttribList.erase(m_samplerAttribList.begin()+ii);
        }
        else {
          //move to the next item
          ii++;
        }
      }

      for (ii=0; ii<(int)m_attributeAttribList.size(); ) {
        MStatus stat;
        if (m_attributeAttribList[ii].pNum == -1) {
          //attrib is unused, disconnect it and remove it

          stat = mod.removeAttribute( thisMObject(), m_attributeAttribList[ii].attrib);
          if (stat != MStatus::kSuccess) {
                MStatus strStat;
                MString error = MStringResource::getString(rGLSLShaderNodeFailedDestroyAttribute, strStat);
                error.format(error, m_attributeAttribList[ii].name);
                MGlobal::displayError(MString("  ") + stat.errorString());
          }

          m_attributeAttribList[ii].attrib = MObject::kNullObj;
          m_attributeAttribList.erase(m_attributeAttribList.begin()+ii);
        }
        else {
          //move to the next item
          ii++;
        }
      }

      GL_CHECK;

      mod.doIt();
    }

    //
    //
    //
    bool glslShaderNode::locateFile( const MString &name, MString &path) {
      MString proj;
      struct stat fstats;
      MString temp;

      //
      // I swear, there must be a better way
      MGlobal::executeCommand( MString("workspace -q -rd;"), proj);

      //search order:
      // current directory
      // project/shaders directory
      // project directory

      // Try absolute path first
      if ( stat(name.asChar(), &fstats) == 0){
        //found it in the current path
        path = name;
        return true;
      }

      // Get the current working directory and
      // try relative path
      char buffer[MAX_PATH_LENGTH];
      if ( getcwd( buffer, MAX_PATH_LENGTH ) != NULL)
      {
          temp = MString(buffer) + "\\" + name;
          if ( stat(temp.asChar(), &fstats) == 0){
              //found it in the current path
              path = temp;
              return true;
          }
      }

      temp = proj + "shaders/" + name;
      if ( stat(temp.asChar(), &fstats) == 0){
        //found it in the project/shaders path
        path = temp;
        return true;
      }

      temp = proj + name;
      if ( stat(temp.asChar(), &fstats) == 0){
        //found it in the project path
        path = temp;
        return true;
      }

      return false;
    }

    // Swatch rendering

    /* virtual */
    MStatus glslShaderNode::renderSwatchImage( MImage & outImage )
    {
        MStatus status = MStatus::kFailure;

        // Get the hardware renderer utility class
        MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
        if (pRenderer)
        {
            const MString& backEndStr = pRenderer->backEndString();

            // Get geometry
            // ============
            unsigned int* pIndexing = 0;
            unsigned int  numberOfData = 0;
            unsigned int  indexCount = 0;

            MHardwareRenderer::GeometricShape gshape = MHardwareRenderer::kDefaultSphere;
            MGeometryData* pGeomData =
                pRenderer->referenceDefaultGeometry( gshape, numberOfData, pIndexing, indexCount );
            if( !pGeomData )
            {
                return MStatus::kFailure;
            }

            // Make the swatch context current
            // ===============================
            //
            unsigned int width, height;
            outImage.getSize( width, height );
            unsigned int origWidth = width;
            unsigned int origHeight = height;

            MStatus status2 = pRenderer->makeSwatchContextCurrent( backEndStr, width, height );

            if( status2 != MS::kSuccess )
            {
                pRenderer->dereferenceGeometry( pGeomData, numberOfData );
                return status2;
            }

            // Get the light direction from the API, and use it
            // =============================================
            {
                float light_pos[4];
                pRenderer->getSwatchLightDirection( light_pos[0], light_pos[1], light_pos[2], light_pos[3] );

                glMatrixMode(GL_MODELVIEW);
                glPushMatrix();
                glLoadIdentity();
                glLightfv(GL_LIGHT0, GL_POSITION, light_pos );
                glPopMatrix();

                float light_ambt[4] = {1.0f, 1.0f, 1.0f, 1.0};
                float light_diff[4] = {1.0f, 1.0f, 1.0f, 1.0};
                float light_spec[4] = {1.0f, 1.0f, 1.0f, 1.0};

                glLightfv( GL_LIGHT0, GL_AMBIENT,  light_ambt );
                glLightfv( GL_LIGHT0, GL_DIFFUSE,  light_diff );
                glLightfv( GL_LIGHT0, GL_SPECULAR, light_spec );

                glEnable( GL_LIGHTING );
                glEnable( GL_LIGHT0 );

                GL_CHECK;
            }

            // Get camera
            // ==========
            {
                // Get the camera frustum from the API
                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
                double l, r, b, t, n, f;
                pRenderer->getSwatchPerspectiveCameraSetting( l, r, b, t, n, f );
                glFrustum( l, r, b, t, n, f );

                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                float x, y, z, w;
                pRenderer->getSwatchPerspectiveCameraTranslation( x, y, z, w );
                glTranslatef( x, y, z );

                GL_CHECK;
            }

            // Get the default background color and clear the background
            //
            float r, g, b, a;
            MHWShaderSwatchGenerator::getSwatchBackgroundColor( r, g, b, a );
            glClearColor( r, g, b, a );
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            glShadeModel(GL_SMOOTH);
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LEQUAL);
            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

            GL_CHECK;

            // Draw The Swatch
            // ===============
            //drawTheSwatch( pGeomData, pIndexing, numberOfData, indexCount );
            MDagPath dummyPath;
            glBind( dummyPath );

            float *vertexData = (float *)( pGeomData[0].data() );
            float *normalData = (float *)( pGeomData[1].data() );
            float *uvData = (float *)( pGeomData[2].data() );
            float *tangentData = (float *)( pGeomData[3].data() );
            float *binormalData = (float *)( pGeomData[4].data() );
            unsigned int normalCount = 0;

            // Stick normal, tangent, binormals into ptr array
            float ** normalArrays = new float * [3];
            if (normalData)
            {
                normalArrays[0] = normalData;
                normalCount++;
            }
            else
                normalArrays[0] = NULL;
            if (tangentData)
            {
                normalArrays[1] = tangentData;
                normalCount++;
            }
            else
                normalArrays[1] = NULL;
            if (binormalData)
            {
                normalArrays[2] = binormalData;
                normalCount++;
            }
            else
                normalArrays[2] = NULL;

            // Stick uv data into ptr array
            unsigned int uvCount = 0;
            float ** texCoordArrays = new float * [1];
            if (uvData)
            {
                texCoordArrays[0] = uvData;
                uvCount = 1;
            }
            else
                texCoordArrays[0] = NULL;

            m_setNums[0] = 0;
            m_setNums[1] = 0;
            m_setNums[2] = 0;
            m_setNums[3] = 0;
            m_setNums[4] = 0;
            m_setNums[5] = 0;
            m_setNums[6] = 0;
            m_setNums[7] = 0;

            m_mayaType[0] = kUvSet;
            m_mayaType[1] = kTangent;
            m_mayaType[2] = kBinormal;
            m_mayaType[3] = kUvSet;
            m_mayaType[4] = kUvSet;
            m_mayaType[5] = kUvSet;
            m_mayaType[6] = kUvSet;
            m_mayaType[7] = kUvSet;

            // No colours
            m_colorSetNums[0] = -1;
            m_colorSetNums[1] = -1;
            m_colorMayaType[0] = kColorSet;
            m_colorMayaType[1] = kColorSet;

            glGeometry( dummyPath,
                        GL_TRIANGLES,
                        false,
                        indexCount,
                        pIndexing,
                        0,
                        NULL, /* no vertex ids */
                        vertexData,
                        normalCount,
                        (const float **) normalArrays,
                        0,
                        NULL, /* no colours */
                        uvCount,
                        (const float **) texCoordArrays);

            glUnbind( dummyPath );
            glFinish();

            normalArrays[0] = NULL;
            normalArrays[1] = NULL;
            delete[] normalArrays;
            texCoordArrays[0] = NULL;
            delete[] texCoordArrays;

            // Read pixels back from swatch context to MImage
            // ==============================================
            pRenderer->readSwatchContextPixels( backEndStr, outImage );

            // Double check the outing going image size as image resizing
            // was required to properly read from the swatch context
            outImage.getSize( width, height );
            if (width != origWidth || height != origHeight)
            {
                status = MStatus::kFailure;
            }
            else
            {
                status = MStatus::kSuccess;
            }
        }
        GL_CHECK;
        return status;
    }

    //
    //
    bool glslShaderNode::printVertexShader( int pass) {

      if ( m_shader) {
        const char *shader = m_shader->getVertexShader( pass);
        if (shader) {
          MGlobal::displayInfo(shader);
          return true;
        }
      }

      return false;
    }

    //
    //
    bool glslShaderNode::printPixelShader( int pass) {
      if ( m_shader) {
        const char *shader = m_shader->getPixelShader( pass);
        if (shader) {
          MGlobal::displayInfo(shader);
          return true;
        }
      }

      return false;
    }

    //
    //
    glslShaderNode* glslShaderNode::findNodeByName(const MString &name) {
      for (std::vector<glslShaderNode*>::iterator it = sNodeList.begin(); it < sNodeList.end(); it++) {
        if ( (*it)->name() == name)
          return *it;
      }

      return NULL;
    }