hwColorPerVertexShader.cpp

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

//
// NOTE: PLEASE READ THE README.TXT FILE FOR INSTRUCTIONS ON
// COMPILING AND USAGE REQUIREMENTS.
//
// DESCRIPTION:
//      This is an example of drawing an object using
//      the stored color per vertex, or using false
//      coloring of either : the nornmals, the tangents,
//      or the binormals.
//

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

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

#include <maya/MString.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFloatVector.h>
#include <maya/MStringArray.h>
#include <maya/MFnPlugin.h>
#include <maya/MPxHwShaderNode.h>
#include <maya/MStringArray.h>
#include <maya/MFnMesh.h>
#include <maya/MGlobal.h>
#include <maya/MFloatPoint.h>
#include <maya/MFloatPointArray.h>

// For swatch rendering
#include <maya/MHardwareRenderer.h>
#include <maya/MGeometryData.h>

// Viewport 2.0 includes
#include <maya/MDrawRegistry.h>
#include <maya/MPxShaderOverride.h>
#include <maya/MDrawContext.h>
#include <maya/MStateManager.h>
#include <maya/MGLdefinitions.h>
#include <maya/MGLFunctionTable.h>
#include <maya/MHWGeometryUtilities.h>


#include <maya/MHWShaderSwatchGenerator.h>

#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif

static void drawBoundingBox( const MBoundingBox& box, const MColor& color )
{
    MPoint bmin = box.min();
    MPoint bmax = box.max();
    MFloatPoint min( float(bmin.x), float(bmin.y), float(bmin.z) );
    MFloatPoint max( float(bmax.x), float(bmax.y), float(bmax.z) );

    glPushAttrib(GL_ALL_ATTRIB_BITS);
    {
        glDisable( GL_LIGHTING );
        glColor3f( color.r, color.g, color.b);
        glBegin( GL_LINE_STRIP );
        glVertex3f( min.x, min.y, min.z );
        glVertex3f( max.x, min.y, min.z );
        glVertex3f( max.x, max.y, min.z );
        glVertex3f( min.x, max.y, min.z );
        glVertex3f( min.x, min.y, min.z );
        glVertex3f( min.x, min.y, max.z );
        glVertex3f( min.x, max.y, max.z );
        glVertex3f( min.x, max.y, min.z );
        glVertex3f( max.x, max.y, min.z );
        glVertex3f( max.x, max.y, max.z );
        glVertex3f( max.x, min.y, max.z );
        glVertex3f( max.x, min.y, min.z );
        glVertex3f( max.x, min.y, max.z );
        glVertex3f( min.x, min.y, max.z );
        glVertex3f( min.x, max.y, max.z );
        glVertex3f( max.x, max.y, max.z );
        glEnd();
    }
    glPopAttrib();

}

static const MString sHWCPVShaderRegistrantId("HWCPVShaderRegistrantId");

class hwColorPerVertexShader : public MPxHwShaderNode
{
    public:
                    hwColorPerVertexShader();
    virtual         ~hwColorPerVertexShader();

    virtual MStatus compute( const MPlug&, MDataBlock& );
    virtual void    postConstructor();

    // Internally cached attribute handling routines
    virtual bool getInternalValueInContext( const MPlug&,
                                              MDataHandle&,
                                              MDGContext&);
    virtual bool setInternalValueInContext( const MPlug&,
                                              const MDataHandle&,
                                              MDGContext&);

    virtual MStatus     bind( const MDrawRequest& request,
                              M3dView& view );
    virtual MStatus     unbind( const MDrawRequest& request,
                                M3dView& view );
    virtual MStatus     geometry( const MDrawRequest& request,
                                M3dView& view,
                                int prim,
                                unsigned int writable,
                                int indexCount,
                                const unsigned int * indexArray,
                                int vertexCount,
                                const int * vertexIDs,
                                const float * vertexArray,
                                int normalCount,
                                const float ** normalArrays,
                                int colorCount,
                                const float ** colorArrays,
                                int texCoordCount,
                                const float ** texCoordArrays);

    // Batch overrides
    virtual MStatus glBind(const MDagPath& shapePath);
    virtual MStatus glUnbind(const MDagPath& shapePath);
    virtual MStatus glGeometry( const MDagPath&,
                              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);

    // Overridden to draw an image for swatch rendering.
    virtual MStatus renderSwatchImage( MImage & image );
    void            drawTheSwatch( MGeometryData* pGeomData,
                                   unsigned int* pIndexing,
                                   unsigned int  numberOfData,
                                   unsigned int  indexCount );
    MStatus draw( int prim,
                    unsigned int writable,
                    int indexCount,
                    const unsigned int * indexArray,
                    int vertexCount,
                    const float * vertexArray,
                    int colorCount,
                    const float ** colorArrays);


    virtual int     colorsPerVertex();
    virtual bool    hasTransparency();
    virtual int     normalsPerVertex();
    virtual int     texCoordsPerVertex();

    const MString&  colorSetName()const{return mColorSetName;}

    static  void *  creator();
    static  MStatus initialize();

    static  MTypeId id;

    bool            wantDrawBoundingBox() const { return mDrawBoundingBox; }

    private:
    void getFloat3(MObject attr, MFloatVector & val) const;
    void getFloat(MObject attr, float & val) const;

    // Attributes
    static MObject  aColorGain;
    static MObject  aColorBias;
    static MObject  aTranspGain;
    static MObject  aTranspBias;
    static MObject  aNormalsPerVertex;
    static MObject  aColorsPerVertex;
    static MObject  aColorSetName;
    static MObject  aTexRotateX;
    static MObject  aTexRotateY;
    static MObject  aTexRotateZ;
    static MObject  aDrawBoundingBox;

    // Cached internal values
    float3  mColorGain;
    float3  mColorBias;
    float   mTranspGain;
    float   mTranspBias;

    unsigned int mNormalsPerVertex;
    unsigned int mColorsPerVertex;
    MString mColorSetName;
    float   mTexRotateX;
    float   mTexRotateY;
    float   mTexRotateZ;

    bool    mDrawBoundingBox;

    MImage  *mSampleImage;
    GLuint  mSampleImageId;

    bool    mAttributesChanged;
};

