cgfxShaderNode.cpp

//
// Copyright (C) 2002-2004 NVIDIA
//
// File: cgfxShaderNode.cpp
//
// Dependency Graph Node: cgfxShader
//
// Author: Jim Atkinson
//
// Changes:
//  10/2003  Kurt Harriman - www.octopusgraphics.com +1-415-893-1023
//           - Multiple UV sets; user-specified texcoord assignment.
//           - "tcs/texCoordSource", a new static attribute, is a
//             string array of up to 32 elements.  Set it to specify
//             the source of each TEXCOORD vertex parameter as one of:
//             a UV set name; "tangent"; "binormal"; "normal"; an empty
//             string; or up to 4 float values "x y z w".  Default is
//             {"map1","tangent","binormal"}.
//           - "-mtc/maxTexCoords" flag of cgfxShader command returns an
//             upper bound on the number of texcoord inputs per vertex
//             (GL_MAX_TEXTURE_UNITS) that can be passed from Maya thru
//             OpenGL to vertex shaders on the current workstation.
//           - The MEL command `pluginInfo -q -version cgfxShader`
//             returns the plug-in version and cgfxShaderNode.cpp
//             compile date.
//           - Improved error handling.
//  12/2003  Kurt Harriman - www.octopusgraphics.com +1-415-893-1023
//           - To load or reload an effect, use the cgfxShader command
//             "-fx/fxFile <filename>" flag.  Setting the cgfxShader
//             node's "s/shader" attribute no longer loads the effect.
//           - To choose a technique, set the "t/technique"
//             attribute of the cgfxShader node.  The effect is not
//             reloaded.  There is no longer a message box requiring
//             the user to choose a technique when loading an effect.
//           - The techniques defined by the current effect are returned
//             by the cgfxShader command "-lt/-listTechniques" flag.
//           - Fixed incorrect transformation of direction/position
//             parameters to spaces other than world space.
//           - Dangling references to deleted dynamic attributes
//             caused exceptions in MObject destructor, terminating
//             the Maya process.  This has been fixed.
//           - Improved error handling.
//
//-
// ==========================================================================
// 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.
// ==========================================================================
//+
#ifndef CGFXSHADER_VERSION
#define CGFXSHADER_VERSION  "4.4"
#endif

#include "cgfxShaderNode.h"
#include "cgfxFindImage.h"

#include <maya/MDagPath.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MEventMessage.h>
#include <maya/MFloatVector.h>
#include <maya/MFnMesh.h>
#include <maya/MFnStringArrayData.h>
#include <maya/MFnStringData.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MGlobal.h>
#include <maya/MPlug.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MDGModifier.h>
#include <maya/MFileIO.h>
#include <maya/MNodeMessage.h>
#include <maya/MFileObject.h>

// Viewport 2.0 includes
#include <maya/MDrawContext.h>
#include <maya/MFloatMatrix.h>
#include <maya/MFloatVector.h>
#include <maya/MFloatPoint.h>
#include "cgfxPassStateSetter.h"

#undef ENABLE_TRACE_API_CALLS
#ifdef ENABLE_TRACE_API_CALLS
#define TRACE_API_CALLS(x) cerr << "cgfxShader: "<<(x)<<"\n"
#else
#define TRACE_API_CALLS(x)
#endif


#if defined(_SWATCH_RENDERING_SUPPORTED_)
    // For swatch rendering
    #include <maya/MHardwareRenderer.h>
    #include <maya/MGeometryData.h>
    #include <maya/MHWShaderSwatchGenerator.h>
#endif
    #include <maya/MImage.h>

#include "nv_dds.h"


#ifdef _WIN32
#else
#   include <sys/timeb.h>
#   include <string.h>
#
#   define stricmp strcasecmp
#   define strnicmp strncasecmp
#endif

#define GLOBJECT_BUFFER_OFFSET(i) ((char *)NULL + (i)) // For GLObject offsets

//
// Statics and globals...
//

PFNGLCLIENTACTIVETEXTUREARBPROC glStateCache::glClientActiveTexture = 0;
PFNGLVERTEXATTRIBPOINTERARBPROC glStateCache::glVertexAttribPointer = 0;
PFNGLENABLEVERTEXATTRIBARRAYARBPROC glStateCache::glEnableVertexAttribArray = 0;
PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glStateCache::glDisableVertexAttribArray = 0;
PFNGLVERTEXATTRIB4FARBPROC glStateCache::glVertexAttrib4f = 0;
PFNGLSECONDARYCOLORPOINTEREXTPROC glStateCache::glSecondaryColorPointer = 0;
PFNGLSECONDARYCOLOR3FEXTPROC glStateCache::glSecondaryColor3f = 0;
PFNGLMULTITEXCOORD4FARBPROC glStateCache::glMultiTexCoord4fARB = 0;

int glStateCache::sMaxTextureUnits = 0;

glStateCache::glStateCache()
{
    reset();
}


glStateCache glStateCache::gInstance;

void glStateCache::activeTexture( int i)
{
    if( i != fActiveTextureUnit)
    {
        fActiveTextureUnit = i;
        if( glStateCache::glClientActiveTexture)
            glStateCache::glClientActiveTexture( GL_TEXTURE0_ARB + i );
    }
}

void glStateCache::enableVertexAttrib( int i)
{
    if( !(fEnabledRegisters & (1 << (glRegister::kVertexAttrib + i))))
    {
        if( glStateCache::glEnableVertexAttribArray)
            glStateCache::glEnableVertexAttribArray( i);
        fEnabledRegisters |= (1 << (glRegister::kVertexAttrib + i));
    }
    fRequiredRegisters |= (1 << (glRegister::kVertexAttrib + i));
}


void glStateCache::flushState()
{
    // Work out which registers are enabled, but no longer required
    long redundantRegisters = fEnabledRegisters & ~fRequiredRegisters;
    //printf( "State requires %d, enabled %d, redundant %d\n", fRequiredRegisters, fEnabledRegisters, redundantRegisters);

    // Disable them
    if( redundantRegisters & (1 << glRegister::kPosition))
        glDisableClientState(GL_VERTEX_ARRAY);
    if( redundantRegisters & (1 << glRegister::kNormal))
        glDisableClientState(GL_NORMAL_ARRAY);
    if( redundantRegisters & (1 << glRegister::kColor))
        glDisableClientState(GL_COLOR_ARRAY);
    if( redundantRegisters & (1 << glRegister::kSecondaryColor))
        glDisableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
    for( int i = glRegister::kTexCoord; i <= glRegister::kLastTexCoord; i++)
    {
        if( redundantRegisters & (1 << i))
        {
            activeTexture( i - glRegister::kTexCoord);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
    }
    for( int i = glRegister::kVertexAttrib; i <= glRegister::kLastVertexAttrib; i++)
    {
        if( redundantRegisters & (1 << i))
        {
            if( glStateCache::glDisableVertexAttribArray)
                glStateCache::glDisableVertexAttribArray( i - glRegister::kVertexAttrib);
        }
    }
    fEnabledRegisters = fRequiredRegisters;
    fRequiredRegisters = 0;
}


// This typeid must be unique across the universe of Maya plug-ins.
//
// TODO: Get a unique ID from NVIDIA if they have them or from A|W
// if they do not.
//

#ifdef _WIN32
MTypeId     cgfxShaderNode::sId( 4084862000 );
#else
MTypeId     cgfxShaderNode::sId( 0xF37A0C30 );
#endif

CGcontext       cgfxShaderNode::sCgContext;

CGprofile       cgfxShaderNode::sCgBestVtxProfile;
CGprofile       cgfxShaderNode::sCgBestGeomProfile;
CGprofile       cgfxShaderNode::sCgBestFragProfile;

// Attribute declarations
//
MObject     cgfxShaderNode::sShader;
MObject     cgfxShaderNode::sTechnique;
MObject     cgfxShaderNode::sAttributeList;
MObject     cgfxShaderNode::sVertexAttributeList;
MObject     cgfxShaderNode::sVertexAttributeSource;
MObject     cgfxShaderNode::sTexCoordSource;
MObject     cgfxShaderNode::sColorSource;
MObject     cgfxShaderNode::sTexturesByName;


// Codes used in ftexCoordList array
enum ETexCoord
{
    etcNull      = -1,
    etcConstant  = -2,
    etcNormal    = -3,
    etcTangent   = -4,
    etcBinormal  = -5,
    etcDataSet   = -6,
};



//--------------------------------------------------------------------//
// Constructor:
//
cgfxShaderNode::cgfxShaderNode()
:   fEffect(0)
,   fAttrDefList(0)
,   fVertexAttributes( NULL)
#ifdef TEXTURES_BY_NAME
,   fTexturesByName( true )
#else
,   fTexturesByName( false )
#endif
,   fNormalsPerVertex( 3 )
,   fConstructed(false)
,   fErrorCount( 0 )
,   fErrorLimit( 8 )
,   fTechniqueHasBlending( false )
,   fShaderFxFile()
,   fShaderFxFileChanged( false )
{
    // Set texCoordSource attribute to its default value.
    MStringArray sa;
    sa.append( "map1" );
    sa.append( "tangent" );
    sa.append( "binormal" );
    MStringArray sa2;
    sa2.append( "colorSet1" );
    setDataSources( &sa, &sa2 );
}


// Post-constructor
void
cgfxShaderNode::postConstructor()
{
    fConstructed = true;               // ok to call MPxNode member functions
}                                      // cgfxShaderNode::postConstructor


// Destructor:
//
cgfxShaderNode::~cgfxShaderNode()
{
#ifdef KH_DEBUG
    MString ss = "  .. ~node ";
    if ( fConstructed )
    {
        MFnDependencyNode fnNode( thisMObject() );
        ss += fnNode.name();
    }
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif

    // Remove all the callbacks that we registered.
    MMessage::removeCallbacks( fCallbackIds );
    fCallbackIds.clear();

    if (fAttrDefList)
    {
        fAttrDefList->release();
        fAttrDefList = 0;
    }

    // Make sure that any loaded shader is unloaded and freed
    // before going away.
    //
    if (fEffect)
    {
        cgDestroyEffect(fEffect);
        fEffect = 0;
    }
}

//
//  Description:
//      This method computes the value of the given output plug based
//      on the values of the input attributes.
//
//  Arguments:
//      plug - the plug to compute
//      data - object that provides access to the attributes for this node
//
MStatus cgfxShaderNode::compute( const MPlug& plug, MDataBlock& data )
{
    MStatus returnStatus;

    // Compute a color, so that Hypershade swatches do not render black.
    if ((plug == outColor) || (plug.parent() == outColor))
    {
        MFloatVector color(.07f, .8f, .07f);
        MDataHandle outputHandle = data.outputValue( outColor );
        outputHandle.asFloatVector() = color;
        outputHandle.setClean();
        return MS::kSuccess;
    }

    return MS::kUnknownParameter;
}

// ========== cgfxShaderNode::creator ==========
//
//  Description:
//      this method exists to give Maya a way to create new objects
//      of this type.
//
//  Return Value:
//      a new object of this type.
//
/* static */
void* cgfxShaderNode::creator()
{
    return new cgfxShaderNode();
}


// ========== cgfxShaderNode::initialize ==========
//
//  Description:
//      This method is called to create and initialize all of the attributes
//      and attribute dependencies for this node type.  This is only called
//      once when the node type is registered with Maya.
//
//  Return Values:
//      MS::kSuccess
//      MS::kFailure
//
/* static */
MStatus
cgfxShaderNode::initialize()
{
    MStatus ms;

    try
    {
        initializeNodeAttrs();
    }
    catch ( cgfxShaderCommon::InternalError* e )   // internal error
    {
        size_t ee = (size_t)e;
        MString es = "cgfxShaderNode internal error ";
        es += (int)ee;
        MGlobal::displayError( es );
        ms = MS::kFailure;
    }
    catch ( ... )
    {
        MString es = "cgfxShaderNode internal error: Unhandled exception in initialize";
        MGlobal::displayError( es );
        ms = MS::kFailure;
    }

    return ms;
}


// Create all the attributes.
/* static */
void
cgfxShaderNode::initializeNodeAttrs()
{
    MFnTypedAttribute   typedAttr;
    MFnNumericAttribute numericAttr;
    MFnStringData       stringData;
    MFnStringArrayData  stringArrayData;
    MStatus             stat, stat2;

    // The shader attribute holds the name of the .fx file that defines
    // the shader
    //
    sShader = typedAttr.create("shader", "s", MFnData::kString, stringData.create(&stat2), &stat);
    M_CHECK( stat2 );
    M_CHECK( stat );

    // Attribute is keyable and will show up in the channel box
    //
    stat = typedAttr.setKeyable(true);
    M_CHECK( stat );

    // Mark it as internal so we can track changes to it and know when to
    // reload the .fx file
    //
    stat = typedAttr.setInternal(true);
    M_CHECK( stat );

    // Attribute affects VP2.0 viewport appearance
    //
    stat = typedAttr.setAffectsAppearance(true);
    M_CHECK( stat );

    // Add the attribute we have created to the node
    //
    stat = addAttribute(sShader);
    M_CHECK( stat );

    //
    // technique
    //

    sTechnique = typedAttr.create("technique", "t", MFnData::kString,
        stringData.create(&stat2), &stat);
    M_CHECK( stat2 );
    M_CHECK( stat );

    typedAttr.setInternal(true);
    typedAttr.setKeyable(true);
    typedAttr.setAffectsAppearance(true);

    stat = addAttribute(sTechnique);
    M_CHECK( stat );

    //
    // attributeList (uniform parameters)
    //

    sAttributeList = typedAttr.create("attributeList", "al", MFnData::kStringArray,
        stringArrayData.create(&stat2), &stat);
    M_CHECK( stat2 );
    M_CHECK( stat );

    // Attribute is NOT keyable and will NOT show up in the channel box
    //
    stat = typedAttr.setKeyable(false);
    M_CHECK( stat );

    // Attribute is NOT connectable
    //
    stat = typedAttr.setConnectable(false);
    M_CHECK( stat );

    // This attribute is an NOT an array.  (It is a single valued attribute
    // whose value is a single MStringArray object.)
    //
    stat = typedAttr.setArray(false);
    M_CHECK( stat );

    // Mark it as internal so we can track changes to it and know when to
    // reload the .fx file
    //
    stat = typedAttr.setInternal(true);
    M_CHECK( stat );

    // This attribute is a hidden.
    //
    stat = typedAttr.setHidden(true);
    M_CHECK( stat );

    // Attribute affects VP2.0 viewport appearance
    //
    stat = typedAttr.setAffectsAppearance(true);
    M_CHECK( stat );

    // Add the attribute we have created to the node
    //
    stat = addAttribute(sAttributeList);
    M_CHECK( stat );

    //
    // vertexAttributeList (varying parameters)
    //

    sVertexAttributeList = typedAttr.create("vertexAttributeList", "val", MFnData::kStringArray,
        stringArrayData.create(&stat2), &stat);
    M_CHECK( stat2 );
    M_CHECK( stat );

    // Attribute is NOT keyable and will NOT show up in the channel box
    //
    stat = typedAttr.setKeyable(false);
    M_CHECK( stat );

    // Attribute is NOT connectable
    //
    stat = typedAttr.setConnectable(false);
    M_CHECK( stat );

    // This attribute is an NOT an array.  (It is a single valued attribute
    // whose value is a single MStringArray object.)
    //
    stat = typedAttr.setArray(false);
    M_CHECK( stat );

    // Mark it as internal so we can track changes to it and know when to
    // reload the .fx file
    //
    stat = typedAttr.setInternal(true);
    M_CHECK( stat );

    // This attribute is a hidden.
    //
    stat = typedAttr.setHidden(true);
    M_CHECK( stat );

    // Attribute affects VP2.0 viewport appearance
    //
    stat = typedAttr.setAffectsAppearance(true);
    M_CHECK( stat );

    // Add the attribute we have created to the node
    //
    stat = addAttribute(sVertexAttributeList);
    M_CHECK( stat );

    //
    // vertexAttributeSource
    //
    sVertexAttributeSource = typedAttr.create( "vertexAttributeSource", "vas", MFnData::kStringArray,
        MObject::kNullObj, &stat );
    M_CHECK( stat );
    stat = typedAttr.setInternal( true );
    M_CHECK( stat );
    stat = typedAttr.setAffectsAppearance(true);
    M_CHECK( stat );
    stat = addAttribute( sVertexAttributeSource );
    M_CHECK( stat );


    //
    // texCoordSource
    //
    sTexCoordSource = typedAttr.create( "texCoordSource", "tcs", MFnData::kStringArray,
        MObject::kNullObj, &stat );
    M_CHECK( stat );
    stat = typedAttr.setInternal( true );
    M_CHECK( stat );
    stat = typedAttr.setAffectsAppearance(true);
    M_CHECK( stat );
    stat = addAttribute( sTexCoordSource );
    M_CHECK( stat );


    //
    // colorSource
    //
    sColorSource = typedAttr.create( "colorSource", "cs", MFnData::kStringArray,
        MObject::kNullObj, &stat );
    M_CHECK( stat );
    stat = typedAttr.setInternal( true );
    M_CHECK( stat );
    stat = typedAttr.setAffectsAppearance(true);
    M_CHECK( stat );
    stat = addAttribute( sColorSource );
    M_CHECK( stat );


    //
    // texturesByName
    //
    sTexturesByName = numericAttr.create( "texturesByName", "tbn", MFnNumericData::kBoolean,
        0, &stat );
    M_CHECK( stat );
    stat = numericAttr.setInternal(true);
    M_CHECK( stat );
    stat = typedAttr.setAffectsAppearance(true);
    M_CHECK( stat );
    // Hide this switch - TDs can recompile this to default to
    // different options, but we don't want to encourage users
    // to switch some shading nodes to use node textures, and
    // others named textures (and we definitely don't want to
    // try and handle converting configured shaders from one to
    // the other)
    //
    stat = numericAttr.setHidden(true);
    M_CHECK( stat );
    numericAttr.setKeyable(false);
    stat = addAttribute( sTexturesByName );
    M_CHECK( stat );

}                                      // cgfxShaderNode::initializeNodeAttrs


/* virtual */
void
cgfxShaderNode::copyInternalData( MPxNode* pSrc )
{
    const cgfxShaderNode& src = *(cgfxShaderNode*)pSrc;
    setTexturesByName( src.getTexturesByName() );
    setShaderFxFile( src.shaderFxFile() );
    setShaderFxFileChanged( true );
    setTechnique( src.getTechnique() );
    setDataSources( &src.getTexCoordSource(), &src.getColorSource() );

    // Rebuild the shader from the fx file.
    //
    MString fileName = cgfxFindFile(shaderFxFile());
    bool hasFile = (fileName.asChar() != NULL) && strcmp(fileName.asChar(), "");
    if ( hasFile )
    {
        // Create the effect for this node.
        //
        MStringArray fileOptions;
        cgfxGetFxIncludePath( fileName, fileOptions );
        const char *opts[_CGFX_PLUGIN_MAX_COMPILER_ARGS_];
        unsigned int numOpts = fileOptions.length();
        if (numOpts)
        {
            numOpts = (numOpts > _CGFX_PLUGIN_MAX_COMPILER_ARGS_) ? _CGFX_PLUGIN_MAX_COMPILER_ARGS_ : numOpts;
            for (unsigned int i=0; i<numOpts; i++)
                opts[i] = fileOptions[i].asChar();
            opts[numOpts] = NULL;
        }
        CGeffect cgEffect = cgCreateEffectFromFile(sCgContext, fileName.asChar(), opts);

        if (cgEffect)
        {
            cgfxAttrDefList* effectList = NULL;
            MStringArray attributeList;
            MDGModifier dagMod;

            // Update the node.
            //
            cgfxAttrDef::updateNode(cgEffect, this, &dagMod, effectList, attributeList);
            MStatus status = dagMod.doIt();
            assert(status == MS::kSuccess);

            setAttrDefList(effectList);
            setAttributeList(attributeList);
            setEffect(cgEffect);

            // The node now owns this list, and we don't need it anymore. So release
            if( effectList )
                effectList->release();
        }
    }
}
// cgfxShaderNode::copyInternalData

bool cgfxShaderNode::setInternalValueInContext( const MPlug& plug,
                                              const MDataHandle& handle,
                                              MDGContext&)
{
    bool retVal = true;

    try
    {
#ifdef KH_DEBUG
        MString ss = "  .. seti ";
        ss += plug.partialName( true, true, true, false, false, true );
        if (plug == sShader ||
            plug == sTechnique)
        {
            ss += " \"";
            ss += handle.asString();
            ss += "\"";
        }
        ss += "\n";
        ::OutputDebugString( ss.asChar() );
#endif
        if (plug == sShader)
        {
            setShaderFxFile(handle.asString());
        }
        else if (plug == sTechnique)
        {
            setTechnique(handle.asString());
        }
        else if (plug == sAttributeList)
        {
            MDataHandle nonConstHandle(handle);
            MObject saData = nonConstHandle.data();
            MFnStringArrayData fnSaData(saData);
            setAttributeList(fnSaData.array());
        }
        else if (plug == sVertexAttributeList)
        {
            MDataHandle nonConstHandle(handle);
            MObject saData = nonConstHandle.data();
            MFnStringArrayData fnSaData(saData);
            const MStringArray& attributeList = fnSaData.array();

            cgfxVertexAttribute* attributes = NULL;
            cgfxVertexAttribute** nextAttribute = &attributes;
            int numAttributes = attributeList.length() / 4;
            for( int i = 0; i < numAttributes; i++)
            {
                cgfxVertexAttribute* attribute = new cgfxVertexAttribute();
                attribute->fName = attributeList[ i * 4 + 0];
                attribute->fType = attributeList[ i * 4 + 1];
                attribute->fUIName = attributeList[ i * 4 + 2];
                attribute->fSemantic = attributeList[ i * 4 + 3];
                *nextAttribute = attribute;
                nextAttribute = &attribute->fNext;
            }
            setVertexAttributes( attributes);
        }
        else if ( plug == sVertexAttributeSource )
        {
            MDataHandle nonConstHandle( handle );
            MObject     saData = nonConstHandle.data();
            MFnStringArrayData fnSaData( saData );
            MStringArray values = fnSaData.array();
            setVertexAttributeSource( values);
        }
        else if ( plug == sTexCoordSource )
        {
            MDataHandle nonConstHandle( handle );
            MObject     saData = nonConstHandle.data();
            MFnStringArrayData fnSaData( saData );
            MStringArray values = fnSaData.array();
            setDataSources( &values, NULL );
        }
        else if ( plug == sColorSource )
        {
            MDataHandle nonConstHandle( handle );
            MObject     saData = nonConstHandle.data();
            MFnStringArrayData fnSaData( saData );
            MStringArray values = fnSaData.array();
            setDataSources( NULL, &values );
        }
        else if ( plug == sTexturesByName )
        {
            setTexturesByName( handle.asBool(), !MFileIO::isReadingFile());
        }
        else
        {
            retVal = MPxHwShaderNode::setInternalValue(plug, handle);
        }
    }
    catch ( cgfxShaderCommon::InternalError* e )
    {
        reportInternalError( __FILE__, (size_t)e );
        retVal = false;
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
        retVal = false;
    }

    return retVal;
}


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

    try
    {
#ifdef KH_DEBUG
        MString ss = "  .. geti ";
        ss += plug.partialName( true, true, true, false, false, true );
        if ( plug == sShader )
            ss += " \"" + fShaderFxFile + "\"";
        else if (plug == sTechnique)
            ss += " \"" + fTechnique + "\"";
        ss += "\n";
        ::OutputDebugString( ss.asChar() );
#endif
        if (plug == sShader)
        {
            handle.set(fShaderFxFile);
        }
        else if (plug == sTechnique)
        {
            handle.set(fTechnique);
        }
        else if (plug == sAttributeList)
        {
            MFnStringArrayData saData;
            handle.set(saData.create(fAttributeListArray));
        }
        else if (plug == sVertexAttributeList)
        {

            MStringArray attributeList;
            cgfxVertexAttribute* attribute = fVertexAttributes;
            while( attribute)
            {
                attributeList.append( attribute->fName);
                attributeList.append( attribute->fType);
                attributeList.append( attribute->fUIName);
                attributeList.append( attribute->fSemantic);
                attribute = attribute->fNext;
            }

            MFnStringArrayData saData;
            handle.set(saData.create( attributeList));
        }
        else if ( plug == sVertexAttributeSource )
        {
            MStringArray attributeSources;
            cgfxVertexAttribute* attribute = fVertexAttributes;
            while( attribute)
            {
                attributeSources.append( attribute->fSourceName);
                attribute = attribute->fNext;
            }

            MFnStringArrayData saData;
            handle.set( saData.create( attributeSources ) );
        }
        else if ( plug == sTexCoordSource )
        {
            MFnStringArrayData saData;
            handle.set( saData.create( fTexCoordSource ) );
        }
        else if ( plug == sColorSource )
        {
            MFnStringArrayData saData;
            handle.set( saData.create( fColorSource ) );
        }
        else if (plug == sTexturesByName)
        {
            handle.set(fTexturesByName);
        }
        else
        {
            retVal = MPxHwShaderNode::getInternalValue(plug, handle);
        }
    }
    catch ( cgfxShaderCommon::InternalError* e )
    {
        reportInternalError( __FILE__, (size_t)e );
        retVal = false;
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
        retVal = false;
    }

    return retVal;
}


