hwColorPerVertexShader/hwColorPerVertexShader.cpp
 
 
 
hwColorPerVertexShader/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::MFrameContext::kWorldViewMtx, &status);
    if (status)
    {
        glLoadMatrixd(transform.matrix[0]);
    }

    // Set projection matrix
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    MMatrix projection =
        context.getMatrix(MHWRender::MFrameContext::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;
}