MTypeId hwColorPerVertexShader::id( 0x00105450 );

MObject  hwColorPerVertexShader::aColorGain;
MObject  hwColorPerVertexShader::aTranspGain;
MObject  hwColorPerVertexShader::aColorBias;
MObject  hwColorPerVertexShader::aTranspBias;
MObject  hwColorPerVertexShader::aNormalsPerVertex;
MObject  hwColorPerVertexShader::aColorsPerVertex;
MObject  hwColorPerVertexShader::aColorSetName;
MObject  hwColorPerVertexShader::aTexRotateX;
MObject  hwColorPerVertexShader::aTexRotateY;
MObject  hwColorPerVertexShader::aTexRotateZ;
MObject  hwColorPerVertexShader::aDrawBoundingBox;

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

hwColorPerVertexShader::hwColorPerVertexShader()
{
    mColorGain[0] = mColorGain[1] = mColorGain[2] = 1.0f;
    mColorBias[0] = mColorBias[1] = mColorBias[2] = 0.0f;
    mTranspGain = 1.0f;
    mTranspBias = 0.0f;
    mAttributesChanged = false;
    mNormalsPerVertex = 0;
    mColorsPerVertex = 0;
    mSampleImage = 0;
    mSampleImageId = 0;
    mTexRotateX = 0.0f;
    mTexRotateY = 0.0f;
    mTexRotateZ = 0.0f;
    mDrawBoundingBox = false;
}

hwColorPerVertexShader::~hwColorPerVertexShader()
{
    if ( mSampleImageId > 0 )
        glDeleteTextures(1, &mSampleImageId );
}

// The creator method creates an instance of the
// hwColorPerVertexShader class and is the first method called by Maya
// when a hwColorPerVertexShader needs to be created.
//
void * hwColorPerVertexShader::creator()
{
    return new hwColorPerVertexShader();
}


/* virtual */
bool
hwColorPerVertexShader::setInternalValueInContext( const MPlug& plug,
                                                  const MDataHandle& handle,
                                                  MDGContext&)
{
    bool handledAttribute = false;
    if (plug == aNormalsPerVertex)
    {
        handledAttribute = true;
        mNormalsPerVertex = (unsigned int) handle.asInt();
    }
    else if (plug == aColorsPerVertex)
    {
        handledAttribute = true;
        mColorsPerVertex = (unsigned int) handle.asInt();
    }
    else if (plug == aColorSetName)
    {
        handledAttribute = true;
        mColorSetName = (MString) handle.asString();
    }
    else if (plug == aTexRotateX)
    {
        handledAttribute = true;
        mTexRotateX = handle.asFloat();
    }
    else if (plug == aTexRotateY)
    {
        handledAttribute = true;
        mTexRotateY = handle.asFloat();
    }
    else if (plug == aTexRotateZ)
    {
        handledAttribute = true;
        mTexRotateZ = handle.asFloat();
    }

    else if (plug == aColorGain)
    {
        handledAttribute = true;
        float3 & val = handle.asFloat3();
        if (val[0] != mColorGain[0] ||
            val[1] != mColorGain[1] ||
            val[2] != mColorGain[2])
        {
            mColorGain[0] = val[0];
            mColorGain[1] = val[1];
            mColorGain[2] = val[2];
            mAttributesChanged = true;
        }
    }
    else if (plug == aColorBias)
    {
        handledAttribute = true;
        float3 &val = handle.asFloat3();
        if (val[0] != mColorBias[0] ||
            val[1] != mColorBias[1] ||
            val[2] != mColorBias[2])
        {
            mColorBias[0] = val[0];
            mColorBias[1] = val[1];
            mColorBias[2] = val[2];
            mAttributesChanged = true;
        }
    }
    else if (plug == aTranspGain)
    {
        handledAttribute = true;
        float val = handle.asFloat();
        if (val != mTranspGain)
        {
            mTranspGain = val;
            mAttributesChanged = true;
        }
    }
    else if (plug == aTranspBias)
    {
        handledAttribute = true;
        float val = handle.asFloat();
        if (val != mTranspBias)
        {
            mTranspBias = val;
            mAttributesChanged = true;
        }
    }
    else if( plug == aDrawBoundingBox )
    {
        handledAttribute = true;
        bool val = handle.asBool();
        if( val != mDrawBoundingBox )
        {
            mDrawBoundingBox = val;
            mAttributesChanged = true;
        }
    }

    return handledAttribute;
}

/* virtual */
bool
hwColorPerVertexShader::getInternalValueInContext( const MPlug& plug,
                                              MDataHandle& handle,
                                              MDGContext&)
{
    bool handledAttribute = false;
    if (plug == aColorGain)
    {
        handledAttribute = true;
        handle.set( mColorGain[0], mColorGain[1], mColorGain[2] );
    }
    else if (plug == aColorBias)
    {
        handledAttribute = true;
        handle.set( mColorBias[0], mColorBias[1], mColorBias[2] );
    }
    else if (plug == aTranspGain)
    {
        handledAttribute = true;
        handle.set( mTranspGain );
    }
    else if (plug == aTranspBias)
    {
        handledAttribute = true;
        handle.set( mTranspBias );
    }
    else if (plug == aNormalsPerVertex)
    {
        handledAttribute = true;
        handle.set( (int)mNormalsPerVertex );
    }
    else if (plug == aColorsPerVertex)
    {
        handledAttribute = true;
        handle.set( (int)mColorsPerVertex );
    }
    else if (plug == aColorSetName)
    {
        handledAttribute = true;
        handle.set( mColorSetName );
    }
    else if (plug == aTexRotateX)
    {
        handledAttribute = true;
        handle.set( mTexRotateX );
    }
    else if (plug == aTexRotateY)
    {
        handledAttribute = true;
        handle.set( mTexRotateY );
    }
    else if (plug == aTexRotateZ)
    {
        handledAttribute = true;
        handle.set( mTexRotateZ );
    }
    else if( plug == aDrawBoundingBox )
    {
        handledAttribute = true;
        handle.set( mDrawBoundingBox );
    }

    return handledAttribute;
}