static void checkGlErrors(const char* msg)
{
#if defined(CGFX_DEBUG)
#define MYERR(n)    case n: OutputDebugStrings("    ", #n); break

    GLenum err;
    bool errors = false;

    while ((err = glGetError()) != GL_NO_ERROR)
    {
        if (!errors)
        {
            // Print this the first time through the loop
            //
            OutputDebugStrings("OpenGl errors: ", msg);
        }

        errors = true;

        switch (err)
        {
            MYERR(GL_INVALID_ENUM);
            MYERR(GL_INVALID_VALUE);
            MYERR(GL_INVALID_OPERATION);
            MYERR(GL_STACK_OVERFLOW);
            MYERR(GL_STACK_UNDERFLOW);
            MYERR(GL_OUT_OF_MEMORY);
        default:
            {
                char tmp[32];
                sprintf(tmp, "%d", err);
                OutputDebugStrings("    GL Error #", tmp);
            }
        }
    }
#undef MYERR
#endif /* CGFX_DEBUG */
}


// Handle a change in a connected texture
//
void textureChangedCallback( MNodeMessage::AttributeMessage msg, MPlug & plug, MPlug & otherPlug, void* aDef)
{
    // Whenever there is a change in our texture's attributes (which
    // could also be texture node deleted or disconnected), remove
    // our callback to signify that this texture needs to be refreshed.
    // We don't release the GL texture here because there may not be
    // a valid GL context around when the DG is being updated. The
    // texture will get flushed at the next draw time when the bind
    // code determines there is a node but no callback.
    ((cgfxAttrDef*)aDef)->releaseCallback();
}


#if defined(_SWATCH_RENDERING_SUPPORTED_)
/* virtual */
MStatus cgfxShaderNode::renderSwatchImage( MImage & outImage )
{

    MStatus status = MStatus::kFailure;
    if( sCgContext == 0 )   return status;

    // 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;
        }

        glPushAttrib ( GL_ALL_ATTRIB_BITS );
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();

        // 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] );
        }

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

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

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

        // Stick uvs into ptr array
        int uvCount = fTexCoordSource.length();
        float ** texCoordArrays = uvCount ? new float * [ uvCount] : NULL;
        for( int uv = 0; uv < uvCount; uv++)
        {
            texCoordArrays[ uv] = uvData;
        }

        // Stick normal, tangent, binormals into ptr array
        int normalCount = uvCount > 0 ? uvCount : 1;
        float ** normalArrays = new float * [ fNormalsPerVertex * normalCount];
        for( int n = 0; n < normalCount; n++)
        {
            if( fNormalsPerVertex > 0)
            {
                normalArrays[ n * fNormalsPerVertex + 0] = normalData;
                if( fNormalsPerVertex > 1)
                {
                    normalArrays[ n * fNormalsPerVertex + 1] = tangentData;
                    if( fNormalsPerVertex > 2)
                    {
                        normalArrays[ n * fNormalsPerVertex + 2] = binormalData;
                    }
                }
            }
        }

        glGeometry( dummyPath,
                    GL_TRIANGLES,
                    false,
                    indexCount,
                    pIndexing,
                    pGeomData[0].elementCount(),
                    NULL, /* no vertex ids */
                    vertexData,
                    fNormalsPerVertex,
                    (const float **) normalArrays,
                    0,
                    NULL, /* no colours */
                    uvCount,
                    (const float **) texCoordArrays);


        glUnbind( dummyPath );

        if( normalArrays) delete[] normalArrays;
        if( texCoordArrays) 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;
        }

        //restore matrix and gl state
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
        glPopAttrib();
    }
    return status;
}
#endif

// Tell Maya that Cg effects can be batched
//
bool cgfxShaderNode::supportsBatching() const
{
    return true;
}


// Tell Maya to invert texture coordinates for this shader
// This function is only called in the old interface: glBind/glGeometry/glUnbind
bool cgfxShaderNode::invertTexCoords() const
{
    // since we have flipped the texture image in glBind(),  in GL texture coordinate system, we don't need to invert the texture coordinate
    return false;
}


// Try and create a missing effect (e.g. once a GL context is available)
//
bool cgfxShaderNode::createEffect()
{
    // Attempt to read the effect from the file. But only when it has
    // changed file name. In the case where the file cannot be found
    // we will not continuously search for the same file while refreshing.
    // The user will need to manually "refresh" the file name, or change
    // it to force a new attempt to load the file here.
    //
    bool rc = false;
    if (shaderFxFileChanged())
    {
        MString fileName = cgfxFindFile(shaderFxFile());

        if(fileName.asChar() != NULL && strcmp(fileName.asChar(), ""))
        {
            // Compile and create the effect.
            MStringArray fileOptions;
            cgfxGetFxIncludePath( fileName, fileOptions );
            const char *opts[_CGFX_PLUGIN_MAX_COMPILER_ARGS_];
            unsigned int numOpts = fileOptions.length();
            if (numOpts)
            {
                numOpts = (numOpts > _CGFX_PLUGIN_MAX_COMPILER_ARGS_) ? _CGFX_PLUGIN_MAX_COMPILER_ARGS_ : numOpts;
                for (unsigned int i=0; i<numOpts; i++)
                    opts[i] = fileOptions[i].asChar();
                opts[numOpts] = NULL;
            }
            CGeffect cgEffect = cgCreateEffectFromFile(sCgContext, fileName.asChar(), opts);

            if (cgEffect)
            {
                cgfxAttrDefList* effectList = NULL;
                MStringArray attributeList;
                MDGModifier dagMod;
                // updateNode does a fair amount of work.  It determines which
                // attributes need to be added and which need to be deleted and
                // fills in all the changes in the MDagModifier.  Then it builds
                // a new value for the attributeList attribute.  Finally, it
                // builds a new value for the attrDefList internal value.  All
                // these values are returned here where we can set them into the
                // node.
                cgfxAttrDef::updateNode(cgEffect, this, &dagMod, effectList, attributeList);
                MStatus status = dagMod.doIt();
                assert(status == MS::kSuccess);

                // Actually update the node.
                setAttrDefList(effectList);
                setAttributeList(attributeList);
                setEffect(cgEffect);
                setTechnique( fTechnique);
                rc = true;

                // The node now owns this, and we don't need it anymore, so release it
                if( effectList )
                    effectList->release();
            }
        }
        setShaderFxFileChanged( false );
    }
    return rc;
}


/* virtual */
MStatus cgfxShaderNode::glBind(const MDagPath& shapePath)
{
    // This is the routine where you would do all the expensive,
    // one-time kind of work.  Create vertex programs, load
    // textures, etc.
    //
    glStateCache::instance().reset();

    // Since we have no idea what the effect may set, we have
    // to push everything.

    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

    // In this case, we will evaluate all the attributes and store the
    // parameter values.  In theory, there could be multiple calls to
    // geometry in between single calls to bind and unbind.  Since we
    // only need to get the attribute values once per frame, get them
    // in bind.

    MStatus stat;

#ifdef KH_DEBUG
    MString ss = "  .. bind ";
    if ( this && fConstructed )
        ss += name();
    ss += " ";
    ss += request.multiPath().fullPathName();
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif

    try
    {
        // One-time OpenGL initialization...
        if ( glStateCache::sMaxTextureUnits <= 0 )
        {
            // Before this point, we never had a good OpenGL context.  Now
            // we can check for extensions and set up pointers to the
            // extension procs.
#ifdef _WIN32
#define RESOLVE_GL_EXTENSION( fn, ext) wglGetProcAddress( #fn #ext)
#elif defined LINUX
#define RESOLVE_GL_EXTENSION( fn, ext) &fn ## ext
#else
#define RESOLVE_GL_EXTENSION( fn, ext) &fn
#endif

            glStateCache::glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREARBPROC) RESOLVE_GL_EXTENSION( glClientActiveTexture, ARB);
            glStateCache::glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERARBPROC) RESOLVE_GL_EXTENSION( glVertexAttribPointer, ARB);
            glStateCache::glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) RESOLVE_GL_EXTENSION( glEnableVertexAttribArray, ARB);
            glStateCache::glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) RESOLVE_GL_EXTENSION( glDisableVertexAttribArray, ARB);
            glStateCache::glVertexAttrib4f = (PFNGLVERTEXATTRIB4FARBPROC) RESOLVE_GL_EXTENSION( glVertexAttrib4f, ARB);
            glStateCache::glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTEREXTPROC) RESOLVE_GL_EXTENSION( glSecondaryColorPointer, EXT);
            glStateCache::glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FEXTPROC) RESOLVE_GL_EXTENSION( glSecondaryColor3f, EXT);
            glStateCache::glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) RESOLVE_GL_EXTENSION( glMultiTexCoord4f, ARB);

            // Don't use GL_MAX_TEXTURE_UNITS as this does not provide a proper
            // count when the # of image or texcoord inputs differs
            // from the conventional (older) notion of texture unit.
            //
            // Instead take the minimum of GL_MAX_TEXTURE_COORDS_ARB and
            // GL_MAX_TEXUTRE_IMAGE_UNITS_ARB according to the
            // ARB_FRAGMENT_PROGRAM specification.
            //
            GLint tval;
            glGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &tval );
            GLint mic = 0;
            glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &mic );
            if (mic < tval)
                tval = mic;

            // Don't use this...
            //glGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &tval );
            glStateCache::sMaxTextureUnits = tval;
            if (!glStateCache::glClientActiveTexture || glStateCache::sMaxTextureUnits < 1)
                glStateCache::sMaxTextureUnits = 1;
            else if (glStateCache::sMaxTextureUnits > CGFXSHADERNODE_GL_TEXTURE_MAX)
                glStateCache::sMaxTextureUnits = CGFXSHADERNODE_GL_TEXTURE_MAX;
        }

        // Try and grab the first pass of our effect
        if( fNewEffect.fTechnique && fNewEffect.fTechnique->fPasses)
        {
            // Set up the uniform attribute values for the effect.
            bindAttrValues();

            // Set depth function properly in case we have multi-pass
            if (fTechniqueHasBlending)
                glPushAttrib( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
            glGetBooleanv( GL_DEPTH_TEST, &fDepthEnableState);
            glGetIntegerv( GL_DEPTH_FUNC, &fDepthFunc);
            glGetIntegerv( GL_BLEND_SRC, &fBlendSourceFactor);
            glGetIntegerv( GL_BLEND_DST, &fBlendDestFactor);
            glDepthFunc(GL_LEQUAL);
        }
        else
        {
            // There is no effect.  Either they never set one or the one provided
            // failed to compile.  Just use this default material which is sort
            // of a shiny salmon-pink color.  It looks like nothing that Maya
            // creates by default but still lets you see your geometry.
            //
            glPushAttrib( GL_LIGHTING);
            static float diffuse_color[4]  = {1.0, 0.5, 0.5, 1.0};
            static float specular_color[4] = {1.0, 1.0, 1.0, 1.0};

            glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
            glEnable(GL_COLOR_MATERIAL);
            glColor4fv(diffuse_color);

            // Set up the specular color
            //
            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular_color);

            // Set up a default shininess
            //
            glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0);
        }

        checkGlErrors("cgfxShaderNode::glBind");
    }
    catch ( cgfxShaderCommon::InternalError* e )
    {
        reportInternalError( __FILE__, (size_t)e );
        stat = MS::kFailure;
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
        stat = MS::kFailure;
    }

    return stat;
}                                      // cgfxShaderNode::bind


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

void cgfxShaderNode::bindAttrValues()
{
    if ( !fEffect || !fTechnique.length() )
        return;

    MStatus  status;
    MObject  oNode = thisMObject();

    // This method should NEVER access the shape. If you find yourself tempted to access
    // any data from the shape here (like the matrices), be strong and resist! Any shape
    // dependent data should be set in bindAttrViewValues instead!
    //

    for ( cgfxAttrDefList::iterator it( fAttrDefList ); it; ++it )
    {                                  // loop over fAttrDefList
        cgfxAttrDef* aDef = *it;

        try
        {

            switch (aDef->fType)
            {
            case cgfxAttrDef::kAttrTypeBool:
                {
                    bool tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetParameter1i(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeInt:
                {
                    int tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetParameter1i(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeFloat:
                {
                    float tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetParameter1f(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeString:
                {
                    MString tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetStringParameterValue(aDef->fParameterHandle, tmp.asChar());
                    break;
                }

            case cgfxAttrDef::kAttrTypeVector2:
                {
                    float tmp[2];
                    aDef->getValue(oNode, tmp[0], tmp[1]);
                    cgSetParameter2fv(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeVector3:
            case cgfxAttrDef::kAttrTypeColor3:
                {
                    float tmp[3];
                    aDef->getValue(oNode, tmp[0], tmp[1], tmp[2]);
                    cgSetParameter3fv(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeVector4:
            case cgfxAttrDef::kAttrTypeColor4:
                {
                    float tmp[4];
                    aDef->getValue(oNode, tmp[0], tmp[1], tmp[2], tmp[3]);
                    cgSetParameter4fv(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeWorldDir:
            case cgfxAttrDef::kAttrTypeWorldPos:
                {
                    // Read the value
                    float tmp[4];
                    if (aDef->fSize == 3)
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2]);
                        tmp[3] = 1.0;
                    }
                    else
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2], tmp[3]);
                    }

                    // Find the coordinate space, and whether it is a
                    // point or a vector
                    int base = cgfxAttrDef::kAttrTypeFirstPos;
                    if (aDef->fType <= cgfxAttrDef::kAttrTypeLastDir)
                        base = cgfxAttrDef::kAttrTypeFirstDir;
                    int space = aDef->fType - base;

                    // Compute the transform matrix
                    MMatrix mat;
                    switch (space)
                    {
                        /* case 0:  object space, handled in view dependent method */
                        case 1: /* world space  - do nothing, identity */ break;
                        /* case 2: eye space, unsupported yet */
                        /* case 3: clip space, unsupported yet */
                        /* case 4: screen space, unsupported yet */
                    }

                    if (base == cgfxAttrDef::kAttrTypeFirstPos)
                    {
                        MPoint point(tmp[0], tmp[1], tmp[2], tmp[3]);
                        point *= mat;
                        tmp[0] = (float)point.x;
                        tmp[1] = (float)point.y;
                        tmp[2] = (float)point.z;
                        tmp[3] = (float)point.w;
                    }
                    else
                    {
                        MVector vec(tmp[0], tmp[1], tmp[2]);
                        vec *= mat;
                        tmp[0] = (float)vec.x;
                        tmp[1] = (float)vec.y;
                        tmp[2] = (float)vec.z;
                        tmp[3] = 1.F;
                    }

                    cgSetParameterValuefr(aDef->fParameterHandle, aDef->fSize, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeMatrix:
                {
                    MMatrix tmp;
                    float tmp2[4][4];
                    aDef->getValue(oNode, tmp);

                    if (aDef->fInvertMatrix)
                    {
                        tmp = tmp.inverse();
                    }

                    if (!aDef->fTransposeMatrix)
                    {
                        tmp = tmp.transpose();
                    }

                    tmp.get(tmp2);
                    cgSetMatrixParameterfr(aDef->fParameterHandle, &tmp2[0][0]);
                    break;
                }

            case cgfxAttrDef::kAttrTypeColor1DTexture:
            case cgfxAttrDef::kAttrTypeColor2DTexture:
            case cgfxAttrDef::kAttrTypeColor3DTexture:
            case cgfxAttrDef::kAttrTypeColor2DRectTexture:
            case cgfxAttrDef::kAttrTypeNormalTexture:
            case cgfxAttrDef::kAttrTypeBumpTexture:
            case cgfxAttrDef::kAttrTypeCubeTexture:
            case cgfxAttrDef::kAttrTypeEnvTexture:
            case cgfxAttrDef::kAttrTypeNormalizationTexture:

                {
                    MString tmp;
                    MObject textureNode = MObject::kNullObj;

                    if( fTexturesByName)
                    {
                        aDef->getValue(oNode, tmp);
                    }
                    else
                    {
                        // If we have a fileTexture node connect, get the
                        // filename it is using
                        MPlug srcPlug;
                        aDef->getSource(oNode, srcPlug);
                        MObject srcNode = srcPlug.node();
                        if( srcNode != MObject::kNullObj)
                        {
                            MStatus rc;
                            MFnDependencyNode dgFn( srcNode);
                            MPlug filenamePlug = dgFn.findPlug( "fileTextureName", &rc);
                            if( rc == MStatus::kSuccess)
                            {
                                filenamePlug.getValue( tmp);
                                textureNode = filenamePlug.node(&rc);
                            }

                            // attach a monitor to this texture if we don't already have one
                            // Note that we don't need to worry about handling node destroyed
                            // or disconnected, as both of these will trigger attribute changed
                            // messages before going away, and we will deregister our callback
                            // in the handler!
                            if( aDef->fTextureMonitor == kNullCallback && textureNode != MObject::kNullObj)
                            {
                                // If we don't have a callback, this may mean our texture is dirty
                                // and needs to be re-loaded (because we can't actually delete the
                                // texture itself in the DG callback we need to wait until we
                                // know we have a GL context - like right here)
                                aDef->releaseTexture();
                                aDef->fTextureMonitor = MNodeMessage::addAttributeChangedCallback( textureNode, textureChangedCallback, aDef);
                            }
                        }
                    }

                    if (aDef->fTextureId == 0 || tmp != aDef->fStringDef)
                    {
                        // If the texture object already exists, reuse
                        // it.  Otherwise, we create a new one.
                        //
                        if (aDef->fTextureId == 0)
                        {
                            GLuint val;
                            glGenTextures(1, &val);
                            aDef->fTextureId = val;
                        }

                        aDef->fStringDef = tmp;

                        nv_dds::CDDSImage image;

                        MString path = cgfxFindFile(tmp);

                        // If that failed, try and resolve the texture path relative to the
                        // effect
                        //
                        if (path.asChar() == NULL || !strcmp(path.asChar(), ""))
                        {
                            MFileObject effectFile;
                            effectFile.setRawFullName( fShaderFxFile);
                            path = cgfxFindFile( effectFile.path() + tmp);
                        }


                        if (path.asChar() != NULL && strcmp(path.asChar(), ""))
                        {
                            switch (aDef->fType)
                            {
                            case cgfxAttrDef::kAttrTypeEnvTexture:
                            case cgfxAttrDef::kAttrTypeCubeTexture:
                            case cgfxAttrDef::kAttrTypeNormalizationTexture:
                                // we don't want to flip cube maps...
                                image.load(path.asChar(),false);
                                break;
                            default:
                                // we always flip the texture image. This makes the image be GL convention with left-bottom as the origin.
                                // when drawing, we use inverted or non-inverted uv sets based on the texture coordinate system.
                                // inverted for DX texture coordinate system, non-inverted for GL texture coordinate system
                                image.load(path.asChar());
                                break;
                            }
                        }

                        // Our common stand-in "texture"
                        // The code below creates a separate stand-in GL texture
                        // for every attribute without a value (rather than sharing
                        // the default across all node/attributes of a given type.
                        // This is done because the current design does not support
                        // GL texture id sharing across nodes/attributes AND because
                        // we want to avoid checking disk every frame for missing
                        // textures. Once this plugin is re-factored to support a
                        // shared texture cache, we should revisit this to share
                        // default textures too
                        //
                        static unsigned char whitePixel[ 4] = { 255, 255, 255, 255};

                        switch (aDef->fType)
                        {
                        case cgfxAttrDef::kAttrTypeColor1DTexture:
                            glBindTexture(GL_TEXTURE_1D,aDef->fTextureId);
                            if( image.is_valid())
                            {
                                // Load the image
                                glTexParameteri(GL_TEXTURE_1D, GL_GENERATE_MIPMAP_SGIS, image.get_num_mipmaps() == 0);
                                image.upload_texture1D();
                            }
                            else
                            {
                                // Create a dummy stand-in texture
                                glTexImage1D( GL_TEXTURE_1D, 0, GL_RGBA, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                            }
                            break;
                        case cgfxAttrDef::kAttrTypeColor2DTexture:
                        case cgfxAttrDef::kAttrTypeNormalTexture:
                        case cgfxAttrDef::kAttrTypeBumpTexture:
#if !defined(WIN32) && !defined(LINUX)
                        case cgfxAttrDef::kAttrTypeColor2DRectTexture:
#endif
                            glBindTexture(GL_TEXTURE_2D,aDef->fTextureId);
                            if( image.is_valid())
                            {
                                // Load the image
                                glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, image.get_num_mipmaps() == 0);
                                image.upload_texture2D();
                            }
                            else
                            {
                                // Try to use Maya's default file texture loading,
                                // if the DDS loader failed. For now all that
                                // we can support is 2D textures.
                                //
                                if (textureNode != MObject::kNullObj)
                                {
                                    MImage img;
                                    unsigned int width, height;

                                    if (MS::kSuccess == img.readFromTextureNode(textureNode))
                                    {
                                        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 (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 */);
                                                }
                                            }
                                            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, true);
                                            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels());
                                        }
                                        else
                                        {
                                            // Create a dummy stand-in texture
                                            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
                                                GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                                        }
                                    }
                                    else
                                    {
                                        // Create a dummy stand-in texture
                                        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
                                            GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                                    }
                                }
                                else
                                {
                                    // Create a dummy stand-in texture
                                    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
                                        GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                                }
                            }
                            break;
                        case cgfxAttrDef::kAttrTypeEnvTexture:
                        case cgfxAttrDef::kAttrTypeCubeTexture:
                        case cgfxAttrDef::kAttrTypeNormalizationTexture:
                            {
                                glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, aDef->fTextureId);
                                if( image.is_valid())
                                {
                                    GLenum target;
                                    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_GENERATE_MIPMAP_SGIS, image.get_num_mipmaps() == 0);
                                    // loop through cubemap faces and load them as 2D textures
                                    for (int n = 0; n < 6; ++n)
                                    {
                                        // specify cubemap face
                                        target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB+n;
                                        image.upload_texture2D(image.is_cubemap() ? n : 0, target);
                                    }
                                }
                                // else Add default environment map here if needed
                                break;
                            }
                        case cgfxAttrDef::kAttrTypeColor3DTexture:
                            glBindTexture(GL_TEXTURE_3D,aDef->fTextureId);
                            if( image.is_valid())
                            {
                            image.upload_texture3D();
                            }
                            break;
#if defined(WIN32) || defined(LINUX)
                            // No such thing as NV texture rectangle
                            // on Mac.
                        case cgfxAttrDef::kAttrTypeColor2DRectTexture:
                            glBindTexture(GL_TEXTURE_RECTANGLE_NV, aDef->fTextureId);
                            if( image.is_valid())
                            {
                                // Load the image
                                image.upload_textureRectangle();
                            }
                            else
                            {
                                // Create a dummy stand-in texture
                                glTexImage2D( GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                            }
                            break;
#endif
                        default:
                            assert(false);
                        }
                    }
                    checkGlErrors("After loading texture");
                    cgGLSetupSampler(aDef->fParameterHandle, aDef->fTextureId);
                    break;
                }
#ifdef _WIN32
            case cgfxAttrDef::kAttrTypeTime:
                {
                    int ival = timeGetTime() & 0xffffff;
                    float val = (float)ival * 0.001f;
                    cgSetParameter1f(aDef->fParameterHandle, val);
                    break;
                }
#endif
            case cgfxAttrDef::kAttrTypeOther:
            case cgfxAttrDef::kAttrTypeUnknown:
                break;

            case cgfxAttrDef::kAttrTypeObjectDir:
            case cgfxAttrDef::kAttrTypeViewDir:
            case cgfxAttrDef::kAttrTypeProjectionDir:
            case cgfxAttrDef::kAttrTypeScreenDir:

            case cgfxAttrDef::kAttrTypeObjectPos:
            case cgfxAttrDef::kAttrTypeViewPos:
            case cgfxAttrDef::kAttrTypeProjectionPos:
            case cgfxAttrDef::kAttrTypeScreenPos:

            case cgfxAttrDef::kAttrTypeWorldMatrix:
            case cgfxAttrDef::kAttrTypeViewMatrix:
            case cgfxAttrDef::kAttrTypeProjectionMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewProjectionMatrix:
                // View dependent parameter
                break;
            default:
                M_CHECK( false );
            }                          // switch (aDef->fType)
        }
        catch ( cgfxShaderCommon::InternalError* e )
        {
            if ( ++fErrorCount <= fErrorLimit )
            {
                size_t ee = (size_t)e;
                MFnDependencyNode fnNode( oNode );
                MString sMsg = "cgfxShader warning ";
                sMsg += (int)ee;
                sMsg += ": ";
                sMsg += fnNode.name();
                sMsg += " internal error while setting parameter \"";
                sMsg += aDef->fName;
                sMsg += "\" of effect \"";
                sMsg += fShaderFxFile;
                sMsg += "\" for shape ";
                sMsg += currentPath().partialPathName();
                MGlobal::displayWarning( sMsg );
            }
        }
    }                                  // loop over fAttrDefList
}                                      // cgfxShaderNode::bindAttrValues

void
cgfxShaderNode::bindViewAttrValues(const MDagPath& shapePath)
{
    if ( !fEffect ||
        !fTechnique.length() )
        return;

    MStatus  status;
    MObject  oNode = thisMObject();

    MMatrix wMatrix, vMatrix, pMatrix, sMatrix;
    MMatrix wvMatrix, wvpMatrix, wvpsMatrix;
    {
        float tmp[4][4];

        if (shapePath.isValid())
            wMatrix = shapePath.inclusiveMatrix();
        else
            wMatrix.setToIdentity();

        glGetFloatv(GL_MODELVIEW_MATRIX, &tmp[0][0]);
        wvMatrix = MMatrix(tmp);

        vMatrix = wMatrix.inverse() * wvMatrix;
//
        glGetFloatv(GL_PROJECTION_MATRIX, &tmp[0][0]);
        pMatrix = MMatrix(tmp);

        wvpMatrix = wvMatrix * pMatrix;

        float vpt[4];
        float depth[2];

        glGetFloatv(GL_VIEWPORT, vpt);
        glGetFloatv(GL_DEPTH_RANGE, depth);

        // Construct the NDC -> screen space matrix
        //
        float x0, y0, z0, w, h, d;

        x0 = vpt[0];
        y0 = vpt[1];
        z0 = depth[0];
        w  = vpt[2];
        h  = vpt[3];
        d  = depth[1] - z0;

        // Make a reference to ease the typing
        //
        double* s = &sMatrix.matrix[0][0];

        s[ 0] = w/2;    s[ 1] = 0.0;    s[ 2] = 0.0;    s[ 3] = 0.0;
        s[ 4] = 0.0;    s[ 5] = h/2;    s[ 6] = 0.0;    s[ 7] = 0.0;
        s[ 8] = 0.0;    s[ 9] = 0.0;    s[10] = d/2;    s[11] = 0.0;
        s[12] = x0+w/2; s[13] = y0+h/2; s[14] = z0+d/2; s[15] = 1.0;

        wvpsMatrix = wvpMatrix * sMatrix;
    }

    for ( cgfxAttrDefList::iterator it( fAttrDefList ); it; ++it )
    {                                  // loop over fAttrDefList
        cgfxAttrDef* aDef = *it;

        try
        {

            switch (aDef->fType)
            {
            case cgfxAttrDef::kAttrTypeObjectDir:
            case cgfxAttrDef::kAttrTypeViewDir:
            case cgfxAttrDef::kAttrTypeProjectionDir:
            case cgfxAttrDef::kAttrTypeScreenDir:

            case cgfxAttrDef::kAttrTypeObjectPos:
            case cgfxAttrDef::kAttrTypeViewPos:
            case cgfxAttrDef::kAttrTypeProjectionPos:
            case cgfxAttrDef::kAttrTypeScreenPos:
                {
                    float tmp[4];
                    if (aDef->fSize == 3)
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2]);
                        tmp[3] = 1.0;
                    }
                    else
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2], tmp[3]);
                    }

                    // Maya's API only provides for vectors of size 3.
                    // When we do the matrix multiply, it will only
                    // work correctly if the 4th coordinate is 1.0
                    //

                    MVector vec(tmp[0], tmp[1], tmp[2]);

                    int space = aDef->fType - cgfxAttrDef::kAttrTypeFirstPos;
                    if (space < 0)
                    {
                        space = aDef->fType - cgfxAttrDef::kAttrTypeFirstDir;
                    }

                    MMatrix mat;  // initially the identity matrix.

                    switch (space)
                    {
                    case 0: /* mat = identity */    break;
                    case 1: mat = wMatrix;          break;
                    case 2: mat = wvMatrix;         break;
                    case 3: mat = wvpMatrix;        break;
                    case 4: mat = wvpsMatrix;       break;
                    }

                    // Maya's transformation matrices are set up with
                    // the translation in row 3 (like OpenGL) rather
                    // than column 3. To transform a point or vector,
                    // use V*M, not M*V.   kh 11/2003
                    int base = cgfxAttrDef::kAttrTypeFirstPos;
                    if (aDef->fType <= cgfxAttrDef::kAttrTypeLastDir)
                        base = cgfxAttrDef::kAttrTypeFirstDir;
                    if (base == cgfxAttrDef::kAttrTypeFirstPos)
                    {
                        MPoint point(tmp[0], tmp[1], tmp[2], tmp[3]);
                        point *= wMatrix.inverse() * mat;
                        tmp[0] = (float)point.x;
                        tmp[1] = (float)point.y;
                        tmp[2] = (float)point.z;
                        tmp[3] = (float)point.w;
                    }
                    else
                    {
                        MVector vec(tmp[0], tmp[1], tmp[2]);
                        vec *= wMatrix.inverse() * mat;
                        tmp[0] = (float)vec.x;
                        tmp[1] = (float)vec.y;
                        tmp[2] = (float)vec.z;
                        tmp[3] = 1.F;
                    }

                    cgSetParameterValuefc(aDef->fParameterHandle, aDef->fSize, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeWorldMatrix:
            case cgfxAttrDef::kAttrTypeViewMatrix:
            case cgfxAttrDef::kAttrTypeProjectionMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewProjectionMatrix:
                {
                    MMatrix mat;

                    switch (aDef->fType)
                    {
                    case cgfxAttrDef::kAttrTypeWorldMatrix:
                        mat = wMatrix; break;
                    case cgfxAttrDef::kAttrTypeViewMatrix:
                        mat = vMatrix; break;
                    case cgfxAttrDef::kAttrTypeProjectionMatrix:
                        mat = pMatrix; break;
                    case cgfxAttrDef::kAttrTypeWorldViewMatrix:
                        mat = wvMatrix; break;
                    case cgfxAttrDef::kAttrTypeWorldViewProjectionMatrix:
                        mat = wvpMatrix; break;
                    default:
                        break;
                    }

                    if (aDef->fInvertMatrix)
                    {
                        mat = mat.inverse();
                    }

                    if (!aDef->fTransposeMatrix)
                    {
                        mat = mat.transpose();
                    }

                    float tmp[4][4];
                    mat.get(tmp);
                    cgSetMatrixParameterfr(aDef->fParameterHandle, &tmp[0][0]);
                    break;
                }
                default:
                    break;
            }                          // switch (aDef->fType)
        }
        catch ( cgfxShaderCommon::InternalError* e )
        {
            if ( ++fErrorCount <= fErrorLimit )
            {
                size_t ee = (size_t)e;
                MFnDependencyNode fnNode( oNode );
                MString sMsg = "cgfxShader warning ";
                sMsg += (int)ee;
                sMsg += ": ";
                sMsg += fnNode.name();
                sMsg += " internal error while setting parameter \"";
                sMsg += aDef->fName;
                sMsg += "\" of effect \"";
                sMsg += fShaderFxFile;
                sMsg += "\" for shape ";
                if (shapePath.isValid())
                    sMsg += shapePath.partialPathName();
                else
                    sMsg += "SWATCH GEOMETRY";
                MGlobal::displayWarning( sMsg );
            }
        }
    }                                  // loop over fAttrDefList
}