// The initialize routine is called after the node has been created.
// It sets up the input and output attributes and adds them to the node.
// Finally the dependencies are arranged so that when the inputs
// change Maya knowns to call compute to recalculate the output values.
//
MStatus hwColorPerVertexShader::initialize()
{
    MFnNumericAttribute nAttr;
    MFnTypedAttribute tAttr;
    MStatus status;

    // Create input attributes.
    // All attributes are cached internal
    //
    aColorGain = nAttr.createColor( "colorGain", "cg", &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(1.f, 1.f, 1.f));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aTranspGain = nAttr.create("transparencyGain", "tg",
                          MFnNumericData::kFloat, 1.f, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(1.f));
    CHECK_MSTATUS( nAttr.setSoftMin(0.f));
    CHECK_MSTATUS( nAttr.setSoftMax(2.f));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aColorBias = nAttr.createColor( "colorBias", "cb", &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(0.f, 0.f, 0.f));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aTranspBias = nAttr.create( "transparencyBias", "tb",
                           MFnNumericData::kFloat, 0.f, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(0.f));
    CHECK_MSTATUS( nAttr.setSoftMin(-1.f));
    CHECK_MSTATUS( nAttr.setSoftMax(1.f));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aNormalsPerVertex = nAttr.create("normalsPerVertex", "nv",
        MFnNumericData::kInt, 0, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(false));
    CHECK_MSTATUS( nAttr.setDefault(0));
    CHECK_MSTATUS( nAttr.setSoftMin(0));
    CHECK_MSTATUS( nAttr.setSoftMax(3));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aColorsPerVertex = nAttr.create("colorsPerVertex", "cv",
        MFnNumericData::kInt, 0, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(false));
    CHECK_MSTATUS( nAttr.setDefault(0));
    CHECK_MSTATUS( nAttr.setSoftMin(0));
    CHECK_MSTATUS( nAttr.setSoftMax(5));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aColorSetName = tAttr.create("colorSetName", "cs",
        MFnData::kString, MObject::kNullObj, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( tAttr.setStorable(true));
    CHECK_MSTATUS( tAttr.setKeyable(false));
    tAttr.setCached( true );
    tAttr.setInternal( true );
    tAttr.setAffectsAppearance( true );

    aTexRotateX = nAttr.create( "texRotateX", "tx",
                           MFnNumericData::kFloat, 0.f, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(0.f));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aTexRotateY = nAttr.create( "texRotateY", "ty",
                           MFnNumericData::kFloat, 0.f, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(0.f));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aTexRotateZ = nAttr.create( "texRotateZ", "tz",
                           MFnNumericData::kFloat, 0.f, &status);
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(0.f));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    aDrawBoundingBox = nAttr.create( "drawBoundingBox", "dbb",
                            MFnNumericData::kBoolean, false, &status );
    CHECK_MSTATUS( status );
    CHECK_MSTATUS( nAttr.setStorable(true));
    CHECK_MSTATUS( nAttr.setKeyable(true));
    CHECK_MSTATUS( nAttr.setDefault(false));
    nAttr.setCached( true );
    nAttr.setInternal( true );
    nAttr.setAffectsAppearance( true );

    // create output attributes here
    // outColor is the only output attribute and it is inherited
    // so we do not need to create or add it.
    //

    // Add the attributes here

    CHECK_MSTATUS( addAttribute(aColorGain));
    CHECK_MSTATUS( addAttribute(aTranspGain));
    CHECK_MSTATUS( addAttribute(aColorBias));
    CHECK_MSTATUS( addAttribute(aTranspBias));
    CHECK_MSTATUS( addAttribute(aNormalsPerVertex));
    CHECK_MSTATUS( addAttribute(aColorsPerVertex));
    CHECK_MSTATUS( addAttribute(aColorSetName));
    CHECK_MSTATUS( addAttribute(aTexRotateX));
    CHECK_MSTATUS( addAttribute(aTexRotateY));
    CHECK_MSTATUS( addAttribute(aTexRotateZ));
    CHECK_MSTATUS( addAttribute(aDrawBoundingBox));

    CHECK_MSTATUS( attributeAffects (aColorGain,  outColor));
    CHECK_MSTATUS( attributeAffects (aTranspGain, outColor));
    CHECK_MSTATUS( attributeAffects (aColorBias,  outColor));
    CHECK_MSTATUS( attributeAffects (aTranspBias, outColor));
    CHECK_MSTATUS( attributeAffects (aNormalsPerVertex, outColor));
    CHECK_MSTATUS( attributeAffects (aColorsPerVertex, outColor));
    CHECK_MSTATUS( attributeAffects (aColorSetName, outColor));
    CHECK_MSTATUS( attributeAffects (aTexRotateX, outColor));
    CHECK_MSTATUS( attributeAffects (aTexRotateY, outColor));
    CHECK_MSTATUS( attributeAffects (aTexRotateZ, outColor));
    CHECK_MSTATUS( attributeAffects (aDrawBoundingBox, outColor));

    return MS::kSuccess;
}

/* virtual */
MStatus
hwColorPerVertexShader::bind( const MDrawRequest& request,
                              M3dView& view )
{
    return MS::kSuccess;
}

/* virtual */
MStatus
hwColorPerVertexShader::unbind( const MDrawRequest& request,
                                M3dView& view )
{
    return MS::kSuccess;
}


/* virtual */
MStatus
hwColorPerVertexShader::geometry( const MDrawRequest& request,
                            M3dView& view,
                            int prim,
                            unsigned int writable,
                            int indexCount,
                            const unsigned int * indexArray,
                            int vertexCount,
                            const int * vertexIDs,
                            const float * vertexArray,
                            int normalCount,
                            const float ** normalArrays,
                            int colorCount,
                            const float ** colorArrays,
                            int texCoordCount,
                            const float ** texCoordArrays)
{
    if( mDrawBoundingBox )
    {
        // Compute the bounding box.
        int i;
        MBoundingBox box;
        for( i = 0; i < vertexCount; i++ )
        {
            box.expand( MPoint(vertexArray[i*3], vertexArray[i*3+1], vertexArray[i*3+2]) );
        }

        // Draw the bounding box.
        MColor wireColor( 0.1f, 0.15f, 0.35f );
        drawBoundingBox( box, wireColor );
    }

    // If we're received a color, that takes priority
    //
    if( colorCount > 0 && colorArrays[ colorCount - 1] != NULL )
    {
        glPushAttrib(GL_ALL_ATTRIB_BITS);
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
        glDisable(GL_LIGHTING);

        glEnableClientState(GL_COLOR_ARRAY);
        glColorPointer( 4, GL_FLOAT, 0, &colorArrays[colorCount - 1][0]);

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer ( 3, GL_FLOAT, 0, &vertexArray[0] );
        glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );

        glEnableClientState(GL_COLOR_ARRAY);

        glPopClientAttrib();
        glPopAttrib();

        return MS::kSuccess;
    }
    // If this attribute is enabled, normals, tangents,
    // and binormals can be visualized using false coloring.
    // Negative values will clamp to black however.
    if ((unsigned int)normalCount > mNormalsPerVertex) {
        normalCount = mNormalsPerVertex;
        return MS::kSuccess;
    }
    else if (normalCount > 0)
    {
        glPushAttrib(GL_ALL_ATTRIB_BITS);
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
        glDisable(GL_LIGHTING);

        if (normalCount > 1)
        {
            if (normalCount == 2)
            {
    #ifdef _TANGENT_DEBUG
                const float *tangents = (const float *)&normalArrays[1][0];
                for (unsigned int  i=0; i< vertexCount; i++)
                {
                    cout<<"tangent["<<i
                        <<"] = "<<tangnets[i*3]
                        <<","<<tangents[i*3+1]<<","<<tangents[i*3+2]<<endl;
                }
    #endif
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(3, GL_FLOAT, 0, &normalArrays[1][0]);
            }
            else
            {
    #ifdef _BINORMAL_DEBUG
                const float *binormals = (const float *)&normalArrays[2][0];
                for (unsigned int  i=0; i< vertexCount; i++)
                {
                    cout<<"binormals["<<i<<"] = "<<binormals[i*3]
                        <<","<<binormals[i*3+1]<<","<<binormals[i*3+2]<<endl;
                }
    #endif
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(3, GL_FLOAT, 0, &normalArrays[2][0]);
            }
        }
        else if (normalCount)
        {
            glEnableClientState(GL_COLOR_ARRAY);
            glColorPointer(3, GL_FLOAT, 0, &normalArrays[0][0]);
        }

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer ( 3, GL_FLOAT, 0, &vertexArray[0] );
        glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );

        glDisableClientState(GL_COLOR_ARRAY);

        glPopClientAttrib();
        glPopAttrib();

        return MS::kSuccess;
    }
    else
    {
        return draw( prim, writable, indexCount, indexArray,
            vertexCount, vertexArray, colorCount, colorArrays );
    }
}


// Batch overrides
/* virtual */
MStatus
hwColorPerVertexShader::glBind(const MDagPath& shapePath)
{
    return MS::kSuccess;
}

/* virtual */
MStatus
hwColorPerVertexShader::glUnbind(const MDagPath& shapePath)
{
    return MS::kSuccess;
}

/* virtual */
MStatus hwColorPerVertexShader::glGeometry( const MDagPath& path,
                                int prim,
                                unsigned int writable,
                                int indexCount,
                                const unsigned int * indexArray,
                                int vertexCount,
                                const int * vertexIDs,
                                const float * vertexArray,
                                int normalCount,
                                const float ** normalArrays,
                                int colorCount,
                                const float ** colorArrays,
                                int texCoordCount,
                                const float ** texCoordArrays)
{
    // If this attribute is enabled, normals, tangents,
    // and binormals can be visualized using false coloring.
    // Negative values will clamp to black however.

#ifdef _TEST_FILE_PATH_DURING_DRAW_
    if (path.hasFn( MFn::kMesh ) )
    {
        // Check the # of uvsets. If no uvsets
        // then can't return tangent or binormals
        //
        MGlobal::displayInfo( path.fullPathName() );

        MFnMesh fnMesh( path.node() );
        numUVSets = fnMesh.numUVSets();
    }
#endif
    if ((unsigned int)normalCount > mNormalsPerVertex)
        normalCount = mNormalsPerVertex;
    if (normalCount > 0)
    {
        glPushAttrib(GL_ALL_ATTRIB_BITS);
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
        glDisable(GL_LIGHTING);
        glDisable(GL_BLEND);

        if (normalCount > 1)
        {
            if (normalCount == 2)
            {
    //#define _TANGENT_DEBUG
    #ifdef _TANGENT_DEBUG
                const float *tangents = (const float *)&normalArrays[1][0];
                for (int  i=0; i< vertexCount; i++)
                {
                    cout<<"tangent["<<i<<"] = "<<tangents[i*3]
                        <<","<<tangents[i*3+1]<<","<<tangents[i*3+2]<<endl;
                }
    #endif
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(3, GL_FLOAT, 0, &normalArrays[1][0]);
            }
            else
            {
    #ifdef _BINORMAL_DEBUG
                const float *binormals = (const float *)&normalArrays[2][0];
                for (unsigned int  i=0; i< vertexCount; i++)
                {
                    cout<<"binormals["<<i<<"] = "<<binormals[i*3]
                        <<","<<binormals[i*3+1]<<","<<binormals[i*3+2]<<endl;
                }
    #endif
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(3, GL_FLOAT, 0, &normalArrays[2][0]);
            }
        }
        else if (normalCount)
        {
            glEnableClientState(GL_COLOR_ARRAY);
            glColorPointer(3, GL_FLOAT, 0, &normalArrays[0][0]);
        }

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer ( 3, GL_FLOAT, 0, &vertexArray[0] );
        glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );

        glDisableClientState(GL_COLOR_ARRAY);

        glPopClientAttrib();
        glPopAttrib();

        return MS::kSuccess;
    }
    else
    {
        return draw( prim, writable, indexCount, indexArray,
            vertexCount, vertexArray, colorCount, colorArrays );
    }
}

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

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

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

        MHardwareRenderer::GeometricShape gshape = MHardwareRenderer::kDefaultSphere;
        //MHardwareRenderer::GeometricShape gshape = MHardwareRenderer::kDefaultPlane;
        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 MStatus::kFailure;
        }

        glPushAttrib(GL_ALL_ATTRIB_BITS);

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

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

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

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

            glEnable( GL_LIGHTING );
            glEnable( GL_LIGHT0 );
        }

        // Get camera
        // ==========
        {
            // Get the camera frustum from the API
            double l, r, b, t, n, f;
            pRenderer->getSwatchOrthoCameraSetting( l, r, b, t, n, f );

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho( l, r, b, t, n, f );

            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
        }

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

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

        glPopAttrib();
    }

    return status;
}