/* virtual */
MStatus cgfxShaderNode::glUnbind(const MDagPath& shapePath)
{
    if( fNewEffect.fTechnique && fNewEffect.fTechnique->fPasses)
    {
        // Shaders have an uncanny ability to corrupt depth state
        if( fDepthEnableState)
            glEnable( GL_DEPTH_TEST);
        else
            glDisable( GL_DEPTH_TEST);
        glDepthFunc( fDepthFunc);
        glBlendFunc( fBlendSourceFactor, fBlendDestFactor);

        if (fTechniqueHasBlending)
            glPopAttrib();
    }
    else
    {
        // Restore material attributes
        glPopAttrib();
    }

    glPopClientAttrib();
    glPopAttrib();

    glStateCache::instance().disableAll();
    glStateCache::instance().activeTexture( 0);


#ifdef KH_DEBUG
    MString ss = "  .. unbd ";
    if ( this && fConstructed )
        ss += name();
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif
    return MS::kSuccess;
}


/* virtual */
MStatus cgfxShaderNode::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)
{
    MStatus stat;

#ifdef KH_DEBUG
    MString ss = "  .. geom ";
    if ( this && fConstructed )
        ss += name();
    ss += " ";
    ss += indexCount;
    ss += "i ";
    ss += vertexCount;
    ss += "v ";
    ss += normalCount;
    ss += "n ";
    ss += colorCount;
    ss += "c ";
    ss += texCoordCount;
    ss += "t ";
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif

    try
    {
        if( fNewEffect.fTechnique && fNewEffect.fTechnique->fPasses)
        {
            //register cg default state callbacks
            cgfxPassStateSetter::registerCgStateCallBacks(
                cgfxPassStateSetter::kDefaultViewport);

            // Set up the uniform attribute values for the effect.
            bindViewAttrValues(shapePath);

            // If our input shape is dirty, clear any cached data
            if( dirtyMask() != kDirtyNone)
                fNewEffect.flushCache( shapePath);

            // Now render the passes for this effect
            cgfxPass* pass = fNewEffect.fTechnique->fPasses;
            while( pass)
            {
                pass->bind( shapePath, vertexCount, vertexArray, fNormalsPerVertex, normalCount, normalArrays, colorCount, colorArrays, texCoordCount, texCoordArrays);
                glStateCache::instance().flushState();
                cgSetPassState( pass->fPass);
                glDrawElements(prim, indexCount, GL_UNSIGNED_INT, indexArray);
                cgResetPassState( pass->fPass);
                pass = pass->fNext;
            }
        }
        else // fEffect must be NULL
        {
            // Now call glDrawElements to put all the primitives on the
            // screen.  See the comment above re: glDrawRangeElements.
            //
            glStateCache::instance().enablePosition();
            glVertexPointer(3, GL_FLOAT, 0, vertexArray);

            if ( normalCount > 0 && normalArrays[ 0 ] )
            {
                glStateCache::instance().enableNormal();
                glNormalPointer(GL_FLOAT, 0, normalArrays[0]);
            }
            else
            {
                glStateCache::instance().disableNormal();
                glNormal3f(0.0, 0.0, 1.0);
            }
            glStateCache::instance().flushState();
            glDrawElements(prim, indexCount, GL_UNSIGNED_INT, indexArray);
        }

        checkGlErrors("After effects End");
    }
    catch ( cgfxShaderCommon::InternalError* e )
    {
        reportInternalError( __FILE__, (size_t)e );
        stat = MS::kFailure;
    }
    catch ( ... )
    {
        reportInternalError( __FILE__, __LINE__ );
        stat = MS::kFailure;
    }

    return stat;
}                                      // cgfxShaderNode::geometry


/* virtual */
int
cgfxShaderNode::getTexCoordSetNames( MStringArray& names )
{
    names = fUVSets;
    return names.length();
}                                      // cgfxShaderNode::getTexCoordSetNames


#if MAYA_API_VERSION >= 700

/* virtual */
int
cgfxShaderNode::getColorSetNames( MStringArray& names )
{
    names = fColorSets;
    return names.length();
}

#else

/* virtual */
int cgfxShaderNode::colorsPerVertex()
{
    fColorType.setLength(1);
    fColorIndex.setLength(1);
    fColorType[0] = 0;
    fColorIndex[0] = 0;
    return 1;
} // cgfxShaderNode::texCoordsPerVertex

#endif

/* virtual */
int cgfxShaderNode::normalsPerVertex()
{
#ifdef KH_DEBUG
    MString ss = "  .. npv  ";
    if ( this && fConstructed )
        ss += name();
    ss += " ";
    ss += fNormalsPerVertex;
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif

    // Now, when using MPxHwShaderNode, this is the first call Maya makes when
    // trying to render a plugin shader. So, in the cases where we were unable
    // to create our effect, try and do it here
    if (fEffect == NULL) {
#ifdef _WIN32
        ::OutputDebugString( "CGFX: fEffect was NULL\n");
#endif

        // When batch off-screen rendering through "mayabatch -command hwRender ...",
        // the effect will be uninitialized because there was no active OpenGL
        // context at the time "cgfxShader -e -fx ..." was executed. This setup
        // is delayed until now when hardware renderer guarantees a valid context
        // and requests the plug-in to bind its resources to it. -cdt
        //
        createEffect();
    }

    return fNormalsPerVertex;

    // NB: Maya calls normalsPerVertex() both before and after bind().
    // It appears that the normalCount passed to geometry() is
    // obtained *before* the call to bind().  Therefore we set
    // fNormalsPerVertex as early as possible.  kh 9/03
}                                      // cgfxShaderNode::normalsPerVertex


void
cgfxShaderNode::setAttrDefList( cgfxAttrDefList* list )
{
    if (fAttrDefList)
        cgfxAttrDef::purgeMObjectCache( fAttrDefList );

    if ( list )
    {
        list->addRef();
        cgfxAttrDef::validateMObjectCache( thisMObject(), list );
    }

    if (fAttrDefList)
        fAttrDefList->release();

    fAttrDefList = list;
}                                      // cgfxShaderNode::setAttrDefList


void cgfxShaderNode::getAttributeList(MStringArray& attrList) const
{
    MString tmp;
    int len = fAttributeListArray.length();

    attrList.clear();

    for (int i = 0; i < len; ++i)
    {
        tmp = fAttributeListArray[i];
        attrList.append(tmp);
    }
}

void cgfxShaderNode::setAttributeList(const MStringArray& attrList)
{
    MString tmp;
    int len = attrList.length();

    fAttributeListArray.clear();

    for (int i = 0; i < len; ++i)
    {
        tmp = attrList[i];
        fAttributeListArray.append(tmp);
    }
}


//
// Set the current per-vertex attributes the shader needs (replacing any existing set)
//
void
cgfxShaderNode::setVertexAttributes( cgfxVertexAttribute* attributeList)
{
    // Backward compatibility: if we have values set in the old texCoordSources
    // or colorSources, find any varying attributes that use that register
    // and inherit the maya source
    if( fTexCoordSource.length())
    {
        int length = fTexCoordSource.length();
        for( int i = 0; i < length; i++)
        {
            MString semantic( "TEXCOORD");
            if( i)
                semantic += i;
            else
                semantic += "0";
            MString source( fTexCoordSource[ i]);
            if( source.index( ':') < 0)
                source = "uv:" + source;
            cgfxVertexAttribute* newAttribute = attributeList;
            while( newAttribute)
            {
                if( newAttribute->fSemantic == semantic ||
                    (i == 6 && (newAttribute->fSemantic == "TANGENT" || newAttribute->fSemantic == "TANGENT0")) ||
                    (i == 7 && (newAttribute->fSemantic == "BINORMAL" || newAttribute->fSemantic == "BINORMAL0")))
                    newAttribute->fSourceName = source;
                newAttribute = newAttribute->fNext;
            }
        }
        fTexCoordSource.clear();
    }
    if( fColorSource.length())
    {
        int length = fColorSource.length();
        for( int i = 0; i < length; i++)
        {
            MString semantic( "COLOR");
            if( i)
                semantic += i;
            else
                semantic += "0";
            MString source( fColorSource[ i]);
            if( source.index( ':') < 0)
                source = "color:" + source;
            cgfxVertexAttribute* newAttribute = attributeList;
            while( newAttribute)
            {
                if( newAttribute->fSemantic == semantic)
                    newAttribute->fSourceName = source;
                newAttribute = newAttribute->fNext;
            }
        }
        fColorSource.clear();
    }

    // If we have an existing attribute list, copy across the current values
    // the remove it
    cgfxVertexAttribute* oldAttributes = fVertexAttributes;
    while( oldAttributes)
    {
        cgfxVertexAttribute* oldAttribute = oldAttributes;
        oldAttributes = oldAttributes->fNext;
        cgfxVertexAttribute* newAttribute = attributeList;
        while( newAttribute)
        {
            if( newAttribute->fName == oldAttribute->fName)
            {
                newAttribute->fSourceName = oldAttribute->fSourceName;
                newAttribute->fSourceType = oldAttribute->fSourceType;
            }
            newAttribute = newAttribute->fNext;
        }
        delete oldAttribute;
    }

    // Now set our new attribute list
    fVertexAttributes = attributeList;

    // And determine the minimum set of data we need to request from Maya to
    // populate these values
    analyseVertexAttributes();
}


//
// Set the data set names that will be populating our vertex attributes
//
void
cgfxShaderNode::setVertexAttributeSource( const MStringArray& sources)
{
    // Flush any cached data our effect has - the inputs have changed
    fNewEffect.flushCache();

    // Set the attributes sources as specified
    int i = 0;
    int numSources = sources.length();
    cgfxVertexAttribute* attribute = fVertexAttributes;
    while( attribute)
    {
        attribute->fSourceName = ( i <  numSources) ? sources[ i++] : "";
        attribute = attribute->fNext;
    }

    // And determine the minimum set of data we need to request from Maya to
    // populate these values
    analyseVertexAttributes();
}

inline int findOrInsert( const MString& value, MStringArray& list)
{
    int length = list.length();
    for( int i = 0; i < length; i++)
        if( list[ i] == value)
            return i;
    list.append( value);
    return length;
}