void
hwColorPerVertexShader::drawTheSwatch( MGeometryData* pGeomData,
                                       unsigned int* pIndexing,
                                       unsigned int  numberOfData,
                                       unsigned int  indexCount )

{
    MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
    if( !pRenderer )    return;

    // Set the default background color
    {
        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);

    {
        // Enable the blending to get the transparency to work
        //
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }

    // Load in a sample background image
    if (mSampleImageId == 0)
    {
        mSampleImage = new MImage;
        MStatus rstatus = mSampleImage->readFromFile("C:\\temp\\maya.gif");
        if (rstatus == MStatus::kSuccess)
        {
            unsigned int w, h;
            mSampleImage->getSize( w, h );
            if (w > 2 && h > 2 )
            {
                glGenTextures( 1, &mSampleImageId );
                if (mSampleImageId > 0)
                {
                    glEnable(GL_TEXTURE_2D);
                    glBindTexture ( GL_TEXTURE_2D, mSampleImageId );
                    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0,
                        GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *) mSampleImage->pixels() );
                }
            }
        }
        if (mSampleImage)
        {
            delete mSampleImage;
        }
    }

    // Overlay the background checker board
    //
    bool drawBackGround = ( mTranspBias > 0.0f );
    bool drawBackGroundTexture = (mSampleImageId != 0);
    if (drawBackGround)
    {
        if (drawBackGroundTexture)
        {
            glMatrixMode(GL_TEXTURE);
            glLoadIdentity();
            glMatrixMode(GL_MODELVIEW);

            glBindTexture(GL_TEXTURE_2D, mSampleImageId );

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

            glEnable(GL_TEXTURE_2D);
        }

        unsigned int numberOfRepeats = 8;
        MColor quadColor( 0.5f, 0.5f, 0.5f, 1.0f );
        pRenderer->drawSwatchBackGroundQuads( quadColor,
                            drawBackGroundTexture, numberOfRepeats );

        if (drawBackGroundTexture)
            glDisable(GL_TEXTURE_2D);
        glEnable(GL_LIGHTING);
    }

    {
        // Set some example material
        //
        float ambient[4]  = { 0.1f, 0.1f, 0.1f, 1.0f };
        float diffuse[4]  = { 0.7f, 0.7f, 0.7f, 1.0f };
        float specular[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
        float emission[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,  ambient);
        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission );
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular );
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0f);

        // Track diffuse color
        float swatchColor[4];
        float biasT = 1.0f - mTranspGain - mTranspBias;
        swatchColor[0] = (diffuse[0] * mColorGain[0]) + mColorBias[0];
        swatchColor[1] = (diffuse[1] * mColorGain[1]) + mColorBias[1];
        swatchColor[2] = (diffuse[2] * mColorGain[2]) + mColorBias[2];
        swatchColor[3] = (diffuse[3] * mTranspGain) + biasT;

        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, swatchColor );
        glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);

        glEnable(GL_COLOR_MATERIAL);
        glColor4fv( swatchColor );
    }

    if (pGeomData)
    {
        glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );

        if (mNormalsPerVertex >= 1)
        {
            glDisable(GL_LIGHTING);
            float *normalData = (float *)( pGeomData[1].data() );
            float * tangentData = (float *)( pGeomData[3].data() );
            float * binormalData = (float *)( pGeomData[4].data() );
            if (normalData && mNormalsPerVertex  == 1)
            {
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(3, GL_FLOAT, 0, normalData);
            }
            else if (tangentData && mNormalsPerVertex  == 2)
            {
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(3, GL_FLOAT, 0, tangentData);
            }
            else if (binormalData)
            {
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(3, GL_FLOAT, 0, binormalData);
            }
        }

        float *vertexData = (float *)( pGeomData[0].data() );
        if (vertexData)
        {
            glEnableClientState( GL_VERTEX_ARRAY );
            glVertexPointer ( 3, GL_FLOAT, 0, vertexData );
        }

        float *normalData = (float *)( pGeomData[1].data() );
        if (normalData)
        {
            glEnableClientState( GL_NORMAL_ARRAY );
            glNormalPointer (    GL_FLOAT, 0, normalData );
        }

        if (mSampleImageId > 0)
        {
            float *uvData = (float *) (pGeomData[2].data() );
            if (uvData)
            {
                glBindTexture ( GL_TEXTURE_2D, mSampleImageId );
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

                glEnable(GL_TEXTURE_2D);
                glEnableClientState( GL_TEXTURE_COORD_ARRAY );
                glTexCoordPointer( 2, GL_FLOAT, 0, uvData );

                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
                glScalef( 0.5f, 0.5f, 1 );
                //glTranslatef(0.5f, 0.5f, 0.0f);
                glRotatef( mTexRotateX, 1.0f, 0.0f, 0.0f);
                glRotatef( mTexRotateY, 0.0, 1.0f, 0.0f);
                glRotatef( mTexRotateZ, 0.0, 0.0f, 1.0f);
                glMatrixMode(GL_MODELVIEW);
            }
        }

        if (vertexData && normalData && pIndexing )
            glDrawElements ( GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, pIndexing );

        glPopClientAttrib();

        // Release data references
        pRenderer->dereferenceGeometry( pGeomData, numberOfData );

    }
    glDisable( GL_COLOR_MATERIAL );
    glDisable( GL_LIGHTING );
}


MStatus hwColorPerVertexShader::draw(
                                int prim,
                                unsigned int writable,
                                int indexCount,
                                const unsigned int * indexArray,
                                int vertexCount,
                                const float * vertexArray,
                                int colorCount,
                                const float ** colorArrays)
{
    // Should check this value to allow caching
    // of color values.
    mAttributesChanged = false;

    // We assume triangles here.
    //
    if (!vertexCount ||
        !((prim == GL_TRIANGLES) || (prim == GL_TRIANGLE_STRIP)))
    {
        return MS::kFailure;
    }

    //glPushAttrib(GL_ALL_ATTRIB_BITS); // This is really overkill
    glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT );
    glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
    glDisable(GL_LIGHTING);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer ( 3, GL_FLOAT, 0, &vertexArray[0] );

    bool localCopyOfColors = false;
    float * colors = NULL;

    // Do "cheesy" multi-pass here for more than one color set
    //
    bool blendSet = false;

    if( colorCount <= 1 )
    {
        glDisable(GL_BLEND);
        if( colorCount > 0 && colorArrays[0] != NULL)
        {
            glEnableClientState(GL_COLOR_ARRAY);
            glColorPointer(4, GL_FLOAT, 0, colorArrays[0]);
        }
        else
        {
            glColor4f(1.0, 0.5, 1.0, 1.0);
        }
        glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );
    }
    else
    {
        // Do a 1:1 Blend if we have more than on color set available.
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        glEnableClientState(GL_COLOR_ARRAY);
        blendSet = true;

        for (int i=0; i<colorCount; i++)
        {
            if (colorArrays[i] != NULL)
            {
                // Apply gain and bias
                //
                bool haveTransparency = false;

                if (mColorGain[0] != 1.0f ||
                    mColorGain[1] != 1.0f ||
                    mColorGain[2] != 1.0f ||
                    mColorBias[0] != 0.0f ||
                    mColorBias[1] != 0.0f ||
                    mColorBias[2] != 0.0f ||
                    mTranspGain != 1.0f ||
                    mTranspBias != 0.0f)
                {
                    // This sample code is a CPU bottlneck. It could be
                    // replaced with a vertex program or color matrix
                    // operator.
                    //

                    // We really want to scale 1-transp.
                    // T = 1 - ((1-T)*gain + bias) =
                    //   = T * gain + 1 - gain - bias
                    float biasT = 1 - mTranspGain - mTranspBias;

                    // Either make a copy or read directly colors.
                    //
                    if (!(writable & kWriteColorArrays))
                    {
                        unsigned int numFloats = 4 * vertexCount;
                        colors = new float[numFloats];
                        localCopyOfColors = true;
                    }
                    else
                    {
                        colors = (float *)colorArrays[i];
                    }

                    float *origColors = (float *)colorArrays[i];
                    float *colorPtr = colors;
                    for (int ii = vertexCount ; ii-- ; )
                    {
                        colorPtr[0] = (origColors[0] * mColorGain[0]) + mColorBias[0];
                        colorPtr[1] = (origColors[1] * mColorGain[1]) + mColorBias[1];
                        colorPtr[2] = (origColors[2] * mColorGain[2]) + mColorBias[2];
                        colorPtr[3] = (origColors[3] * mTranspGain) + biasT;
                        if (colorPtr[3] != 1.0f)
                            haveTransparency = true;

                        colorPtr += 4;
                        origColors += 4;
                    }
                }
                else
                {
                    // Do a quick test for transparency.
                    // This attribute is currently not
                    // being passed through to the plugin.
                    // so must be recomputed per refresh.
                    //
                    colors = (float *)colorArrays[i];
                    float *colorPtr = colors;
                    for (int ii = vertexCount ; ii-- ; )
                    {
                        if (colorPtr[3] != 1.0f )
                        {
                            haveTransparency = true;
                            break;
                        }
                        else
                            colorPtr += 4;
                    }
                }

                // Blending when there are alpha values
                //
                if (!blendSet)
                {
                    if (haveTransparency)
                    {
                        glEnable(GL_BLEND);
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                    }
                }
                else {
                    glDisable(GL_BLEND);
                }
                glEnableClientState(GL_COLOR_ARRAY);
                glColorPointer(4, GL_FLOAT, 0, colors);
            }
            else {
                glDisable(GL_BLEND);
                glDisableClientState(GL_COLOR_ARRAY);
                glColor4f(1.0, 1.0, 1.0, 1.0);
            }

            glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );
        }
    }

    glDisable(GL_BLEND);
    glPopClientAttrib();
    glPopAttrib();

    // Delete any local storage if we are passed
    // non-writable data.
    if (localCopyOfColors)
    {
        delete[] colors;
        colors = NULL;
    }

    return MS::kSuccess;
}

// virtual
// Tells maya that color per vertex will be needed.
int hwColorPerVertexShader::normalsPerVertex()
{
    unsigned int numNormals = mNormalsPerVertex;

    MStringArray setNames;
    const MDagPath & path = currentPath();
    if (path.hasFn( MFn::kMesh ) )
    {
        // Check the # of uvsets. If no uvsets
        // then can't return tangent or binormals
        //
        // MGlobal::displayInfo( path.fullPathName() );

        MFnMesh fnMesh( path.node() );
        if (fnMesh.numUVSets() == 0)
        {
            // Put out a warnnig if we're asking for too many uvsets.
            MString dispWarn("Asking for more uvsets then available for shape: ");
            MString pathName = path.fullPathName();
            dispWarn = dispWarn + pathName;
            MGlobal::displayWarning( dispWarn  );
            numNormals = (mNormalsPerVertex > 1 ? 1 : 0);
        }
    }
    return numNormals;
}

// virtual
// Tells maya that texCoords per vertex will be needed.
int hwColorPerVertexShader::texCoordsPerVertex()
{
    return 0;
}

// virtual
// Tells maya that color per vertex will be needed.
int hwColorPerVertexShader::colorsPerVertex()
{
    int returnVal = 0;

    // Going to be displaying false coloring,
    // so skip getting internal colors.
    if (mNormalsPerVertex)
        return 0;
    else {
        const MDagPath & path = currentPath();
        if (path.hasFn( MFn::kMesh ) )
        {
            MFnMesh fnMesh( path.node() );
            unsigned int numColorSets = fnMesh.numColorSets();
            if (numColorSets < 2)
                returnVal = numColorSets;
            else
                returnVal  = 2;
        }
    }
    return returnVal;
}