//
// Analyse the per-vertex attributes to work out the minimum set of data we require
//
void
cgfxShaderNode::analyseVertexAttributes()
{
    fUVSets.clear();
    fColorSets.clear();
    fNormalsPerVertex = 0;

    cgfxVertexAttribute* attribute = fVertexAttributes;
    while( attribute)
    {
        // Work out where this attribute should come from
        MString source( attribute->fSourceName);
        source.toLowerCase();
        if( attribute->fSourceName.length() == 0)
        {
            attribute->fSourceType = cgfxVertexAttribute::kNone;
        }
        else if( source == "position")
        {
            attribute->fSourceType = cgfxVertexAttribute::kPosition;
        }
        else if( source == "normal")
        {
            attribute->fSourceType = cgfxVertexAttribute::kNormal;
            if( fNormalsPerVertex < 1)
                fNormalsPerVertex = 1;
        }
        else
        {
            // Try and pull off the type
            MString set = attribute->fSourceName;
            int colon = set.index( ':');
            MString type;
            if( colon >= 0)
            {
                if( colon > 0) type = source.substring( 0, colon - 1);
                set = set.substring( colon + 1, set.length() - 1);
            }

            // Now, work out what kind of set we have here
            if( type == "uv")
            {
                attribute->fSourceType = cgfxVertexAttribute::kUV;
                attribute->fSourceIndex = findOrInsert( set, fUVSets);
            }
            else if( type == "tangent")
            {
                attribute->fSourceType = cgfxVertexAttribute::kTangent;
                if( fNormalsPerVertex < 2)
                    fNormalsPerVertex = 2;
                attribute->fSourceIndex = findOrInsert( set, fUVSets);
            }
            else if( type == "binormal")
            {
                attribute->fSourceType = cgfxVertexAttribute::kBinormal;
                if( fNormalsPerVertex < 3)
                    fNormalsPerVertex = 3;
                attribute->fSourceIndex = findOrInsert( set, fUVSets);
            }
            else if( type == "color")
            {
                attribute->fSourceType = cgfxVertexAttribute::kColor;
                attribute->fSourceIndex = findOrInsert( set, fColorSets);
            }
            else
            {
                attribute->fSourceType = cgfxVertexAttribute::kUnknown;
            }
        }
        attribute = attribute->fNext;
    }

    //for( unsigned int i = 0; i < fUVSets.length(); i++) printf( "Requesting UVset[%d] = %s\n", i, fUVSets[i]);
}


// Data accessors for the texCoordSource attribute.
const MStringArray&
cgfxShaderNode::getTexCoordSource() const
{
#ifdef KH_DEBUG
    MString ss = "  .. gtcs ";
    if ( this && fConstructed )
        ss += name();
    ss += " ";
    for ( int ii = 0; ii < fTexCoordSource.length(); ++ii )
    {
        ss += "\"";
        ss += fTexCoordSource[ii];
        ss += "\" ";
    }
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif
    return fTexCoordSource;
}                                      // cgfxShaderNode::getTexCoordSource


// Data accessors for the colorSource attribute.
const MStringArray&
cgfxShaderNode::getColorSource() const
{
#ifdef KH_DEBUG
    MString ss = "  .. gtcs ";
    if ( this && fConstructed )
        ss += name();
    ss += " ";
    for ( int ii = 0; ii < fColorSource.length(); ++ii )
    {
        ss += "\"";
        ss += fColorSource[ii];
        ss += "\" ";
    }
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif
    return fColorSource;
}                                      // cgfxShaderNode::getColorSource


void
cgfxShaderNode::setDataSources( const MStringArray* texCoordSources,
                                const MStringArray* colorSources)
{
    if( texCoordSources )
    {
        int length_TC = texCoordSources->length();
        if ( length_TC > CGFXSHADERNODE_GL_TEXTURE_MAX )
            length_TC = CGFXSHADERNODE_GL_TEXTURE_MAX;
        fTexCoordSource.clear();
        for ( int i = 0; i < length_TC; ++i )
        {
            fTexCoordSource.append( (*texCoordSources)[ i ] );
        }
        // This method is unstable and may causes crashes in the API
        // Don't use for now.
        //fTexCoordSource.setLength( length_TC );
        //for ( int i = 0; i < length_TC; ++i )
        //  fTexCoordSource[ i ] = texCoordSources[ i ];
    }

    if( colorSources )
    {
        int length_CS = colorSources->length();
        if ( length_CS > CGFXSHADERNODE_GL_COLOR_MAX )
            length_CS = CGFXSHADERNODE_GL_COLOR_MAX;
        fColorSource.setLength( length_CS );
        for ( int i = 0; i < length_CS; ++i )
            fColorSource[ i ] = (*colorSources)[ i ];
    }

    fDataSetNames.clear();
    fNormalsPerVertex = 1;
    updateDataSource( fTexCoordSource, fTexCoordType, fTexCoordIndex);
    updateDataSource( fColorSource, fColorType, fColorIndex);
}


void
cgfxShaderNode::updateDataSource( MStringArray& v, MIntArray& typeList, MIntArray& indexList)
{
#ifdef KH_DEBUG
    MString ss = "  .. stcs ";
    if ( this && fConstructed )
        ss += name();
    ss += " ";
    for ( int ii = 0; ii < v.length(); ++ii )
    {
        ss += "\"";
        ss += v[ii];
        ss += "\" ";
    }
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif

    int nDataSets = v.length();
    typeList.setLength( nDataSets );
    indexList.setLength( nDataSets );
    for ( int iDataSet = 0; iDataSet < nDataSets; ++iDataSet )
    {                                  // iDataSet loop
        MString s;
        int iType = etcNull;
        int iBuf = 0;

        // Strip leading and trailing spaces and control chars.
        const char* bp = v[ iDataSet ].asChar();
        const char* ep = v[ iDataSet ].length() + bp;
#ifdef _WIN32
        while ( bp < ep && *bp <= ' ' && *bp >= '\0') ++bp;
#else
        while ( bp < ep && *bp <= ' ') ++bp;
#endif

#ifdef _WIN32
        while ( bp < ep && ep[-1] <= ' ' && ep[-1] >= '\0' ) --ep;
#else
        while ( bp < ep && ep[-1] <= ' ' ) --ep;
#endif

        // Empty?
        if ( bp == ep )
            iType = etcNull;

        // Constant?  (1, 2, 3 or 4 float values)
        else if ( (*bp >= '0' && *bp <= '9') ||
            *bp == '-' ||
            *bp == '+' ||
            *bp == '.' )
        {
            const char* cp = bp;
            int nValues = 0;
            while ( cp < ep &&
                nValues < 4 )
            {
                float x;
                int   nc = 0;
                int   nv = sscanf( cp, " %f%n", &x, &nc );
                if ( nv != 1 )
                    break;
                ++nValues;
                cp += nc;
            }
            if ( nValues > 0 )
            {
                s.set( bp, (int)(cp - bp) );      // drop trailing junk
                for ( ; nValues < 4; ++nValues )
                    s += " 0";
                iType = etcConstant;
            }
        }

        // UV set name or reserved word.
        else
        {
            s.set( bp, (int)(ep - bp) );

            // Pull out any qualifiers (e.g. tangent:uvSet1) and register
            // the data set they require
            //
            MStringArray splitStrings;
            #define kDefaultUVSet "map1"
            if ((MStatus::kSuccess == s.split( ':', splitStrings)) && splitStrings.length() > 1)
            {
                s = splitStrings[0];
                iBuf = findOrAppend( fDataSetNames, splitStrings[1]);
            }

            // Force reserved words to lower case.
            bp = s.asChar();
            if ( 0 == stricmp( "normal", bp ) )
            {
                s = "normal";
                iType = etcNormal;
            }
            else if ( 0 == stricmp( "tangent", bp ) )
            {
                s = "tangent";
                if( splitStrings.length() < 2)
                {
                    splitStrings.setLength( 2);
                    splitStrings[ 1] = kDefaultUVSet;
                    iBuf = findOrAppend( fDataSetNames, kDefaultUVSet);
                }
                s += ":" + splitStrings[1];
                iType = etcTangent;
                if( fNormalsPerVertex < 2)
                    fNormalsPerVertex = 2;
            }
            else if ( 0 == stricmp( "binormal", bp ) )
            {
                s = "binormal";
                if( splitStrings.length() < 2)
                {
                    splitStrings.setLength( 2);
                    splitStrings[ 1] = kDefaultUVSet;
                    iBuf = findOrAppend( fDataSetNames, kDefaultUVSet);
                }
                s += ":" + splitStrings[1];
                iType = etcBinormal;
                fNormalsPerVertex = 3;
            }

            // Data set name... tell Maya that we want to retrieve this data set.
            else
            {
                iType = etcDataSet;
                iBuf = findOrAppend( fDataSetNames, s );
            }
        }

        // Tell our geometry() method where to get data.
        typeList[ iDataSet ] = iType;
        indexList[ iDataSet ] = iBuf;

        // Store cleaned-up string.
        v[ iDataSet ] = s;
    }                                  // iDataSet loop
}                                      // cgfxShaderNode::updateDataSource


// Data accessor for list of empty UV sets.
const MStringArray&
cgfxShaderNode::getEmptyUVSets() const
{
    static const MStringArray saNull;
    return saNull;
}                                      // cgfxShaderNode::getEmptyUVSets

const MObjectArray&
cgfxShaderNode::getEmptyUVSetShapes() const
{
    static const MObjectArray oaNull;
    return oaNull;
}                                      // cgfxShaderNode::getEmptyUVSetShapes


void
cgfxShaderNode::setEffect(CGeffect pNewEffect)
{
#ifdef KH_DEBUG
    char ssbuf[64];
    MString ss = "  .. se   ";
    ss += name();
    ss += " ";
    sprintf( ssbuf, "new=%X old=%X", pNewEffect, fEffect );
    ss += ssbuf;
    ss += "\n";
    ::OutputDebugString( ss.asChar() );
#endif

    if (fEffect)
        cgDestroyEffect(fEffect);

    fEffect = pNewEffect;

    fNewEffect.setEffect( fEffect);
    // fNewEffect.setupAttributes( this); this will happen when the technique gets set!

    // Build string array containing technique names and descriptions.
    //     Each item in the technique list has the form
    //         "techniqueName<TAB>numPasses"
    //     where
    //         numPasses is the number of passes defined by the
    //             technique, or 0 if the technique is not valid.
    //     (Future versions of the cgfxShader plug-in may append
    //      additional tab-separated fields.)
    fTechniqueList.clear();
    if (fEffect)
    {
        CGtechnique cgTechnique = cgGetFirstTechnique(fEffect);
        while (cgTechnique)
        {
            MString s;

            const char*     techniqueName = cgGetTechniqueName(cgTechnique);
            if (techniqueName)
            {
                s += techniqueName;
            }

            // Force the program profiles before attempting to
            // validate the technique...
            int     numPasses = 0;
            {
                CGpass cgPass = cgGetFirstPass(cgTechnique);
                while (cgPass)
                {
                    ++numPasses;

                    CGprogram vp = cgGetPassProgram(cgPass, CG_VERTEX_DOMAIN);
                    if (vp != NULL &&
                        cgGetProgramProfile(vp) != cgfxShaderNode::sCgBestVtxProfile)
                    {
                        cgSetProgramProfile(vp, cgfxShaderNode::sCgBestVtxProfile);
                    }
                    CGprogram gp = cgGetPassProgram(cgPass, CG_GEOMETRY_DOMAIN);
                    if (gp != NULL &&
                        cgGetProgramProfile(gp) != cgfxShaderNode::sCgBestGeomProfile)
                    {
                        cgSetProgramProfile(gp, cgfxShaderNode::sCgBestGeomProfile);
                    }
                    CGprogram fp = cgGetPassProgram(cgPass, CG_FRAGMENT_DOMAIN);
                    if (fp != NULL &&
                        cgGetProgramProfile(fp) != cgfxShaderNode::sCgBestFragProfile)
                    {
                        cgSetProgramProfile(fp, cgfxShaderNode::sCgBestFragProfile);
                    }

                    cgPass = cgGetNextPass(cgPass);
                }
            }

            if (cgValidateTechnique(cgTechnique) == CG_TRUE)
            {
                s += "\t";
                s += numPasses;
            }
            else
            {
                // TO DO: should get the errors from the failed technique
                //
                s += "\t0";
            }

            fTechniqueList.append(s);
            cgTechnique = cgGetNextTechnique(cgTechnique);

        }
    }
}                                      // cgfxShaderNode::setEffect


/* virtual */ bool cgfxShaderNode::hasTransparency()
{
    // Always return false, so that transparencyOptions() will be
    // called to give finer grain control.
    return false;
}


/* virtual */
unsigned int cgfxShaderNode::transparencyOptions()
{
    if (fTechniqueHasBlending)
    {
        // Set as transparent, but we don't want any internal transparency algorithms
        // being used.
        return ( kIsTransparent | kNoTransparencyFrontBackCull | kNoTransparencyPolygonSort );
    }
    return 0;
}

//
// Scan the technique for passes which use blending
//
void cgfxShaderNode::setTechniqueHasBlending( CGtechnique technique)
{
    // Assume not blending
    fTechniqueHasBlending = false;

    // Check for : BlendEnable=true, BlendFunc=something valid on the first pass only.
    //
    // We ignore any depth enable and functions for now...
    //
    CGpass cgPass = cgGetFirstPass(technique);
    bool foundBlendEnabled = false;
    bool foundBlendFunc = false;
    if (cgPass)
    {
        CGstateassignment stateAssignment = cgGetFirstStateAssignment(cgPass);
        while ( stateAssignment )
        {
            CGstate state = cgGetStateAssignmentState( stateAssignment);
            const char *stateName = cgGetStateName(state);

            // Check for blend enabled.
            if (!foundBlendEnabled && stricmp( stateName, "BlendEnable") == 0)
            {
                int numValues = 0;
                const CGbool *values = cgGetBoolStateAssignmentValues(stateAssignment, &numValues);
                if (values && numValues)
                {
                    if (values[0])
                    {
                        foundBlendEnabled = true;
                    }
                }
            }

            // Check for valid blend function
            else if (!foundBlendFunc && ( stricmp( stateName, "BlendFunc") == 0 ||
                                          stricmp( stateName, "BlendFuncSeparate") == 0 ))
            {
                int numValues = 0;
                const int * values = cgGetIntStateAssignmentValues(stateAssignment, &numValues);
                if (values)
                {
#if defined(CGFX_DEBUG_BLEND_FUNCTIONS)
                    /*
                    #define GL_SRC_COLOR                      0x0300 = 768
                    #define GL_ONE_MINUS_SRC_COLOR            0x0301 = 769
                    #define GL_SRC_ALPHA                      0x0302 = 770
                    #define GL_ONE_MINUS_SRC_ALPHA            0x0303 = 771
                    #define GL_DST_ALPHA                      0x0304 = 772
                    #define GL_ONE_MINUS_DST_ALPHA            0x0305 = 773
                    */
                    MString blendStringTable[6] =
                    {
                        "GL_SRC_COLOR", // SrcColor
                        "GL_ONE_MINUS_SRC_COLOR", // OneMinusSrcColor
                        "GL_SRC_ALPHA", // SrcAlpha
                        "GL_ONE_MINUS_SRC_ALPHA", // OneMinusSrcAlpha
                        "GL_DST_ALPHA", // DstAlpha
                        "GL_ONE_MINUS_DST_ALPHA" // OneMinusDstAlpha
                    };
#endif
                    for (int i=0; i<numValues; i++)
                    {
                        if ((values[i] >= GL_SRC_COLOR) && (values[i] <= GL_ONE_MINUS_DST_ALPHA))
                        {
#if defined(CGFX_DEBUG_BLEND_FUNCTIONS)
                            printf("Found blend function = %s, %s\n",
                            blendStringTable[ values[0]-GL_SRC_COLOR].asChar(),
                            blendStringTable[ values[1]-GL_SRC_COLOR].asChar());
#endif
                            foundBlendFunc = true;
                            break;
                        }
                    }
                }
            }
            fTechniqueHasBlending = foundBlendEnabled && foundBlendFunc;
            if (fTechniqueHasBlending)
                break;
            stateAssignment = cgGetNextStateAssignment( stateAssignment);
        }
    }
}

void
cgfxShaderNode::setTechnique( const MString& techn )
{
    // If effect not loaded, just store the technique name.
    if (!fEffect)
    {
        fTechnique = techn;
        fTechniqueHasBlending = false;
        return;
    }

    // Search for requested technique.
    if (techn.length() != 0) {
        CGtechnique technique = cgGetNamedTechnique(fEffect, techn.asChar());
        if (cgValidateTechnique(technique) == CG_TRUE)
        {
            fTechnique = techn;
            setTechniqueHasBlending( technique );

            // Setup the vertex parameters for this technique
            fNewEffect.setupAttributes( this);
            return;
        }
        else if (!shaderFxFileChanged()) {
            CGerror cgLastError = cgGetError();

            MString s;
            s += typeName();
            s += " \"";
            s += name();
            s += "\" : unable to validate technique \"";
            s += techn.asChar();
            s += "\"";
            MGlobal::displayError(s);

            if(cgLastError)
            {
                MGlobal::displayError(cgGetErrorString(cgLastError));
                MGlobal::displayError(cgGetLastListing(sCgContext));
            }
        }
    }

    // Requested technique was not found or not valid.  Revert to the old one.
    if (fTechnique.length() != 0) {
        CGtechnique technique = cgGetNamedTechnique(fEffect, fTechnique.asChar());
        if (cgValidateTechnique(technique) == CG_TRUE)
        {
            setTechniqueHasBlending( technique );
            return;
        }
        else if (!shaderFxFileChanged()) {
            CGerror cgLastError = cgGetError();

            MString s;
            s += typeName();
            s += " \"";
            s += name();
            s += "\" : unable to validate technique \"";
            s += fTechnique.asChar();
            s += "\"";
            MGlobal::displayError(s);

            if(cgLastError)
            {
                MGlobal::displayError(cgGetErrorString(cgLastError));
                MGlobal::displayError(cgGetLastListing(sCgContext));
            }
        }
    }

    // Old technique is no good.  Activate the first valid technique.
    CGtechnique technique = cgGetFirstTechnique(fEffect);
    while (technique)
    {
        if (cgValidateTechnique(technique) == CG_TRUE)
        {
            fTechnique = cgGetTechniqueName(technique);

            setTechniqueHasBlending( technique );

            // Setup the vertex parameters for this technique
            fNewEffect.setupAttributes( this);
            return;
        }
        else {
            CGerror cgLastError = cgGetError();

            MString s;
            s += typeName();
            s += " \"";
            s += name();
            s += "\" : unable to validate technique \"";
            s += cgGetTechniqueName(technique);
            s += "\"";
            MGlobal::displayError(s);

            if(cgLastError)
            {
                MGlobal::displayError(cgGetErrorString(cgLastError));
                MGlobal::displayError(cgGetLastListing(sCgContext));
            }
        }
        technique = cgGetNextTechnique(technique);
  }

    // No valid technique exists for the current effect.
    //   Save requested technique name.  We'll try to use it as the
    //   initial technique the next time a valid effect is loaded.
    fTechnique = techn;
    fTechniqueHasBlending = false;
}                                      // cgfxShaderNode::setTechnique