// virtual
// Tells maya that color per vertex will be needed.
bool hwColorPerVertexShader::hasTransparency()
{
    return true;
}

// The compute method is called by Maya when the input values
// change and the output values need to be recomputed.
//
// In this case this is only used for software shading, when the to
// compute the rendering swatches.
MStatus hwColorPerVertexShader::compute (const MPlug& plug, MDataBlock& data)
{
    MStatus returnStatus;

    // Check that the requested recompute is one of the output values
    //
    if ((plug != outColor) && (plug.parent() != outColor))
        return MS::kUnknownParameter;

    MDataHandle inputData = data.inputValue (aColorGain, &returnStatus);
    CHECK_MSTATUS( returnStatus );

    const MFloatVector & color = inputData.asFloatVector();

    MDataHandle outColorHandle = data.outputValue( outColor, &returnStatus );
    CHECK_MSTATUS( returnStatus );
    MFloatVector & outColor = outColorHandle.asFloatVector();
    outColor = color;

    CHECK_MSTATUS( data.setClean( plug ) );

    return MS::kSuccess;
}


// Shader override for the custom shading node
class hwCPVShaderOverride : public MHWRender::MPxShaderOverride
{
public:
    static MHWRender::MPxShaderOverride* Creator(const MObject& obj)
    {
        return new hwCPVShaderOverride(obj);
    }

    virtual ~hwCPVShaderOverride()
    {
        fShaderNode = NULL;
    }

    // 1) Initialize phase
    virtual MString initialize(MObject shader)
    {
        // Retrieve and cache the actual node pointer
        if(shader != MObject::kNullObj)
        {
            fShaderNode = (hwColorPerVertexShader*)
                MPxHwShaderNode::getHwShaderNodePtr(shader);
        }

        // Set position requirement
        MString reqName;
        addGeometryRequirement(
            MHWRender::MVertexBufferDescriptor(
                reqName,
                MHWRender::MGeometry::kPosition,
                MHWRender::MGeometry::kFloat,
                3));

        // Set correct color requirement
        if(fShaderNode)
        {
            reqName = fShaderNode->colorSetName();
        }
        addGeometryRequirement(
            MHWRender::MVertexBufferDescriptor(
                reqName,
                MHWRender::MGeometry::kColor,
                MHWRender::MGeometry::kFloat,
                4));

        return MString("Autodesk Maya hwColorPerVertexShader");
    }


    // 2) Update phase -- Not implemented, nothing to do since we explicitly
    //                    rebuild on every update (thus triggering the
    //                    initialize method each time).

    // 3) Draw phase
    virtual bool draw(
        MHWRender::MDrawContext& context,
        const MHWRender::MRenderItemList& renderItemList) const;
    bool customDraw(
        MHWRender::MDrawContext& context,
        const MHWRender::MRenderItemList& renderItemList) const; // draw helper method

    virtual bool rebuildAlways()
    {
        // Since color set name changing needs to add a new
        // named requirement to the geometry, so we should
        // return true here to trigger the geometry rebuild.
        return true;
    }

    virtual MHWRender::DrawAPI supportedDrawAPIs() const
    {
        return MHWRender::kOpenGL;
    }

    virtual bool isTransparent()
    {
        return true;
    }

protected:
    hwCPVShaderOverride(const MObject& obj)
    : MHWRender::MPxShaderOverride(obj)
    , fShaderNode(NULL)
    {
    }

    // Current hwColorPerVertexShader node associated with the shader override.
    // Updated during doDG() time.
    hwColorPerVertexShader *fShaderNode;

    // Blend state will be used in the draw
    static MHWRender::MBlendState* fBlendState;
};

MHWRender::MBlendState* hwCPVShaderOverride::fBlendState = NULL;

bool hwCPVShaderOverride::draw(
    MHWRender::MDrawContext& context,
    const MHWRender::MRenderItemList& renderItemList) const
{
    //--------------------------
    // Matrix set up
    //--------------------------
    // Set world view matrix
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    MStatus status;
    MMatrix transform =
        context.getMatrix(MHWRender::MDrawContext::kWorldViewMtx, &status);
    if (status)
    {
        glLoadMatrixd(transform.matrix[0]);
    }

    // Set projection matrix
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    MMatrix projection =
        context.getMatrix(MHWRender::MDrawContext::kProjectionMtx, &status);
    if (status)
    {
        glLoadMatrixd(projection.matrix[0]);
    }

    //--------------------------
    // State set up
    //--------------------------
    MHWRender::MStateManager* stateMgr = context.getStateManager();

    // Blending
    // Save current blend status
    const MHWRender::MBlendState* curBlendStatus = stateMgr->getBlendState(&status);
    if(!curBlendStatus || status == MStatus::kFailure)
    {
        return false;
    }
    if(!fBlendState)
    {
        // If we haven't got a blending state, then acquire a new one.
        // Since it would be expensive to acquire the state, we would
        // store it.
        // Create a new blend status and acquire blend state from the context
        MHWRender::MBlendStateDesc blendDesc;
        for(int i = 0; i < (blendDesc.independentBlendEnable ? MHWRender::MBlendState::kMaxTargets : 1); ++i)
        {
            blendDesc.targetBlends[i].blendEnable = true;
            blendDesc.targetBlends[i].sourceBlend = MHWRender::MBlendState::kSourceAlpha;
            blendDesc.targetBlends[i].destinationBlend = MHWRender::MBlendState::kInvSourceAlpha;
            blendDesc.targetBlends[i].blendOperation = MHWRender::MBlendState::kAdd;
            blendDesc.targetBlends[i].alphaSourceBlend = MHWRender::MBlendState::kOne;
            blendDesc.targetBlends[i].alphaDestinationBlend = MHWRender::MBlendState::kInvSourceAlpha;
            blendDesc.targetBlends[i].alphaBlendOperation = MHWRender::MBlendState::kAdd;
        }
        blendDesc.blendFactor[0] = 1.0f;
        blendDesc.blendFactor[1] = 1.0f;
        blendDesc.blendFactor[2] = 1.0f;
        blendDesc.blendFactor[3] = 1.0f;

        fBlendState = const_cast<MHWRender::MBlendState*>(stateMgr->acquireBlendState(blendDesc, &status));
        if(!fBlendState || status == MStatus::kFailure)
        {
            return false;
        }
    }

    // Activate the blend on the device
    if(MStatus::kFailure == stateMgr->setBlendState(fBlendState))
    {
        return false;
    }

    // Bounding box draw
    if( fShaderNode->wantDrawBoundingBox() )
    {
        int numRenderItems = renderItemList.length();
        for( int i = 0; i < numRenderItems; i++ )
        {
            const MHWRender::MRenderItem* renderItem = renderItemList.itemAt(i);
            if( !renderItem )
            {
                continue;
            }
            // Modelview matrix is already set so just use the object
            // space bbox.
            MBoundingBox box = renderItem->boundingBox(MSpace::kObject);

            // Draw the bounding box.
            MColor wireColor( 0.1f, 0.15f, 0.35f );
            drawBoundingBox( box, wireColor );
        }
    }

    //--------------------------
    // geometry draw
    //--------------------------
    bool useCustomDraw = false;
    if(useCustomDraw)
    {
        // Custom draw
        // Does not set state, matrix or material
        customDraw(context, renderItemList);
    }
    else
    {
        // Internal standard draw
        // Does not set state, matrix or material
        drawGeometry(context);
    }


    //--------------------------
    // Matrix restore
    //--------------------------
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();


    //--------------------------
    // State restore
    //--------------------------
    // Restore blending
    stateMgr->setBlendState(curBlendStatus);

    return true;
}

bool hwCPVShaderOverride::customDraw(
    MHWRender::MDrawContext& context,
    const MHWRender::MRenderItemList& renderItemList) const
{
    static MGLFunctionTable *gGLFT = 0;
    if ( 0 == gGLFT )
        gGLFT = MHardwareRenderer::theRenderer()->glFunctionTable();

    MGLenum currentError = 0;

    glPushClientAttrib ( GL_CLIENT_ALL_ATTRIB_BITS );
    {
        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;

            // Dump out vertex field information for each field
            //
            int bufferCount = geometry->vertexBufferCount();

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

            bool boundData = true;
            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);

                unsigned int fieldOffset = desc.offset();
                unsigned int fieldStride = desc.stride();

                // Bind each data buffer
                if (*dataBufferId > 0)
                {
                    gGLFT->glBindBufferARB(MGL_ARRAY_BUFFER_ARB, *dataBufferId);
                    currentError = gGLFT->glGetError();
                    if (currentError != MGL_NO_ERROR)
                        boundData = false;
                }
                else
                    boundData = false;

                if (boundData)
                {
                    // Set the data pointers
                    if (desc.semantic() == MHWRender::MGeometry::kPosition)
                    {
                        glEnableClientState(GL_VERTEX_ARRAY);
                        glVertexPointer(3, GL_FLOAT, fieldStride*4, GLOBJECT_BUFFER_OFFSET(fieldOffset));
                        currentError = gGLFT->glGetError();
                        if (currentError != MGL_NO_ERROR)
                            boundData = false;
                    }
                    else if (desc.semantic() == MHWRender::MGeometry::kColor)
                    {
                        glEnableClientState(GL_COLOR_ARRAY);
                        glColorPointer(4, GL_FLOAT, fieldStride*4, GLOBJECT_BUFFER_OFFSET(fieldOffset));
                        currentError = gGLFT->glGetError();
                        if (currentError != MGL_NO_ERROR)
                            boundData = false;
                    }
                }
            }

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

                // Bind the index buffer
                if (indexBufferId  && (*indexBufferId > 0))
                {
                    gGLFT->glBindBufferARB(MGL_ELEMENT_ARRAY_BUFFER_ARB, *indexBufferId);
                    currentError = gGLFT->glGetError();
                    if (currentError == MGL_NO_ERROR)
                    {
                        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:
                                        boundData = false;
                                        break;
                        };
                        if (boundData)
                        {
                            // Draw the geometry
                            GLenum indexType =
                                ( buffer->dataType() == MHWRender::MGeometry::kUnsignedInt32  ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT );
                            glDrawElements(indexPrimTypeGL, indexBufferCount, indexType, GLOBJECT_BUFFER_OFFSET(0));
                        }
                    }
                }
            }
        }
    }
    glPopClientAttrib();

    return true;
}


// The initializePlugin method is called by Maya when the
// plugin is loaded. It registers the hwColorPerVertexShader node
// which provides Maya with the creator and initialize methods to be
// called when a hwColorPerVertexShader is created.
//
MStatus initializePlugin( MObject obj )
{
    MStatus   status;

    const MString& swatchName = MHWShaderSwatchGenerator::initialize();
    const MString UserClassify( "shader/surface/utility/:drawdb/shader/surface/hwColorPerVertexShader:swatch/"+swatchName );

    MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");

    status = plugin.registerNode("hwColorPerVertexShader",
        hwColorPerVertexShader::id,
        hwColorPerVertexShader::creator,
        hwColorPerVertexShader::initialize,
        MPxNode::kHwShaderNode, &UserClassify );
    CHECK_MSTATUS(status);


    // Register a shader override for this node
    MHWRender::MDrawRegistry::registerShaderOverrideCreator(
        "drawdb/shader/surface/hwColorPerVertexShader",
        sHWCPVShaderRegistrantId,
        hwCPVShaderOverride::Creator);
    if (status != MS::kSuccess) return status;

    return status;
}

// The unitializePlugin is called when Maya needs to unload the plugin.
// It basically does the opposite of initialize by calling
// the deregisterNode to remove it.
//
MStatus uninitializePlugin( MObject obj )
{
    MStatus   status;
    MFnPlugin plugin( obj );

    // Unregister all chamelion shader nodes
    status = plugin.deregisterNode( hwColorPerVertexShader::id );
    CHECK_MSTATUS(status);

    // Deregister the shader override
    status = MHWRender::MDrawRegistry::deregisterShaderOverrideCreator(
        "drawdb/shader/surface/hwColorPerVertexShader", sHWCPVShaderRegistrantId);
    if (status != MS::kSuccess) return status;

    return status;
}