MStatus cgfxShaderNode::shouldSave ( const MPlug & plug, bool & ret )
{
    if (plug == sAttributeList)
    {
        ret = true;
        return MS::kSuccess;
    }
    else if (plug == sVertexAttributeList)
    {
        ret = true;
        return MS::kSuccess;
    }
    return MPxNode::shouldSave(plug, ret);
}


void cgfxShaderNode::setTexturesByName(bool texturesByName, bool updateAttributes)
{
    if( updateAttributes && fTexturesByName != texturesByName)
    {
        // We've been explicitly changed to a different
        // texture mode.

        // If we have any current texture attributes, destroy them
        //
        MDGModifier dgMod;
        cgfxAttrDefList* nodeList = attrDefList();
        cgfxAttrDefList::iterator nmIt;
        bool foundTextures = false;
        for (nmIt = nodeList->begin(); nmIt; ++nmIt)
        {
            cgfxAttrDef* adef = (*nmIt);
            if(adef->fType >= cgfxAttrDef::kAttrTypeFirstTexture && adef->fType <= cgfxAttrDef::kAttrTypeLastTexture)
            {
                MObject theMObject = thisMObject();
                adef->destroyAttribute( theMObject, &dgMod);
                foundTextures = true;
            }
        }

        // Switch across to the new texture mode (before creating the
        // new attributes)
        //
        fTexturesByName = texturesByName;

        // Now re-create our texture attributes
        //
        if( foundTextures)
        {
            dgMod.doIt();
            for (nmIt = nodeList->begin(); nmIt; ++nmIt)
            {
                cgfxAttrDef* adef = (*nmIt);
                if( adef->fType >= cgfxAttrDef::kAttrTypeFirstTexture && adef->fType <= cgfxAttrDef::kAttrTypeLastTexture)
                {
                    adef->createAttribute(thisMObject(), &dgMod, this);
                }
            }
            dgMod.doIt();

            // Finally, if we just created new string attributes, we need to
            // set them to a sensible value or they won't show up
            //
            if( fTexturesByName)
            {
                for (nmIt = nodeList->begin(); nmIt; ++nmIt)
                {
                    cgfxAttrDef* adef = (*nmIt);
                    if( adef->fType >= cgfxAttrDef::kAttrTypeFirstTexture &&
                        adef->fType <= cgfxAttrDef::kAttrTypeLastTexture)
                    {
                        MObject theMObject = thisMObject();
                        adef->setTexture( theMObject, adef->fStringDef, &dgMod);
                    }
                }
            }
        }
    }
    else
    {
        fTexturesByName = texturesByName;
    }
}


// Get cgfxShader version string.
MString
cgfxShaderNode::getPluginVersion()
{
    MString sVer = "cgfxShader ";
    sVer += CGFXSHADER_VERSION;
    sVer += " for Maya ";
    sVer += (int)(MAYA_API_VERSION / 100);
    sVer += ".";
    sVer += (int)(MAYA_API_VERSION % 100 / 10);
    sVer += " (";
    sVer += __DATE__;
    sVer += ")";
    return sVer;
}                                      // cgfxShaderNode::getPluginVersion


// Error reporting
void
cgfxShaderNode::reportInternalError( const char* function, size_t errcode )
{
    MString es = "cgfxShader";

    try
    {
        if ( this &&
            fConstructed )
        {
            if ( ++fErrorCount > fErrorLimit )
                return;
            MString s;
            s += "\"";
            s += name();
            s += "\": ";
            s += typeName();
            es = s;
        }
    }
    catch ( ... )
    {}
    es += " internal error ";
    es += (int)errcode;
    es += " in ";
    es += function;
#ifdef KH_DEBUG
    ::OutputDebugString( es.asChar() );
    ::OutputDebugString( "\n" );
#endif
    MGlobal::displayError( es );
}                                      // cgfxShaderNode::reportInternalError

void
cgfxShaderNode::cgErrorCallBack()
{
    MGlobal::displayInfo(__FUNCTION__);
    CGerror cgLastError = cgGetError();
    if(cgLastError)
    {
        MGlobal::displayError(cgGetErrorString(cgLastError));
        MGlobal::displayError(cgGetLastListing(sCgContext));
    }
}                                                                            // cgfxShaderNode::cgErrorCallBack

void
cgfxShaderNode::cgErrorHandler(CGcontext cgContext, CGerror cgError, void* userData)
{
    MGlobal::displayError(cgGetErrorString(cgError));
    MGlobal::displayError(cgGetLastListing(sCgContext));
}


// ===================================================================================
// viewport 2.0 implementation
// ===================================================================================

const MString cgfxShaderOverride::drawDbClassification("drawdb/shader/surface/cgfxShader");
const MString cgfxShaderOverride::drawRegistrantId("cgfxShaderRegistrantId");

cgfxShaderNode* cgfxShaderOverride::sActiveShaderNode = NULL;
cgfxShaderNode* cgfxShaderOverride::sLastDrawShaderNode = NULL;

MHWRender::MPxShaderOverride* cgfxShaderOverride::Creator(const MObject& obj)
{
    return new cgfxShaderOverride(obj);
}

cgfxShaderOverride::cgfxShaderOverride(const MObject& obj)
    : MPxShaderOverride(obj)
    , fShaderNode(NULL)
    , fNeedPassSetterInit(false)
    , fOldBlendState(NULL)
    , fOldDepthStencilState(NULL)
    , fOldRasterizerState(NULL)
{
}

cgfxShaderOverride::~cgfxShaderOverride()
{
    fShaderNode = NULL;
}

// Initialize phase
MString cgfxShaderOverride::initialize(MObject object)
{
    TRACE_API_CALLS("cgfxShaderOverride::initialize");

    // This is the routine where you would do all the expensive,
    // one-time kind of work.  Create vertex programs, load
    // textures, etc.
    //
    glStateCache::instance().reset();

    // One-time OpenGL initialization...
    if ( glStateCache::sMaxTextureUnits <= 0 )
    {
        // Before this point, we never had a good OpenGL context.  Now
        // we can check for extensions and set up pointers to the
        // extension procs.
#ifdef _WIN32
#define RESOLVE_GL_EXTENSION( fn, ext) wglGetProcAddress( #fn #ext)
#elif defined LINUX
#define RESOLVE_GL_EXTENSION( fn, ext) &fn ## ext
#else
#define RESOLVE_GL_EXTENSION( fn, ext) &fn
#endif

        glStateCache::glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREARBPROC) RESOLVE_GL_EXTENSION( glClientActiveTexture, ARB);
        glStateCache::glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERARBPROC) RESOLVE_GL_EXTENSION( glVertexAttribPointer, ARB);
        glStateCache::glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) RESOLVE_GL_EXTENSION( glEnableVertexAttribArray, ARB);
        glStateCache::glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) RESOLVE_GL_EXTENSION( glDisableVertexAttribArray, ARB);
        glStateCache::glVertexAttrib4f = (PFNGLVERTEXATTRIB4FARBPROC) RESOLVE_GL_EXTENSION( glVertexAttrib4f, ARB);
        glStateCache::glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTEREXTPROC) RESOLVE_GL_EXTENSION( glSecondaryColorPointer, EXT);
        glStateCache::glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FEXTPROC) RESOLVE_GL_EXTENSION( glSecondaryColor3f, EXT);
        glStateCache::glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) RESOLVE_GL_EXTENSION( glMultiTexCoord4f, ARB);

        // Don't use GL_MAX_TEXTURE_UNITS as this does not provide a proper
        // count when the # of image or texcoord inputs differs
        // from the conventional (older) notion of texture unit.
        //
        // Instead take the minimum of GL_MAX_TEXTURE_COORDS_ARB and
        // GL_MAX_TEXUTRE_IMAGE_UNITS_ARB according to the
        // ARB_FRAGMENT_PROGRAM specification.
        //
        GLint tval;
        glGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &tval );
        GLint mic = 0;
        glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &mic );
        if (mic < tval)
            tval = mic;

        // Don't use this...
        //glGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &tval );
        glStateCache::sMaxTextureUnits = tval;
        if (!glStateCache::glClientActiveTexture || glStateCache::sMaxTextureUnits < 1)
            glStateCache::sMaxTextureUnits = 1;
        else if (glStateCache::sMaxTextureUnits > CGFXSHADERNODE_GL_TEXTURE_MAX)
            glStateCache::sMaxTextureUnits = CGFXSHADERNODE_GL_TEXTURE_MAX;
    }

    // Get the effect parameters updated
    //
    if (object != MObject::kNullObj)
    {
        // Get the hardware shader node from the MObject.
        fShaderNode = (cgfxShaderNode *) MPxHwShaderNode::getHwShaderNodePtr( object );
    }
    else
        fShaderNode = NULL;

    if (fShaderNode)
    {
        fShaderNode->createEffect();
        const cgfxEffect& effect = fShaderNode->fNewEffect;
        if (effect.fTechnique && effect.fTechnique->fPasses)
        {
            // Add in geometry requirements based on the attributes being asked for.
            // Note that we can ask for streams on initialize since we have
            // set rebuildAlways() to return true.
            //
            MString sourceName;
            cgfxVertexAttribute* pVertexAttribute = fShaderNode->fVertexAttributes;
            while(pVertexAttribute)
            {
                // Convert UI name into a real geometry name
                //
                sourceName = pVertexAttribute->fSourceName;
                if ( sourceName  == "position")
                {
                    // Positions have no name
                    sourceName = "";
                }
                else if( sourceName  == "normal")
                {
                    // Normals have no name
                    sourceName = "";
                }
                else
                {
                    // Try and pull off the set name
                    MString set = pVertexAttribute->fSourceName;
                    int colon = set.index( ':');
                    if( colon >= 0)
                    {
                        sourceName = set.substring( colon + 1, set.length() - 1);
                        //printf("Parsed : out of [%s] to get [%s]\n", set.asChar(), sourceName.asChar() );
                    }
                }

                MStatus geomReqStatus = MS::kFailure;

                switch(pVertexAttribute->fSourceType)
                {
                    case cgfxVertexAttribute::kPosition:
                        {
                            //printf("Ask for position name = [%s]\n", sourceName.asChar());
                            geomReqStatus = addGeometryRequirement(
                                MHWRender::MVertexBufferDescriptor(
                                    sourceName,
                                    MHWRender::MGeometry::kPosition,
                                    MHWRender::MGeometry::kFloat,
                                    3));
                        }
                        break;
                    case cgfxVertexAttribute::kNormal:
                        {
                            //printf("Ask for normals name = [%s]\n", sourceName.asChar());
                            geomReqStatus = addGeometryRequirement(
                                MHWRender::MVertexBufferDescriptor(
                                    sourceName,
                                    MHWRender::MGeometry::kNormal,
                                    MHWRender::MGeometry::kFloat,
                                    3));
                        }
                        break;
                    case cgfxVertexAttribute::kUV:
                        {
                            //printf("Ask for uvset name = [%s]\n", sourceName.asChar());
                            geomReqStatus = addGeometryRequirement(
                                MHWRender::MVertexBufferDescriptor(
                                    sourceName,
                                    MHWRender::MGeometry::kTexture,
                                    MHWRender::MGeometry::kFloat,
                                    2));
                        }
                        break;
                    case cgfxVertexAttribute::kTangent:
                        {
                            //printf("Ask for tangent name = [%s]\n", sourceName.asChar());
                            geomReqStatus = addGeometryRequirement(
                                MHWRender::MVertexBufferDescriptor(
                                    sourceName,
                                    MHWRender::MGeometry::kTangent,
                                    MHWRender::MGeometry::kFloat,
                                    3));
                        }
                        break;
                    case cgfxVertexAttribute::kBinormal:
                        {
                            //printf("Ask for bitangent name = [%s]\n", sourceName.asChar());
                            geomReqStatus = addGeometryRequirement(
                                MHWRender::MVertexBufferDescriptor(
                                    sourceName,
                                    MHWRender::MGeometry::kBitangent,
                                    MHWRender::MGeometry::kFloat,
                                    3));
                        }
                        break;
                    case cgfxVertexAttribute::kColor:
                        {
                            //printf("Ask for color name = [%s]\n", sourceName.asChar());
                            geomReqStatus = addGeometryRequirement(
                                MHWRender::MVertexBufferDescriptor(
                                    sourceName,
                                    MHWRender::MGeometry::kColor,
                                    MHWRender::MGeometry::kFloat,
                                    4));
                        }
                        break;
                    case cgfxVertexAttribute::kBlindData: //blindData is not supported yet
                    default:;
                }

                if (geomReqStatus != MS::kSuccess) {
                    MString s = "cgfxShader : Can't find the source named \"";
                    s += pVertexAttribute->fSourceName;;
                    s += "\" for vertex attribute \"";
                    s += pVertexAttribute->fName;;
                    s +="\".";
                    MGlobal::displayWarning(s);
                }

                pVertexAttribute = pVertexAttribute->fNext;
            }
        }
    }

    fNeedPassSetterInit = true;

    // FIXME: We probably want to include the timestamp and size of
    // the FX file at time that it was read to uniquely identify the
    // FX.
    //
    // Logged as bug #375613
    MString result =
        (MString("Autodesk Maya cgfxShaderOverride, shader file = ") +
         fShaderNode->shaderFxFile() +
         MString(" technique = ") +
         fShaderNode->getTechnique());

    return result;
}

// Update phase
void cgfxShaderOverride::updateDG(MObject object)
{
    TRACE_API_CALLS("cgfxShaderOverride::updateDG");

    if (object != MObject::kNullObj)
    {
        // Get the hardware shader node from the MObject.
        fShaderNode = (cgfxShaderNode *) MPxHwShaderNode::getHwShaderNodePtr( object );
    }
    else
        fShaderNode = NULL;
}

void cgfxShaderOverride::updateDevice()
{
}

void cgfxShaderOverride::endUpdate()
{
}

// Draw phase
void cgfxShaderOverride::activateKey(MHWRender::MDrawContext& context)
{
    TRACE_API_CALLS("cgfxShaderOverride::activateKey");

    if (!fShaderNode)
    {
        //printf("Failed cgfxShaderOverride::activateKey() - no shader node\n");
        return;
    }

    // We use the Cg technique, pass and parameters from the shader
    // node at activation time. These Cg data structure can be used
    // until termination because all the fShaderNode's involved will
    // share the ame key.
    sActiveShaderNode = fShaderNode;
    sLastDrawShaderNode = NULL;

    cgfxEffect& effect = sActiveShaderNode->fNewEffect;
    if( effect.fTechnique && effect.fTechnique->fPasses)
    {
        // Register VP20 state callbacks for cg pass state
        cgfxPassStateSetter::registerCgStateCallBacks(
            cgfxPassStateSetter::kVP20Viewport);

        MHWRender::MStateManager* stateMgr = context.getStateManager();

        // Now initialize the passes for this effect
        {
            cgfxPass* pass = effect.fTechnique->fPasses;
            while (pass) {
                if (fNeedPassSetterInit || pass->fPassState == NULL) {
                    delete pass->fPassState;
                    pass->fPassState = new cgfxPassStateSetter(stateMgr, pass->fPass);
                }
                pass = pass->fNext;
            }
            fNeedPassSetterInit = false;
        }

        //save render state before rendering
        fOldBlendState = stateMgr->getBlendState();
        fOldDepthStencilState = stateMgr->getDepthStencilState();
        fOldRasterizerState = stateMgr->getRasterizerState();

        glPushClientAttrib ( GL_CLIENT_ALL_ATTRIB_BITS );

        glStateCache::instance().reset();   // the state cache should be reset before draw

        {
            cgfxPass* pass = effect.fTechnique->fPasses;
            if (pass != NULL && pass->fNext == NULL) {
                // For single pass effects, we set the pass state at
                // activation time.
                pass->fPassState->setPassState(stateMgr);
            }
        }
    }
}

bool cgfxShaderOverride::draw(
    MHWRender::MDrawContext& context,
    const MHWRender::MRenderItemList& renderItemList) const
{
    TRACE_API_CALLS("cgfxShaderOverride::draw");

    if (!fShaderNode || !sActiveShaderNode)
    {
        //printf("Failed cgfxShaderOverride::draw() - no shader node\n");
        return false;
    }

    static MGLFunctionTable *gGLFT = 0;
    if ( 0 == gGLFT )
        gGLFT = MHardwareRenderer::theRenderer()->glFunctionTable();

    bool result = true;

    cgfxEffect& effect = sActiveShaderNode->fNewEffect;
    if( effect.fTechnique && effect.fTechnique->fPasses)
    {
        MHWRender::MStateManager* stateMgr = context.getStateManager();

        // Bind non-varying attributes if necessary.
        if (sLastDrawShaderNode != fShaderNode) {
            try
            {
                bindAttrValues();
                checkGlErrors("cgfxShaderOverride::bindAttrValues");
            }
            catch ( cgfxShaderCommon::InternalError* e )
            {
                if (fShaderNode)
                    fShaderNode->reportInternalError( __FILE__, (size_t)e );
            }
            catch ( ... )
            {
                if (fShaderNode)
                    fShaderNode->reportInternalError( __FILE__, __LINE__ );
            }
        }

        // bind varying attributes
        bindViewAttrValues(context);

        const int numRenderItems = renderItemList.length();
        for (int renderItemIdx=0; renderItemIdx<numRenderItems; renderItemIdx++)
        {
            const MHWRender::MRenderItem* renderItem = renderItemList.itemAt(renderItemIdx);
            if (!renderItem) continue;
            const MHWRender::MGeometry* geometry = renderItem->geometry();
            if (!geometry) continue;

            bool boundData = true;
            const int bufferCount = geometry->vertexBufferCount();
            sourceStreamInfo *pBindSource = new sourceStreamInfo[bufferCount];
            for (int i=0; i<bufferCount && boundData; i++)
            {
                const MHWRender::MVertexBuffer* buffer = geometry->vertexBuffer(i);
                if (!buffer)
                {
                    boundData = false;
                    continue;
                }

                const MHWRender::MVertexBufferDescriptor& desc = buffer->descriptor();
                GLuint * dataBufferId = NULL;
                void *dataHandle = buffer->resourceHandle();
                if (!dataHandle)
                {
                    boundData = false;
                    continue;
                }

                dataBufferId = (GLuint *)(dataHandle);

                switch(desc.semantic())
                {
                    case MHWRender::MGeometry::kPosition:
                        {
                            pBindSource[i].fSourceType = cgfxVertexAttribute::kPosition;
                            pBindSource[i].fSourceName = "position";
                        }
                        break;
                    case MHWRender::MGeometry::kNormal:
                        {
                            pBindSource[i].fSourceType = cgfxVertexAttribute::kNormal;
                            pBindSource[i].fSourceName = "normal";
                        }
                        break;
                    case MHWRender::MGeometry::kTexture:
                        {
                            pBindSource[i].fSourceType = cgfxVertexAttribute::kUV;
                            //printf("uv description name is [%s]\n", desc.name().asChar());
                            pBindSource[i].fSourceName = "uv:" + desc.name();
                            //printf("Build uv source name %s\n", pBindSource[i].fSourceName.asChar());
                        }
                        break;
                    case MHWRender::MGeometry::kColor:
                        {
                            pBindSource[i].fSourceType =  cgfxVertexAttribute::kColor;
                            pBindSource[i].fSourceName = "color:" + desc.name();
                        }
                        break;
                    case MHWRender::MGeometry::kTangent:
                        {
                            pBindSource[i].fSourceType = cgfxVertexAttribute::kTangent;
                            pBindSource[i].fSourceName = "tangent:" + desc.name();
                        }
                        break;
                    case MHWRender::MGeometry::kBitangent:
                        {
                            pBindSource[i].fSourceType = cgfxVertexAttribute::kBinormal;
                            pBindSource[i].fSourceName = "binormal:" + desc.name();
                        }
                        break;
                    // blind data is not supported yet!
                    default:
                        {
                            pBindSource[i].fSourceType = cgfxVertexAttribute::kBlindData;
                            pBindSource[i].fSourceName = "";
                        }
                        break;
                }

                pBindSource[i].fDimension = desc.dimension();
                pBindSource[i].fOffset = desc.offset();
                pBindSource[i].fStride = desc.stride();
                pBindSource[i].fElementSize = desc.dataTypeSize();
                pBindSource[i].fDataBufferId = *dataBufferId;
            }

            //draw
            // Dump out indexing information
            if (boundData && geometry->indexBufferCount() > 0)
            {
                // Dump out indexing information
                //
                const MHWRender::MIndexBuffer* buffer = geometry->indexBuffer(0);
                void *indexHandle = buffer->resourceHandle();
                unsigned int indexBufferCount = 0;
                GLuint *indexBufferId = NULL;
                MHWRender::MGeometry::Primitive indexPrimType = renderItem->primitive();
                if (indexHandle)
                {
                    indexBufferId = (GLuint *)(indexHandle);
                    indexBufferCount = buffer->size();
                    /*  if (debugGeometricDrawPrintData)
                        {
                        fprintf(stderr, "IndexingPrimType(%s), IndexType(%s), IndexCount(%d), Handle(%d)\n",
                        MHWRender::MGeometry::primitiveString(indexPrimType).asChar(),
                        MHWRender::MGeometry::dataTypeString(buffer->dataType()).asChar(),
                        indexBufferCount,
                        *indexBufferId);
                        }                       */
                }

                GLenum indexPrimTypeGL = GL_TRIANGLES;
                switch (indexPrimType) {
                    case MHWRender::MGeometry::kPoints:
                        indexPrimTypeGL = GL_POINTS; break;
                    case MHWRender::MGeometry::kLines:
                        indexPrimTypeGL = GL_LINES; break;
                    case MHWRender::MGeometry::kLineStrip:
                        indexPrimTypeGL = GL_LINE_STRIP; break;
                    case MHWRender::MGeometry::kTriangles:
                        indexPrimTypeGL = GL_TRIANGLES; break;
                    case MHWRender::MGeometry::kTriangleStrip:
                        indexPrimTypeGL = GL_TRIANGLE_STRIP; break;
                    default:
                        result = false;
                        break;
                };

                GLenum indexType =
                    ( buffer->dataType() == MHWRender::MGeometry::kUnsignedInt32  ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT );
                if(!result)
                    break;

                if (indexBufferId  && (*indexBufferId > 0))
                {
                    // Now render the passes for this effect
                    cgfxPass* pass = effect.fTechnique->fPasses;

                    if (pass != NULL && pass->fNext == NULL) {
                        // For single pass effect, the pass state has
                        // is set only once.
                        if (sLastDrawShaderNode == NULL) {
                            if (pass->fPassState->isPushPopAttribsRequired()) {
                                gGLFT->glPushAttrib(GL_ALL_ATTRIB_BITS);
                            }
                            cgSetPassState(pass->fPass);
                        }
                        else {
                            cgUpdatePassParameters(pass->fPass);
                        }

                        pass->bind(pBindSource, bufferCount);
                        gGLFT->glBindBufferARB(MGL_ELEMENT_ARRAY_BUFFER_ARB, *indexBufferId);

                        // Now render the passes for this effect
                        gGLFT->glDrawElements(indexPrimTypeGL, indexBufferCount, indexType, GLOBJECT_BUFFER_OFFSET(0));
                    }
                    else {
                        while(pass)
                        {
                            // Update render state for each pass
                            if (pass->fPassState->isPushPopAttribsRequired()) {
                                gGLFT->glPushAttrib(GL_ALL_ATTRIB_BITS);
                            }
                            cgSetPassState(pass->fPass);
                            pass->fPassState->setPassState(stateMgr);

                            pass->bind(pBindSource, bufferCount);
                            gGLFT->glBindBufferARB(MGL_ELEMENT_ARRAY_BUFFER_ARB, *indexBufferId);

                            // Now render the passes for this effect
                            gGLFT->glDrawElements(indexPrimTypeGL, indexBufferCount, indexType, GLOBJECT_BUFFER_OFFSET(0));

                            glStateCache::instance().flushState();
                            cgResetPassState( pass->fPass);
                            if (pass->fPassState->isPushPopAttribsRequired()) {
                                gGLFT->glPopAttrib();
                            }

                            pass = pass->fNext;
                        }
                    }
                }
            }

            delete[] pBindSource;
        }

        checkGlErrors("cgfxShaderOverride::draw");

        sLastDrawShaderNode = fShaderNode;
    }
    else // fEffect must be NULL
    {
        // Setting the result to false means that the plugin
        // cannot perform the render properly.
        result = false;
    }

    return result;
}

void cgfxShaderOverride::terminateKey(MHWRender::MDrawContext& context)
{
    TRACE_API_CALLS("cgfxShaderOverride::terminateKey");

    if (!fShaderNode || !sActiveShaderNode)
    {
        //printf("Failed cgfxShaderOverride::terminateKey() - no shader node\n");
        return;
    }

    cgfxEffect& effect = sActiveShaderNode->fNewEffect;
    if( effect.fTechnique && effect.fTechnique->fPasses)
    {
        MHWRender::MStateManager* stateMgr = context.getStateManager();

        cgfxPass* pass = effect.fTechnique->fPasses;
        if (pass != NULL && pass->fNext == NULL) {
            // For single pass effects, we reset the pass state at
            // termination time.
            glStateCache::instance().flushState();
            cgResetPassState(pass->fPass);
            if (pass->fPassState->isPushPopAttribsRequired()) {
                MGLFunctionTable *gGLFT =
                    MHardwareRenderer::theRenderer()->glFunctionTable();
                gGLFT->glPopAttrib();
            }
        }

        glPopClientAttrib();

        //restore render state after rendering
        stateMgr->setBlendState(fOldBlendState);
        stateMgr->setDepthStencilState(fOldDepthStencilState);
        stateMgr->setRasterizerState(fOldRasterizerState);
        delete fOldBlendState;
        delete fOldDepthStencilState;
        delete fOldRasterizerState;
        fOldBlendState = 0;
        fOldDepthStencilState = 0;
        fOldRasterizerState = 0;
    }

    sActiveShaderNode = NULL;
    sLastDrawShaderNode = NULL;
}

// Override properties
bool cgfxShaderOverride::isTransparent()
{
    if(fShaderNode)
        return fShaderNode->fTechniqueHasBlending;

    return false;
}

void cgfxShaderOverride::bindAttrValues() const
{

    if ( !fShaderNode || !sActiveShaderNode ||
         !sActiveShaderNode->fEffect || !sActiveShaderNode->fTechnique.length() )
        return;

    MStatus  status;
    MObject  oNode = fShaderNode->thisMObject();

    // This method should NEVER access the shape. If you find yourself tempted to access
    // any data from the shape here (like the matrices), be strong and resist! Any shape
    // dependent data should be set in bindAttrViewValues instead!
    //

    for ( cgfxAttrDefList::iterator it( sActiveShaderNode->fAttrDefList ); it; ++it )
    {                                  // loop over fAttrDefList
        cgfxAttrDef* aDef = *it;

        try
        {

            switch (aDef->fType)
            {
            case cgfxAttrDef::kAttrTypeBool:
                {
                    bool tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetParameter1i(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeInt:
                {
                    int tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetParameter1i(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeFloat:
                {
                    float tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetParameter1f(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeString:
                {
                    MString tmp;
                    aDef->getValue(oNode, tmp);
                    cgSetStringParameterValue(aDef->fParameterHandle, tmp.asChar());
                    break;
                }

            case cgfxAttrDef::kAttrTypeVector2:
                {
                    float tmp[2];
                    aDef->getValue(oNode, tmp[0], tmp[1]);
                    cgSetParameter2fv(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeVector3:
            case cgfxAttrDef::kAttrTypeColor3:
                {
                    float tmp[3];
                    aDef->getValue(oNode, tmp[0], tmp[1], tmp[2]);
                    cgSetParameter3fv(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeVector4:
            case cgfxAttrDef::kAttrTypeColor4:
                {
                    float tmp[4];
                    aDef->getValue(oNode, tmp[0], tmp[1], tmp[2], tmp[3]);
                    cgSetParameter4fv(aDef->fParameterHandle, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeWorldDir:
            case cgfxAttrDef::kAttrTypeWorldPos:   //Xuhui: there is a problem:  mat is not computed
                {
                    // Read the value
                    float tmp[4];
                    if (aDef->fSize == 3)
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2]);
                        tmp[3] = 1.0;
                    }
                    else
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2], tmp[3]);
                    }

                    // Find the coordinate space, and whether it is a
                    // point or a vector
                    int base = cgfxAttrDef::kAttrTypeFirstPos;
                    if (aDef->fType <= cgfxAttrDef::kAttrTypeLastDir)
                        base = cgfxAttrDef::kAttrTypeFirstDir;
                    int space = aDef->fType - base;

                    // Compute the transform matrix
                    MMatrix mat;
                    switch (space)
                    {
                        /* case 0:  object space, handled in view dependent method */
                        case 1: /* world space  - do nothing, identity */ break;
                        /* case 2: eye space, unsupported yet */
                        /* case 3: clip space, unsupported yet */
                        /* case 4: screen space, unsupported yet */
                    }

                    if (base == cgfxAttrDef::kAttrTypeFirstPos)
                    {
                        MPoint point(tmp[0], tmp[1], tmp[2], tmp[3]);
                        point *= mat;
                        tmp[0] = (float)point.x;
                        tmp[1] = (float)point.y;
                        tmp[2] = (float)point.z;
                        tmp[3] = (float)point.w;
                    }
                    else
                    {
                        MVector vec(tmp[0], tmp[1], tmp[2]);
                        vec *= mat;
                        tmp[0] = (float)vec.x;
                        tmp[1] = (float)vec.y;
                        tmp[2] = (float)vec.z;
                        tmp[3] = 1.F;
                    }

                    cgSetParameterValuefr(aDef->fParameterHandle, aDef->fSize, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeMatrix:
                {
                    MMatrix tmp;
                    float tmp2[4][4];
                    aDef->getValue(oNode, tmp);

                    if (aDef->fInvertMatrix)
                    {
                        tmp = tmp.inverse();
                    }

                    if (!aDef->fTransposeMatrix)
                    {
                        tmp = tmp.transpose();
                    }

                    tmp.get(tmp2);
                    cgSetMatrixParameterfr(aDef->fParameterHandle, &tmp2[0][0]);
                    break;
                }

            case cgfxAttrDef::kAttrTypeColor1DTexture:
            case cgfxAttrDef::kAttrTypeColor2DTexture:
            case cgfxAttrDef::kAttrTypeColor3DTexture:
            case cgfxAttrDef::kAttrTypeColor2DRectTexture:
            case cgfxAttrDef::kAttrTypeNormalTexture:
            case cgfxAttrDef::kAttrTypeBumpTexture:
            case cgfxAttrDef::kAttrTypeCubeTexture:
            case cgfxAttrDef::kAttrTypeEnvTexture:
            case cgfxAttrDef::kAttrTypeNormalizationTexture:


                {
                    MString tmp;
                    MObject textureNode = MObject::kNullObj;

                    if( fShaderNode->fTexturesByName)
                    {
                        aDef->getValue(oNode, tmp);
                    }
                    else
                    {
                        // If we have a fileTexture node connect, get the
                        // filename it is using
                        MPlug srcPlug;
                        aDef->getSource(oNode, srcPlug);
                        MObject srcNode = srcPlug.node();
                        if( srcNode != MObject::kNullObj)
                        {
                            MStatus rc;
                            MFnDependencyNode dgFn( srcNode);
                            MPlug filenamePlug = dgFn.findPlug( "fileTextureName", &rc);
                            if( rc == MStatus::kSuccess)
                            {
                                filenamePlug.getValue( tmp);
                                textureNode = filenamePlug.node(&rc);
                            }

                            // attach a monitor to this texture if we don't already have one
                            // Note that we don't need to worry about handling node destroyed
                            // or disconnected, as both of these will trigger attribute changed
                            // messages before going away, and we will deregister our callback
                            // in the handler!
                            if( aDef->fTextureMonitor == kNullCallback && textureNode != MObject::kNullObj)
                            {
                                // If we don't have a callback, this may mean our texture is dirty
                                // and needs to be re-loaded (because we can't actually delete the
                                // texture itself in the DG callback we need to wait until we
                                // know we have a GL context - like right here)
                                aDef->releaseTexture();
                                aDef->fTextureMonitor = MNodeMessage::addAttributeChangedCallback( textureNode, textureChangedCallback, aDef);
                            }
                        }
                    }

                    if (aDef->fTextureId == 0 || tmp != aDef->fStringDef)
                    {
                        // If the texture object already exists, reuse
                        // it.  Otherwise, we create a new one.
                        //
                        if (aDef->fTextureId == 0)
                        {
                            GLuint val;
                            glGenTextures(1, &val);
                            aDef->fTextureId = val;
                        }

                        aDef->fStringDef = tmp;

                        nv_dds::CDDSImage image;

                        MString path = cgfxFindFile(tmp);

                        // If that failed, try and resolve the texture path relative to the
                        // effect
                        //
                        if (path.asChar() == NULL || !strcmp(path.asChar(), ""))
                        {
                            MFileObject effectFile;
                            effectFile.setRawFullName( fShaderNode->fShaderFxFile);
                            path = cgfxFindFile( effectFile.path() + tmp);
                        }


                        if (path.asChar() != NULL && strcmp(path.asChar(), ""))
                        {
                            switch (aDef->fType)
                            {
                            case cgfxAttrDef::kAttrTypeEnvTexture:
                            case cgfxAttrDef::kAttrTypeCubeTexture:
                            case cgfxAttrDef::kAttrTypeNormalizationTexture:
                                // we don't want to flip cube maps...
                                image.load(path.asChar(),false);
                                break;
                            default:
                                // we always flip the texture image. This makes the image be GL convention with left-bottom as the origin.
                                // when drawing, we use inverted or non-inverted uv sets based on the texture coordinate system.
                                // inverted for DX texture coordinate system, non-inverted for GL texture coordinate system
                                image.load(path.asChar());
                                break;
                            }
                        }

                        // Our common stand-in "texture"
                        // The code below creates a separate stand-in GL texture
                        // for every attribute without a value (rather than sharing
                        // the default across all node/attributes of a given type.
                        // This is done because the current design does not support
                        // GL texture id sharing across nodes/attributes AND because
                        // we want to avoid checking disk every frame for missing
                        // textures. Once this plugin is re-factored to support a
                        // shared texture cache, we should revisit this to share
                        // default textures too
                        //
                        static unsigned char whitePixel[ 4] = { 255, 255, 255, 255};

                        switch (aDef->fType)
                        {
                        case cgfxAttrDef::kAttrTypeColor1DTexture:
                            glBindTexture(GL_TEXTURE_1D,aDef->fTextureId);
                            if( image.is_valid())
                            {
                                // Load the image
                                glTexParameteri(GL_TEXTURE_1D, GL_GENERATE_MIPMAP_SGIS, image.get_num_mipmaps() == 0);
                                image.upload_texture1D();
                            }
                            else
                            {
                                // Create a dummy stand-in texture
                                glTexImage1D( GL_TEXTURE_1D, 0, GL_RGBA, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                            }
                            break;
                        case cgfxAttrDef::kAttrTypeColor2DTexture:
                        case cgfxAttrDef::kAttrTypeNormalTexture:
                        case cgfxAttrDef::kAttrTypeBumpTexture:
#if !defined(WIN32) && !defined(LINUX)
                        case cgfxAttrDef::kAttrTypeColor2DRectTexture:
#endif
                            glBindTexture(GL_TEXTURE_2D,aDef->fTextureId);
                            if( image.is_valid())
                            {
                                // Load the image
                                glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, image.get_num_mipmaps() == 0);
                                image.upload_texture2D();
                            }
                            else
                            {
                                // Try to use Maya's default file texture loading,
                                // if the DDS loader failed. For now all that
                                // we can support is 2D textures.
                                //
                                if (textureNode != MObject::kNullObj)
                                {
                                    MImage img;
                                    unsigned int width, height;

                                    if (MS::kSuccess == img.readFromTextureNode(textureNode))
                                    {
                                        // we always flip the texture image. This makes the image be GL convention with left-bottom as the origin.
                                        // when drawing, we use inverted or non-inverted uv sets based on the texture coordinate system.
                                        // inverted for DX texture coordinate system, non-inverted for GL texture coordinate system
                                        img.verticalFlip();

                                        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 (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 */);
                                                }
                                            }
                                            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, true);
                                            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels());
                                        }
                                        else
                                        {
                                            // Create a dummy stand-in texture
                                            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
                                                GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                                        }
                                    }
                                    else
                                    {
                                        // Create a dummy stand-in texture
                                        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
                                            GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                                    }
                                }
                                else
                                {
                                    // Create a dummy stand-in texture
                                    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
                                        GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                                }
                            }
                            break;
                        case cgfxAttrDef::kAttrTypeEnvTexture:
                        case cgfxAttrDef::kAttrTypeCubeTexture:
                        case cgfxAttrDef::kAttrTypeNormalizationTexture:
                            {
                                glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, aDef->fTextureId);
                                if( image.is_valid())
                                {
                                    GLenum target;
                                    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_GENERATE_MIPMAP_SGIS, image.get_num_mipmaps() == 0);
                                    // loop through cubemap faces and load them as 2D textures
                                    for (int n = 0; n < 6; ++n)
                                    {
                                        // specify cubemap face
                                        target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB+n;
                                        image.upload_texture2D(image.is_cubemap() ? n : 0, target);
                                    }
                                }
                                // else Add default environment map here if needed
                                break;
                            }
                        case cgfxAttrDef::kAttrTypeColor3DTexture:
                            glBindTexture(GL_TEXTURE_3D,aDef->fTextureId);
                            if( image.is_valid())
                            {
                            image.upload_texture3D();
                            }
                            break;
#if defined(WIN32) || defined(LINUX)
                            // No such thing as NV texture rectangle
                            // on Mac.
                        case cgfxAttrDef::kAttrTypeColor2DRectTexture:
                            glBindTexture(GL_TEXTURE_RECTANGLE_NV, aDef->fTextureId);
                            if( image.is_valid())
                            {
                                // Load the image
                                image.upload_textureRectangle();
                            }
                            else
                            {
                                // Create a dummy stand-in texture
                                glTexImage2D( GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, whitePixel);
                            }
                            break;
#endif
                        default:
                            assert(false);
                        }
                    }
                    checkGlErrors("After loading texture");
                    cgGLSetupSampler(aDef->fParameterHandle, aDef->fTextureId);

                    break;
                }
#ifdef _WIN32
            case cgfxAttrDef::kAttrTypeTime:
                {
                    int ival = timeGetTime() & 0xffffff;
                    float val = (float)ival * 0.001f;
                    cgSetParameter1f(aDef->fParameterHandle, val);
                    break;
                }
#endif
            case cgfxAttrDef::kAttrTypeOther:
            case cgfxAttrDef::kAttrTypeUnknown:
                break;

            case cgfxAttrDef::kAttrTypeObjectDir:
            case cgfxAttrDef::kAttrTypeViewDir:
            case cgfxAttrDef::kAttrTypeProjectionDir:
            case cgfxAttrDef::kAttrTypeScreenDir:

            case cgfxAttrDef::kAttrTypeObjectPos:
            case cgfxAttrDef::kAttrTypeViewPos:
            case cgfxAttrDef::kAttrTypeProjectionPos:
            case cgfxAttrDef::kAttrTypeScreenPos:

            case cgfxAttrDef::kAttrTypeWorldMatrix:
            case cgfxAttrDef::kAttrTypeViewMatrix:
            case cgfxAttrDef::kAttrTypeProjectionMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewProjectionMatrix:
                // View dependent parameter
                break;
            default:
                M_CHECK( false );
            }                          // switch (aDef->fType)
        }
        catch ( cgfxShaderCommon::InternalError* e )
        {
            if ( ++fShaderNode->fErrorCount <= fShaderNode->fErrorLimit )
            {
                size_t ee = (size_t)e;
                MFnDependencyNode fnNode( oNode );
                MString sMsg = "cgfxShader warning ";
                sMsg += (int)ee;
                sMsg += ": ";
                sMsg += fnNode.name();
                sMsg += " internal error while setting parameter \"";
                sMsg += aDef->fName;
                sMsg += "\" of effect \"";
                sMsg += fShaderNode->fShaderFxFile;
                sMsg += "\" for shape ";
                sMsg += fShaderNode->currentPath().partialPathName();
                MGlobal::displayWarning( sMsg );
            }
        }
    }                                  // loop over fAttrDefList
}

void cgfxShaderOverride::bindViewAttrValues(const MHWRender::MDrawContext& context) const
{
    if ( !fShaderNode || !sActiveShaderNode ||
         !sActiveShaderNode->fEffect || !sActiveShaderNode->fTechnique.length() )
        return;

    MStatus  status;
    MObject  oNode = fShaderNode->thisMObject();

    MFloatMatrix wMatrix, vMatrix, pMatrix, sMatrix;
    MFloatMatrix wvMatrix, wvpMatrix, wvpsMatrix;
    MFloatMatrix vpMatrix, vpsMatrix;
    {
        wvpMatrix = context.getMatrix(MHWRender::MDrawContext::kWorldViewProjMtx);
        wvMatrix = context.getMatrix(MHWRender::MDrawContext::kWorldViewMtx);
        wMatrix = context.getMatrix(MHWRender::MDrawContext::kWorldMtx);
        vMatrix = context.getMatrix(MHWRender::MDrawContext::kViewMtx);
        pMatrix = context.getMatrix(MHWRender::MDrawContext::kProjectionMtx);
        vpMatrix = context.getMatrix(MHWRender::MDrawContext::kViewProjMtx);

        int vpt[4];
        float depth[2];

        context.getViewportDimensions(vpt[0], vpt[1], vpt[2], vpt[3]);
        context.getDepthRange(depth[0], depth[1]);

        // Construct the NDC -> screen space matrix
        //
        float x0, y0, z0, w, h, d;

        x0 = (float)vpt[0];
        y0 = (float)vpt[1];
        z0 = depth[0];
        w  = (float)vpt[2];
        h  = (float)vpt[3];
        d  = depth[1] - z0;

        // Make a reference to ease the typing
        //
        float* s = &sMatrix.matrix[0][0];

        s[ 0] = w/2;    s[ 1] = 0.0;    s[ 2] = 0.0;    s[ 3] = 0.0;
        s[ 4] = 0.0;    s[ 5] = h/2;    s[ 6] = 0.0;    s[ 7] = 0.0;
        s[ 8] = 0.0;    s[ 9] = 0.0;    s[10] = d/2;    s[11] = 0.0;
        s[12] = x0+w/2; s[13] = y0+h/2; s[14] = z0+d/2; s[15] = 1.0;

        vpsMatrix = vpMatrix * sMatrix;
        wvpsMatrix = wvpMatrix * sMatrix;
    }

    for ( cgfxAttrDefList::iterator it( sActiveShaderNode->fAttrDefList ); it; ++it )
    {                                  // loop over fAttrDefList
        cgfxAttrDef* aDef = *it;

        try
        {

            switch (aDef->fType)
            {
            case cgfxAttrDef::kAttrTypeObjectDir:
            case cgfxAttrDef::kAttrTypeViewDir:
            case cgfxAttrDef::kAttrTypeProjectionDir:
            case cgfxAttrDef::kAttrTypeScreenDir:

            case cgfxAttrDef::kAttrTypeObjectPos:
            case cgfxAttrDef::kAttrTypeViewPos:
            case cgfxAttrDef::kAttrTypeProjectionPos:
            case cgfxAttrDef::kAttrTypeScreenPos:
                {
                    float tmp[4];
                    if (aDef->fSize == 3)
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2]);
                        tmp[3] = 1.0;
                    }
                    else
                    {
                        aDef->getValue(oNode, tmp[0], tmp[1], tmp[2], tmp[3]);
                    }

                    // Maya's API only provides for vectors of size 3.
                    // When we do the matrix multiply, it will only
                    // work correctly if the 4th coordinate is 1.0
                    //

                    MFloatVector vec(tmp[0], tmp[1], tmp[2]);

                    int space = aDef->fType - cgfxAttrDef::kAttrTypeFirstPos;
                    if (space < 0)
                    {
                        space = aDef->fType - cgfxAttrDef::kAttrTypeFirstDir;
                    }

                    MFloatMatrix mat;  // initially the identity matrix.

                    switch (space)
                    {
                    case 0: mat = wMatrix.inverse();    break;
                    case 1: /*mat = identity;*/         break;
                    case 2: mat = vMatrix;          break;
                    case 3: mat = vpMatrix;     break;
                    case 4: mat = vpsMatrix;        break;
                    }

                    // Maya's transformation matrices are set up with
                    // the translation in row 3 (like OpenGL) rather
                    // than column 3. To transform a point or vector,
                    // use V*M, not M*V.   kh 11/2003
                    int base = cgfxAttrDef::kAttrTypeFirstPos;
                    if (aDef->fType <= cgfxAttrDef::kAttrTypeLastDir)
                        base = cgfxAttrDef::kAttrTypeFirstDir;
                    if (base == cgfxAttrDef::kAttrTypeFirstPos)
                    {
                        MFloatPoint point(tmp[0], tmp[1], tmp[2], tmp[3]);
                        point *= mat;
                        tmp[0] = point.x;
                        tmp[1] = point.y;
                        tmp[2] = point.z;
                        tmp[3] = point.w;
                    }
                    else
                    {
                        MFloatVector vec(tmp[0], tmp[1], tmp[2]);
                        vec *= mat;
                        tmp[0] = vec.x;
                        tmp[1] = vec.y;
                        tmp[2] = vec.z;
                        tmp[3] = 1.F;
                    }

                    cgSetParameterValuefc(aDef->fParameterHandle, aDef->fSize, tmp);
                    break;
                }

            case cgfxAttrDef::kAttrTypeWorldMatrix:
            case cgfxAttrDef::kAttrTypeViewMatrix:
            case cgfxAttrDef::kAttrTypeProjectionMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewMatrix:
            case cgfxAttrDef::kAttrTypeWorldViewProjectionMatrix:
                {
                    MHWRender::MDrawContext::MatrixType matrixType;
                    switch (aDef->fType)
                    {
                    case cgfxAttrDef::kAttrTypeWorldMatrix:
                        if(aDef->fInvertMatrix && !aDef->fTransposeMatrix)  matrixType = MHWRender::MDrawContext::kWorldTranspInverseMtx;
                        else if(aDef->fInvertMatrix) matrixType = MHWRender::MDrawContext::kWorldInverseMtx;
                        else if(!aDef->fTransposeMatrix) matrixType = MHWRender::MDrawContext::kWorldTransposeMtx;
                        else matrixType = MHWRender::MDrawContext::kWorldMtx;
                        break;
                    case cgfxAttrDef::kAttrTypeViewMatrix:
                        if(aDef->fInvertMatrix && !aDef->fTransposeMatrix)  matrixType = MHWRender::MDrawContext::kViewTranspInverseMtx;
                        else if(aDef->fInvertMatrix) matrixType = MHWRender::MDrawContext::kViewInverseMtx;
                        else if(!aDef->fTransposeMatrix) matrixType = MHWRender::MDrawContext::kViewTransposeMtx;
                        else matrixType = MHWRender::MDrawContext::kViewMtx;
                        break;
                    case cgfxAttrDef::kAttrTypeProjectionMatrix:
                        if(aDef->fInvertMatrix && !aDef->fTransposeMatrix)  matrixType = MHWRender::MDrawContext::kProjectionTranspInverseMtx;
                        else if(aDef->fInvertMatrix) matrixType = MHWRender::MDrawContext::kProjectionInverseMtx;
                        else if(!aDef->fTransposeMatrix) matrixType = MHWRender::MDrawContext::kProjectionTranposeMtx;
                        else matrixType = MHWRender::MDrawContext::kProjectionMtx;
                        break;
                    case cgfxAttrDef::kAttrTypeWorldViewMatrix:
                        if(aDef->fInvertMatrix && !aDef->fTransposeMatrix)  matrixType = MHWRender::MDrawContext::kWorldViewTranspInverseMtx;
                        else if(aDef->fInvertMatrix) matrixType = MHWRender::MDrawContext::kWorldViewInverseMtx;
                        else if(!aDef->fTransposeMatrix) matrixType = MHWRender::MDrawContext::kWorldViewTransposeMtx;
                        else matrixType = MHWRender::MDrawContext::kWorldViewMtx;
                        break;
                    case cgfxAttrDef::kAttrTypeWorldViewProjectionMatrix:
                        if(aDef->fInvertMatrix && !aDef->fTransposeMatrix)  matrixType = MHWRender::MDrawContext::kWorldViewProjTranspInverseMtx;
                        else if(aDef->fInvertMatrix) matrixType = MHWRender::MDrawContext::kWorldViewProjInverseMtx;
                        else if(!aDef->fTransposeMatrix) matrixType = MHWRender::MDrawContext::kWorldViewProjTransposeMtx;
                        else matrixType = MHWRender::MDrawContext::kWorldViewProjMtx;
                        break;
                    default:
                        matrixType = MHWRender::MDrawContext::kWorldMtx;
                        break;
                    }

                    MFloatMatrix mat = context.getMatrix(matrixType);

                    float tmp[4][4];
                    mat.get(tmp);
                    cgSetMatrixParameterfr(aDef->fParameterHandle, &tmp[0][0]);
                    break;
                }
                default:
                    break;
            }                          // switch (aDef->fType)
        }
        catch ( cgfxShaderCommon::InternalError* e )
        {
            if ( ++fShaderNode->fErrorCount <= fShaderNode->fErrorLimit )
            {
                size_t ee = (size_t)e;
                MFnDependencyNode fnNode( oNode );
                MString sMsg = "cgfxShader warning ";
                sMsg += (int)ee;
                sMsg += ": ";
                sMsg += fnNode.name();
                sMsg += " internal error while setting parameter \"";
                sMsg += aDef->fName;
                sMsg += "\" of effect \"";
                sMsg += fShaderNode->fShaderFxFile;
                sMsg += "\" for shape ";
                /*if (shapePath.isValid())
                    sMsg += shapePath.partialPathName();
                else
                    sMsg += "SWATCH GEOMETRY";*/
                MGlobal::displayWarning( sMsg );
            }
        }
    }                                  // loop over fAttrDefList

}