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

# hwPhongShader.cpp

    //-
    // ==========================================================================
    // Copyright 2010 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 a using a cube-environment map
    //      to perforce per pixel Phong shading.
    //
    //      The light direction is currently fixed at the eye
    //      position. This could be changed to track an actual
    //      light but has not been coded for this example.
    //
    //      If multiple lights are to be supported, than the environment
    //      map would need to be looked up for each light either
    //      using multitexturing or multipass.
    //

    #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/MFloatVector.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MSceneMessage.h>

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

    #include <maya/MFloatMatrix.h>

    // Viewport 2.0 includes
    #include <maya/MDrawRegistry.h>
    #include <maya/MPxShaderOverride.h>
    #include <maya/MDrawContext.h>
    #include <maya/MStateManager.h>
    #include <maya/MViewport2Renderer.h>
    #include <maya/MTextureManager.h>

    #include <maya/MGLdefinitions.h>
    #include <maya/MGLFunctionTable.h>

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

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

    #ifndef GL_EXT_texture_cube_map
    # define GL_NORMAL_MAP_EXT                   0x8511
    # define GL_REFLECTION_MAP_EXT               0x8512
    # define GL_TEXTURE_CUBE_MAP_EXT             0x8513
    # define GL_TEXTURE_BINDING_CUBE_MAP_EXT     0x8514
    # define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT  0x8515
    # define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT  0x8516
    # define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT  0x8517
    # define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT  0x8518
    # define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT  0x8519
    # define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT  0x851A
    # define GL_PROXY_TEXTURE_CUBE_MAP_EXT       0x851B
    # define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT    0x851C
    #endif

    #include "hwPhongShader.h"
    #include "hwPhongShaderBehavior.h"

    MTypeId hwPhongShader::id( 0x00105449 );

    MObject  hwPhongShader::aColor;
    MObject  hwPhongShader::aTransparency;
    MObject  hwPhongShader::aDiffuseColor;
    MObject  hwPhongShader::aSpecularColor;
    MObject  hwPhongShader::aShininess;
    MObject  hwPhongShader::aGeometryShape;

    void hwPhongShader::postConstructor( )
    {
        TRACE_API_CALLS("postConstructor");
        setMPSafe(false);
    }

    //
    // DESCRIPTION:

    void hwPhongShader::printGlError( const char *call )
    {
        GLenum error;

        while( (error = glGetError()) != GL_NO_ERROR ) {
            cerr << call << ":" << error << " is " << (const char *)gluErrorString( error ) << "\n";
        }
    }

    MFloatVector hwPhongShader::Phong ( double cos_a )
    {
        MFloatVector p;

        if ( cos_a < 0.0 ) cos_a = 0.0;

        p[0] = (float)(mSpecularColor[0]*pow(cos_a,double(mShininess[0])) +
                       mDiffuseColor[0]*cos_a + mAmbientColor[0]);
        p[1] = (float)(mSpecularColor[1]*pow(cos_a,double(mShininess[1])) +
                       mDiffuseColor[1]*cos_a + mAmbientColor[1]);
        p[2] = (float)(mSpecularColor[2]*pow(cos_a,double(mShininess[2])) +
                       mDiffuseColor[2]*cos_a + mAmbientColor[2]);

        if ( p[0] > 1.0f ) p[0] = 1.0f;
        if ( p[1] > 1.0f ) p[1] = 1.0f;
        if ( p[2] > 1.0f ) p[2] = 1.0f;

        return p;
    }

    #define PHONG_TEXTURE_RES 256

    static const GLenum faceTarget[6] =
    {
        GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT
    };

    // Small reusable utility to bind and unbind a cube map texture by id.
    class CubeMapTextureDrawUtility
    {
    public:
        static void bind( unsigned int phong_map_id )
        {
            // Setup up phong texture
            {
                glPushAttrib ( GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT | GL_TRANSFORM_BIT );

                glDisable ( GL_LIGHTING );
                glDisable ( GL_TEXTURE_1D );
                glDisable ( GL_TEXTURE_2D );

                // Setup cube map generation
                glEnable ( GL_TEXTURE_CUBE_MAP_EXT );
                glBindTexture ( GL_TEXTURE_CUBE_MAP_EXT, phong_map_id );
                glEnable ( GL_TEXTURE_GEN_S );
                glEnable ( GL_TEXTURE_GEN_T );
                glEnable ( GL_TEXTURE_GEN_R );
                glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
                glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
                glTexGeni ( GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );

                glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP);
                glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP);
                glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

                // Setup texture matrix
                glMatrixMode ( GL_TEXTURE );
                glPushMatrix ();
                glLoadIdentity ();
                glMatrixMode ( GL_MODELVIEW );
            }
        }

        static void unbind()
        {
            // Restore texture matrix
            glMatrixMode ( GL_TEXTURE );
            glPopMatrix ();
            glMatrixMode ( GL_MODELVIEW );

            // Disable cube map texture (bind 0)
            glBindTexture( GL_TEXTURE_CUBE_MAP_EXT, 0 );
            glDisable ( GL_TEXTURE_CUBE_MAP_EXT );

            glPopAttrib();
        }
    };

    static void cubeToDir ( int face, double s, double t,
                          double &x, double &y, double &z )
    //
    // Description:
    //      Map uv to cube direction
    {
        switch ( face )
        {
            case 0:
                x = 1;
                y = -t;
                z = -s;
                break;
            case 1:
                x = -1;
                y = -t;
                z = s;
                break;
            case 2:
                x = s;
                y = 1;
                z = t;
                break;
            case 3:
                x = s;
                y = -1;
                z = -t;
                break;
            case 4:
                x = s;
                y = -t;
                z = 1;
                break;
            case 5:
                x = -s;
                y = -t;
                z = -1;
                break;
        }

        double invLen = 1.0 / sqrt ( x*x + y*y + z*z );
        x *= invLen;
        y *= invLen;
        z *= invLen;
    }

    void hwPhongShader::init_Phong_texture ( void )
    //
    // Description:
    //      Set up a cube map for Phong lookup
    //
    {
        // There is nothing dirty, so don't rebuild the texture.
        if ( !mAttributesChanged && (phong_map_id != 0))
        {
            return;
        }

        GLubyte * texture_data;

        // Always release the old texture id before getting a
        // new one.
        if (phong_map_id != 0)
            glDeleteTextures( 1, &phong_map_id );
        glGenTextures ( 1, &phong_map_id );

        glEnable ( GL_TEXTURE_CUBE_MAP_EXT );
        glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
        glPixelStorei ( GL_UNPACK_ROW_LENGTH, 0 );
        glBindTexture ( GL_TEXTURE_CUBE_MAP_EXT, phong_map_id );

        texture_data = new GLubyte[3*PHONG_TEXTURE_RES*PHONG_TEXTURE_RES];

        for ( int face=0 ; face<6 ; face++ )
        {
            int index = 0;
            for ( int j=0 ; j<PHONG_TEXTURE_RES ; j++ )
            {
                double t = 2*double(j)/(PHONG_TEXTURE_RES - 1) - 1; // -1 to 1
                for ( int i=0 ; i<PHONG_TEXTURE_RES ; i++ )
                {
                    double s = 2*double(i)/(PHONG_TEXTURE_RES - 1) - 1; // -1 to 1
                    double x = 0.0, y = 0.0, z = 0.0;
                    cubeToDir ( face, s, t, x, y, z );

                    MFloatVector intensity = Phong ( z );

                    texture_data[index++] = (GLubyte)(255*intensity[0]);
                    texture_data[index++] = (GLubyte)(255*intensity[1]);
                    texture_data[index++] = (GLubyte)(255*intensity[2]);
                }
            }

            glTexImage2D ( faceTarget[face], 0, GL_RGB, PHONG_TEXTURE_RES, PHONG_TEXTURE_RES,
                0, GL_RGB, GL_UNSIGNED_BYTE, texture_data );

        }

        glDisable ( GL_TEXTURE_CUBE_MAP_EXT );

        delete [] texture_data;

        // Make sure to mark attributes "clean".
        mAttributesChanged = false;
    }

    hwPhongShader::hwPhongShader()
    {
        TRACE_API_CALLS("hwPhongShader");
        attachSceneCallbacks();

        mAmbientColor[0] = mAmbientColor[1] = mAmbientColor[2] = 0.1f;
        mDiffuseColor[0] = mDiffuseColor[1] = mDiffuseColor[2] = 0.5f;
        mSpecularColor[0] = mSpecularColor[1] = mSpecularColor[2] = 0.5f;
        mShininess[0] = mShininess[1] = mShininess[2] = 100.0f;
        mAttributesChanged = false;
        mAttributesChangedVP2 = true;

        phong_map_id = 0;
        mGeometryShape = 0;
        mTransparency = 0.0f;
    }

    hwPhongShader::~hwPhongShader()
    {
        TRACE_API_CALLS("~hwPhongShader");
        detachSceneCallbacks();
    }

    void hwPhongShader::releaseEverything()
    {
        if (phong_map_id != 0) {
            M3dView view = M3dView::active3dView();

            // The M3dView class doesn't return the correct status if there isn't
            // an active 3D view, so we rely on the success of beginGL() which
            // will make the context current.
            //
            if (view.beginGL()) {
                glDeleteTextures( 1, &phong_map_id );

                phong_map_id = 0;
            }

            view.endGL();
        }
    }

    void hwPhongShader::attachSceneCallbacks()
    {
        fBeforeNewCB  = MSceneMessage::addCallback(MSceneMessage::kBeforeNew,  releaseCallback, this);
        fBeforeOpenCB = MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, releaseCallback, this);
        fBeforeRemoveReferenceCB = MSceneMessage::addCallback(MSceneMessage::kBeforeRemoveReference,
                                                              releaseCallback, this);
        fMayaExitingCB = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, releaseCallback, this);
    }

    /*static*/
    void hwPhongShader::releaseCallback(void* clientData)
    {
        hwPhongShader *pThis = (hwPhongShader*) clientData;
        pThis->releaseEverything();
    }

    void hwPhongShader::detachSceneCallbacks()
    {
        if (fBeforeNewCB)
            MMessage::removeCallback(fBeforeNewCB);
        if (fBeforeOpenCB)
            MMessage::removeCallback(fBeforeOpenCB);
        if (fBeforeRemoveReferenceCB)
            MMessage::removeCallback(fBeforeRemoveReferenceCB);
        if (fMayaExitingCB)
            MMessage::removeCallback(fMayaExitingCB);

        fBeforeNewCB = 0;
        fBeforeOpenCB = 0;
        fBeforeRemoveReferenceCB = 0;
        fMayaExitingCB = 0;
    }

    static const MString sHWPhongShaderRegistrantId("HWPhongShaderRegistrantId");

    // Viewport 2.0 implementation for the shader
    class hwPhongShaderOverride : public MHWRender::MPxShaderOverride
    {
    public:
        static MHWRender::MPxShaderOverride* Creator(const MObject& obj)
        {
            return new hwPhongShaderOverride(obj);
        }

        virtual ~hwPhongShaderOverride()
        {
            delete[] fTextureData;
            fTextureData = NULL;
            fShaderNode = NULL;
            if(fTexture)
            {
                delete fTexture;
                fTexture = NULL;
            }
        }

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

            // Set the geometry requirements for drawing. Only need
            // position and normals.
            MString empty;
            addGeometryRequirement(
                MHWRender::MVertexBufferDescriptor(
                    empty,
                    MHWRender::MGeometry::kPosition,
                    MHWRender::MGeometry::kFloat,
                    3));
            addGeometryRequirement(
                MHWRender::MVertexBufferDescriptor(
                    empty,
                    MHWRender::MGeometry::kNormal,
                    MHWRender::MGeometry::kFloat,
                    3));

            return MString("Autodesk Maya hwPhongShaderOverride");
        }

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

            if (object != MObject::kNullObj)
            {
                // Get the hardware shader node from the MObject.
                fShaderNode = (hwPhongShader *) MPxHwShaderNode::getHwShaderNodePtr( object );
                if (fShaderNode)
                {
                    fTransparency = fShaderNode->Transparency();
                }
                else
                {
                    fTransparency = 0.0f;
                }
            }
        }
        virtual void updateDevice()
        {
            TRACE_API_CALLS("hwPhongShaderOverride::updateDevice");

            rebuildTexture();
        }
        virtual void endUpdate()
        {
            TRACE_API_CALLS("hwPhongShaderOverride::endUpdate");
        }

        // Draw phase
        virtual bool draw(
            MHWRender::MDrawContext& context,
            const MHWRender::MRenderItemList& renderItemList) const
        {
            TRACE_API_CALLS("hwPhongShaderOverride::draw");

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

            //initialize hwPhongShader blend state once
            if(sBlendState == NULL)
            {
                //initilize blend state
                MHWRender::MBlendStateDesc blendStateDesc;

                for(int i = 0; i < (blendStateDesc.independentBlendEnable ? MHWRender::MBlendState::kMaxTargets : 1); ++i)
                {
                    blendStateDesc.targetBlends[i].blendEnable = true;
                    blendStateDesc.targetBlends[i].sourceBlend = MHWRender::MBlendState::kSourceAlpha;
                    blendStateDesc.targetBlends[i].destinationBlend = MHWRender::MBlendState::kInvSourceAlpha;
                    blendStateDesc.targetBlends[i].blendOperation = MHWRender::MBlendState::kAdd;
                    blendStateDesc.targetBlends[i].alphaSourceBlend = MHWRender::MBlendState::kOne;
                    blendStateDesc.targetBlends[i].alphaDestinationBlend = MHWRender::MBlendState::kInvSourceAlpha;
                    blendStateDesc.targetBlends[i].alphaBlendOperation = MHWRender::MBlendState::kAdd;
                }

                blendStateDesc.blendFactor[0] = 1.0f;
                blendStateDesc.blendFactor[1] = 1.0f;
                blendStateDesc.blendFactor[2] = 1.0f;
                blendStateDesc.blendFactor[3] = 1.0f;
                sBlendState = stateMgr->acquireBlendState(blendStateDesc);
            }

            bool debugDrawContext = false;
            if (debugDrawContext)
                printContextInformation(context);

            // save old render state
            const MHWRender::MBlendState* pOldBlendState = stateMgr->getBlendState();

            int phongTextureId = 0;
            MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();
            if (theRenderer)
            {
                MHWRender::MTextureManager *theTextureManager = theRenderer->getTextureManager();
                if(theTextureManager)
                {
                    if(fTexture)
                    {
                        void *idPtr = fTexture->resourceHandle();
                        if (idPtr)
                        {
                            phongTextureId = *((int *)idPtr);
                        }
                    }
                }
            }
            if (phongTextureId == 0)
            {
                return false;
            }

            // Setup the matrix to draw the object in world space
            glMatrixMode(GL_MODELVIEW);
            glPushMatrix();
            MStatus status;
            MFloatMatrix transform =
                context.getMatrix(MHWRender::MDrawContext::kWorldViewMtx, &status);
            if (status)
            {
                glLoadMatrixf(transform.matrix[0]);
            }

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

            bool needBlending = false;
            if (fTransparency > 0.0f)
            {
                needBlending = true;
                stateMgr->setBlendState(sBlendState);
                glColor4f(1.0, 1.0, 1.0, 1.0f-fTransparency );
            }
            else
                glColor4f(1.0, 1.0, 1.0, 1.0f);

            // Setup up phong texture
            CubeMapTextureDrawUtility::bind( phongTextureId );

            // Trigger geometric draw
            bool debugGeometricDraw = false; // Set to true to debug
            if (debugGeometricDraw)
            {
                // Debugging code to see what is being sent down to draw
                // by MDrawContext::drawGeometry().
                //
                // The sample draw code does basically what is done internally.
                //
                customDraw(context, renderItemList);
            }
            else
            {
                // Draw the geometry through the internal interface instead
                // of drawing via the plugin.
                MHWRender::MPxShaderOverride::drawGeometry(context);
            }

            // Disable texture state
            CubeMapTextureDrawUtility::unbind();

            if (needBlending)
            {
                glColor4f(1.0, 1.0, 1.0, 1.0 );
                //restore blend state
                stateMgr->setBlendState(pOldBlendState);
            }
            glPopMatrix();

            glMatrixMode(GL_MODELVIEW);
            glPopMatrix();

            return true;
        }

        void 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();
                        {
                            fprintf(stderr, "Buffer(%d), Name(%s), BufferType(%s), BufferDimension(%d), BufferSemantic(%s), Offset(%d), Stride(%d), Handle(%d)\n",
                                i,
                                desc.name().asChar(),
                                MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(),
                                desc.dimension(),
                                MHWRender::MGeometry::semanticString(desc.semantic()).asChar(),
                                fieldOffset,
                                fieldStride,
                                *dataBufferId);
                        }

                        // 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::kNormal)
                            {
                                glEnableClientState(GL_NORMAL_ARRAY);
                                glNormalPointer(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();
                            {
                                fprintf(stderr, "IndexingPrimType(%s), IndexType(%s), IndexCount(%d), Handle(%d)\n",
                                    MHWRender::MGeometry::primitiveString(indexPrimType).asChar(),
                                    MHWRender::MGeometry::dataTypeString(buffer->dataType()).asChar(),
                                    indexBufferCount,
                                    *indexBufferId);
                            }
                        }

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

        virtual bool isTransparent()
        {
            TRACE_API_CALLS("hwPhongShaderOverride::isTransparent");
            if (fShaderNode)
            {
                return (fTransparency > 0.0f);
            }
            return false;
        }

        // Other helper methods
        void rebuildTexture()
        {
            TRACE_API_CALLS("hwPhongShaderOverride::rebuildTexture");

            // Rebuild the hardware Phong texture if needed
            if (fShaderNode && fShaderNode->attributesChangedVP2())
            {
                MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();
                if (theRenderer)
                {
                    // Set a unique identifier for this texture
                    // based on the shading parameters used to create
                    // the texture
                    //
                    MString newTextureName = MString("MyPhongCubeMap__");
                    const float3 * amb = fShaderNode->Ambient();
                    const float3 * diff = fShaderNode->Diffuse();
                    const float3 * spec= fShaderNode->Specular();
                    const float3 * shininess = fShaderNode->Shininess();

                    newTextureName += (*amb)[0]; newTextureName += MString("_");
                    newTextureName += (*amb)[1]; newTextureName += MString("_");
                    newTextureName += (*amb)[2]; newTextureName += MString("_");
                    newTextureName += (*diff)[0]; newTextureName += MString("_");
                    newTextureName += (*diff)[1]; newTextureName += MString("_");
                    newTextureName += (*diff)[2]; newTextureName += MString("_");
                    newTextureName += (*spec)[0]; newTextureName += MString("_");
                    newTextureName += (*spec)[1]; newTextureName += MString("_");
                    newTextureName += (*spec)[2]; newTextureName += MString("_");
                    newTextureName += (*shininess)[0]; newTextureName += MString("_");
                    newTextureName += (*shininess)[1]; newTextureName += MString("_");
                    newTextureName += (*shininess)[2];

                    // Release the old one, and set the new name
                    //
                    if(fTexture)
                    {
                        delete fTexture;
                        fTexture = NULL;
                    }

                    // We create 1 contiguous block of data for the texture.
                    //
                    if (!fTextureData)
                    {
                        fTextureData =
                            new unsigned char[4*PHONG_TEXTURE_RES*PHONG_TEXTURE_RES*6];
                    }
                    if (fTextureData)
                    {
                        int index = 0;
                        for ( int face=0 ; face<6 ; face++ )
                        {
                            for ( int j=0 ; j<PHONG_TEXTURE_RES ; j++ )
                            {
                                double t = 2*double(j)/(PHONG_TEXTURE_RES - 1) - 1; // -1 to 1
                                for ( int i=0 ; i<PHONG_TEXTURE_RES ; i++ )
                                {
                                    double s = 2*double(i)/(PHONG_TEXTURE_RES - 1) - 1; // -1 to 1
                                    double x = 0.0, y = 0.0, z = 0.0;
                                    cubeToDir ( face, s, t, x, y, z );

                                    MFloatVector intensity = fShaderNode->Phong ( z );

                                    fTextureData[index++] = (unsigned char)(255*intensity[0]);
                                    fTextureData[index++] = (unsigned char)(255*intensity[1]);
                                    fTextureData[index++] = (unsigned char)(255*intensity[2]);
                                    fTextureData[index++] = 255;
                                }
                            }
                        }
                        MHWRender::MTextureDescription desc;
                        {
                            desc.setToDefault2DTexture();
                            desc.fWidth = PHONG_TEXTURE_RES;
                            desc.fHeight = PHONG_TEXTURE_RES;
                            desc.fDepth = 1;
                            desc.fBytesPerRow = 4*PHONG_TEXTURE_RES;
                            desc.fBytesPerSlice = 4*PHONG_TEXTURE_RES*PHONG_TEXTURE_RES;
                            desc.fMipmaps = 1;
                            desc.fArraySlices = 6;
                            desc.fFormat = MHWRender::kR8G8B8A8_UNORM;
                            desc.fTextureType = MHWRender::kCubeMap;
                        }

                        MHWRender::MTextureManager *theTextureManager = theRenderer->getTextureManager();
                        if(theTextureManager)
                        {
                            fTexture = theTextureManager->acquireTexture( newTextureName, desc, fTextureData );
                        }
                    }

                    // Mark the texture clean
                    fShaderNode->resetAttributesChangedVP2();
                }
            }
        }

        static void printContextInformation( const MHWRender::MDrawContext & context )
        {
            TRACE_API_CALLS("hwPhongShaderOverride::printContextInformation");

            // Sample code to print out the information found in MDrawContext
            //
            MFloatArray dtuple;
            MFloatMatrix dmatrix;
            printf("Draw Context Diagnostics {\n");
            {
                dtuple = context.getTuple( MHWRender::MDrawContext::kViewPosition );
                printf("\tView position: %f, %f, %f\n", dtuple[0], dtuple[1], dtuple[2]);
                dtuple = context.getTuple( MHWRender::MDrawContext::kViewPosition );
                printf("\tView dir : %f, %f, %f\n", dtuple[0], dtuple[1], dtuple[2]);
                dtuple = context.getTuple( MHWRender::MDrawContext::kViewUp );
                printf("\tView up : %f, %f, %f\n", dtuple[0], dtuple[1], dtuple[2]);
                dtuple = context.getTuple( MHWRender::MDrawContext::kViewRight );
                printf("\tView right : %f, %f, %f\n", dtuple[0], dtuple[1], dtuple[2]);
                printf("\n");

                MBoundingBox bbox = context.getSceneBox();
                MPoint bmin = bbox.min();
                MPoint bmax = bbox.max();
                printf("\tScene bounding box = %g,%g,%g -> %g,%g,%g\n",
                    bmin[0],bmin[1],bmin[2],
                    bmax[0],bmax[1],bmax[2]);

                int originX; int originY; int width; int height;
                context.getRenderTargetSize( width, height );
                printf("\tRender target size: %d x %d\n", width, height);
                context.getViewportDimensions( originX, originY, width, height);
                printf("\tViewport dimensions: %d, %d, -> %d, %d\n", originX, originY, width, height);
                MStatus xStatus;
                printf("\tView direction along neg z = %d\n", context.viewDirectionAlongNegZ( &xStatus ));

                unsigned int lightCount = context.numberOfActiveLights();
                MFloatPoint position;
                MFloatVector direction;
                float intensity;
                MColor color;
                bool hasDirection;
                bool hasPosition;

                bool visualizeLighting = false;
                // Reuse for each light.
                MFloatMatrix identity;
                if (visualizeLighting )
                {
                    for (unsigned int i=0; i<8; i++)
                    {
                        GLenum light = GL_LIGHT0+i;
                        glDisable(light);
                    }
                    if (!lightCount)
                        glDisable(GL_LIGHTING);
                    else
                        glEnable(GL_LIGHTING);
                }

                for (unsigned int i=0; i<lightCount; i++)
                {
                    context.getLightInformation( i,
                                     position, direction,
                                     intensity, color, hasDirection,
                                     hasPosition );
                    printf("\tLight %d {\n", i);
                    printf("\t\tDirectional %d, Positional %d\n", hasDirection, hasPosition);
                    printf("\t\tDirection = %g, %g, %g. Position = %g, %g, %g\n",
                        direction[0],direction[1],direction[2],
                        position[0], position[1], position[2]);
                    printf("\t\tColor = %g, %g, %g\n", color[0], color[1], color[2]);
                    printf("\t\tIntensity = %g\n", intensity);
                    printf("\t}\n");

                    if (visualizeLighting)
                    {
                        GLenum light = GL_LIGHT0+i;

                        // Set light parameters
                        float ambient[3] = { 0.0f, 0.0f, 0.0f };
                        float specular[3] = { 1.0f, 1.0f, 1.0f };
                        glLightfv(light, GL_AMBIENT, &ambient[0]);
                        color[0] *= intensity;
                        color[1] *= intensity;
                        color[2] *= intensity;
                        glLightfv(light, GL_DIFFUSE, &color[0]);
                        glLightfv(light, GL_SPECULAR, &specular[0]);

                        glLightf(light,  GL_CONSTANT_ATTENUATION, 1.0f);
                        glLightf(light,  GL_LINEAR_ATTENUATION, 0.0f);
                        glLightf(light,  GL_QUADRATIC_ATTENUATION, 0.0f);

                        glPushMatrix();
                        glLoadMatrixf( identity.matrix[0] );

                        // Set light position
                        if (hasPosition)
                            glLightfv(light, GL_POSITION, &position[0]);
                        else {
                            position[0] = position[1] = position[2] = 0.0f;
                            glLightfv(light, GL_POSITION, &position[0]);
                        }

                        // Set rest of parameters.
                        if (hasDirection)
                        {
                            glLightf(light,  GL_SPOT_CUTOFF, 90.0f);
                            glLightf(light,  GL_SPOT_EXPONENT, 64.0f);
                            glLightfv(light, GL_SPOT_DIRECTION, &direction[0]);
                        }
                        else
                        {
                            glLightf(light, GL_SPOT_CUTOFF, 180.0f);
                            glLightf(light, GL_SPOT_EXPONENT, 0.0f);
                        }

                        glEnable(light);
                        glPopMatrix();
                    }
                }

            }
            printf("}\n");
        }

    protected:
        hwPhongShaderOverride(const MObject& obj)
        : MHWRender::MPxShaderOverride(obj)
        , fShaderNode(NULL)
        , fTextureData(NULL)
        , fTexture(NULL)
        {
        }

        // override blend state when there is blending
        static const MHWRender::MBlendState *sBlendState;

        // Current hwPhongShader node associated with the shader override.
        // Updated during doDG() time.
        hwPhongShader *fShaderNode;
        // Transparency value
        float fTransparency;
        // Temporary system buffer for creating textures
        unsigned char* fTextureData;
        // OGS texture
        MHWRender::MTexture *fTexture;
    };

    const MHWRender::MBlendState* hwPhongShaderOverride::sBlendState = NULL;

    MStatus initializePlugin( MObject obj )
    {
        TRACE_API_CALLS("initializePlugin");
        MStatus   status;

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

        MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");
        status = plugin.registerNode( "hwPhongShader", hwPhongShader::id,
                                      hwPhongShader::creator, hwPhongShader::initialize,
                                      MPxNode::kHwShaderNode, &UserClassify );
        if (!status) {
            status.perror("registerNode");
            return status;
        }

        plugin.registerDragAndDropBehavior("hwPhongShaderBehavior",
                                           hwPhongShaderBehavior::creator);

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

        return MS::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj )
    {
        TRACE_API_CALLS("uninitializePlugin");
        MStatus   status;

        MFnPlugin plugin( obj );

        // Unregister all chamelion shader nodes
        plugin.deregisterNode( hwPhongShader::id );
        if (!status) {
            status.perror("deregisterNode");
            return status;
        }

        plugin.deregisterDragAndDropBehavior("hwPhongShaderBehavior");

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

        return MS::kSuccess;
    }

    void * hwPhongShader::creator()
    {
        TRACE_API_CALLS("creator");
        return new hwPhongShader();
    }

    MStatus hwPhongShader::initialize()
    {
        // Make sure that all attributes are cached internal for
        // optimal performance !

        TRACE_API_CALLS("initialize");
        MFnNumericAttribute nAttr;

        // Create input attributes
        aColor = nAttr.createColor( "color", "c");
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.1f, 0.1f, 0.1f);
        nAttr.setCached( true );
        nAttr.setInternal( true );
        nAttr.setAffectsAppearance( true );

        aTransparency = nAttr.create( "transparency", "tr", MFnNumericData::kFloat );
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.0f);
        nAttr.setMax(1.0f);
        nAttr.setMin(0.0f);
        nAttr.setCached( true );
        nAttr.setInternal( true );
        nAttr.setAffectsAppearance( true );

        aDiffuseColor = nAttr.createColor( "diffuseColor", "dc" );
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.f, 0.5f, 0.5f);
        nAttr.setCached( true );
        nAttr.setInternal( true );
        nAttr.setAffectsAppearance( true );

        aSpecularColor = nAttr.createColor( "specularColor", "sc" );
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f, 0.5f, 0.5f);
        nAttr.setCached( true );
        nAttr.setInternal( true );
        nAttr.setAffectsAppearance( true );

        // This is defined as a point, so that users can easily enter
        // values beyond 1.
        aShininess = nAttr.createPoint( "shininess", "sh" );
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(100.0f, 100.0f, 100.0f);
        nAttr.setCached( true );
        nAttr.setInternal( true );
        nAttr.setAffectsAppearance( true );

        aGeometryShape = nAttr.create( "geometryShape", "gs", MFnNumericData::kInt );
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0);
        nAttr.setCached( true );
        nAttr.setInternal( 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

        addAttribute(aColor);
        addAttribute(aTransparency);
        addAttribute(aDiffuseColor);
        addAttribute(aSpecularColor);
        addAttribute(aShininess);
        addAttribute(aGeometryShape);

        attributeAffects (aColor,           outColor);
        attributeAffects (aTransparency,    outColor);
        attributeAffects (aDiffuseColor,    outColor);
        attributeAffects (aSpecularColor,   outColor);
        attributeAffects (aShininess,       outColor);

        return MS::kSuccess;
    }

    // DESCRIPTION:
    //
    MStatus hwPhongShader::compute(
    const MPlug&      plug,
          MDataBlock& block )
    {
        TRACE_API_CALLS("compute");

        if ((plug != outColor) && (plug.parent() != outColor))
            return MS::kUnknownParameter;

        MFloatVector & color  = block.inputValue( aDiffuseColor ).asFloatVector();

        // set output color attribute
        MDataHandle outColorHandle = block.outputValue( outColor );
        MFloatVector& outColor = outColorHandle.asFloatVector();
        outColor = color;

        outColorHandle.setClean();
        return MS::kSuccess;
    }

    /* virtual */
    bool hwPhongShader::setInternalValueInContext( const MPlug &plug,
                                                   const MDataHandle &handle,
                                                   MDGContext & )
    {
        bool handledAttribute = false;
        if (plug == aColor)
        {
            handledAttribute = true;
            float3 & val = handle.asFloat3();
            if (val[0] != mAmbientColor[0] ||
                val[1] != mAmbientColor[1] ||
                val[2] != mAmbientColor[2])
            {
                mAmbientColor[0] = val[0];
                mAmbientColor[1] = val[1];
                mAmbientColor[2] = val[2];
                mAttributesChanged = true;
                mAttributesChangedVP2 = true;
            }
        }
        else if (plug == aTransparency)
        {
            handledAttribute = true;
            float val = handle.asFloat();
            if (val != mTransparency)
            {
                mTransparency = val;
                mAttributesChanged = true;
                mAttributesChangedVP2 = true;
            }
        }
        else if (plug == aDiffuseColor)
        {
            handledAttribute = true;
            float3 & val = handle.asFloat3();
            if (val[0] != mDiffuseColor[0] ||
                val[1] != mDiffuseColor[1] ||
                val[2] != mDiffuseColor[2])
            {
                mDiffuseColor[0] = val[0];
                mDiffuseColor[1] = val[1];
                mDiffuseColor[2] = val[2];
                mAttributesChanged = true;
                mAttributesChangedVP2 = true;
            }
        }
        else if (plug == aSpecularColor)
        {
            handledAttribute = true;
            float3 & val = handle.asFloat3();
            if (val[0] != mSpecularColor[0] ||
                val[1] != mSpecularColor[1] ||
                val[2] != mSpecularColor[2])
            {
                mSpecularColor[0] = val[0];
                mSpecularColor[1] = val[1];
                mSpecularColor[2] = val[2];
                mAttributesChanged = true;
                mAttributesChangedVP2 = true;
            }
        }
        else if (plug == aShininess)
        {
            handledAttribute = true;
            float3 & val = handle.asFloat3();
            if (val[0] != mShininess[0] ||
                val[1] != mShininess[1] ||
                val[2] != mShininess[2])
            {
                mShininess[0] = val[0];
                mShininess[1] = val[1];
                mShininess[2] = val[2];
                mAttributesChanged = true;
                mAttributesChangedVP2 = true;
            }
        }
        else if (plug == aGeometryShape)
        {
            handledAttribute = true;
            mGeometryShape = handle.asInt();
        }

        return handledAttribute;
    }

    /* virtual */
    bool
    hwPhongShader::getInternalValueInContext( const MPlug& plug,
                                             MDataHandle& handle,
                                             MDGContext&)
    {
        bool handledAttribute = false;
        if (plug == aColor)
        {
            handledAttribute = true;
            handle.set( mAmbientColor[0], mAmbientColor[1], mAmbientColor[2] );
        }
        if (plug == aTransparency)
        {
            handledAttribute = true;
            handle.set( mTransparency );
        }
        else if (plug == aDiffuseColor)
        {
            handledAttribute = true;
            handle.set( mDiffuseColor[0], mDiffuseColor[1], mDiffuseColor[2] );
        }
        else if (plug == aSpecularColor)
        {
            handledAttribute = true;
            handle.set( mSpecularColor[0], mSpecularColor[1], mSpecularColor[2] );
        }
        else if (plug == aShininess)
        {
            handledAttribute = true;
            handle.set( mShininess[0], mShininess[1], mShininess[2] );
        }
        else if (plug == aGeometryShape)
        {
            handledAttribute = true;
            handle.set( (int) mGeometryShape );
        }
        return handledAttribute;
    }

    /* virtual */
    MStatus hwPhongShader::bind(const MDrawRequest& request, M3dView& view)

    {
        TRACE_API_CALLS("bind");

        init_Phong_texture ();

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwPhongShader::glBind(const MDagPath&)
    {
        TRACE_API_CALLS("glBind");

        init_Phong_texture ();

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwPhongShader::unbind(const MDrawRequest& request, M3dView& view)
    {
        TRACE_API_CALLS("unbind");

        // The texture may have been allocated by the draw; it's kept
        // around for use again. When scene new or open is performed this
        // texture will be released in releaseEverything().

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwPhongShader::glUnbind(const MDagPath&)
    {
        TRACE_API_CALLS("glUnbind");

        // The texture may have been allocated by the draw; it's kept
        // around for use again. When scene new or open is performed this
        // texture will be released in releaseEverything().

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwPhongShader::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)
    {
        TRACE_API_CALLS("geometry");
        MStatus stat = MStatus::kSuccess;

        if (mGeometryShape != 0)
            drawDefaultGeometry();
        else
            stat = draw( prim, writable, indexCount, indexArray, vertexCount,
                    vertexIDs, vertexArray, normalCount, normalArrays, colorCount,
                    colorArrays, texCoordCount, texCoordArrays);
        return stat;
    }

    /* virtual */
    MStatus hwPhongShader::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)
    {
        TRACE_API_CALLS("glGeometry");
        MStatus stat = MStatus::kSuccess;

        if (mGeometryShape != 0)
            drawDefaultGeometry();
        else
            stat = draw( prim, writable, indexCount, indexArray, vertexCount,
                        vertexIDs, vertexArray, normalCount, normalArrays, colorCount,
                        colorArrays, texCoordCount, texCoordArrays);
        return stat;
    }

    /* virtual */
    MStatus
    hwPhongShader::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;
            if (mGeometryShape == 2)
            {
                gshape = MHardwareRenderer::kDefaultCube;
            }
            else if (mGeometryShape == 3)
            {
                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 )
            {
                glPushAttrib ( GL_ALL_ATTRIB_BITS );

                // 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();
                    // Rotate the cube a bit so we don't see it head on
                    if (gshape ==  MHardwareRenderer::kDefaultCube)
                        glRotatef( 45, 1.0, 1.0, 1.0 );
                    else if (gshape == MHardwareRenderer::kDefaultPlane)
                        glScalef( 1.5, 1.5, 1.5 );
                    else
                        glScalef( 1.0, 1.0, 1.0 );
                }

                // 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();
            }
            else
            {
                pRenderer->dereferenceGeometry( pGeomData, numberOfData );
            }
        }
        return status;
    }

    void
    hwPhongShader::drawTheSwatch( MGeometryData* pGeomData,
                                       unsigned int* pIndexing,
                                       unsigned int  numberOfData,
                                       unsigned int  indexCount )
    {
        TRACE_API_CALLS("drwaTheSwatch");

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

        if ( mAttributesChanged || (phong_map_id == 0))
        {
            init_Phong_texture ();
        }

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

        CubeMapTextureDrawUtility::bind( phong_map_id );

        // Draw default geometry
        {
            if (pGeomData)
            {
                glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );

                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 (vertexData && normalData && pIndexing )
                    glDrawElements ( GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, pIndexing );

                glPopClientAttrib();

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

        CubeMapTextureDrawUtility::unbind();
    }

    void hwPhongShader::drawDefaultGeometry()
    {
        TRACE_API_CALLS("drawDefaultGeometry");

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

        CubeMapTextureDrawUtility::bind( phong_map_id );

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

            MHardwareRenderer::GeometricShape gshape = MHardwareRenderer::kDefaultSphere;
            if (mGeometryShape == 2)
            {
                gshape = MHardwareRenderer::kDefaultCube;
            }
            else if (mGeometryShape == 3)
            {
                gshape = MHardwareRenderer::kDefaultPlane;
            }

            // Get data references
            MGeometryData * pGeomData =
                pRenderer->referenceDefaultGeometry( gshape, numberOfData, pIndexing, indexCount);

            if (pGeomData)
            {
                glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );

                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 (vertexData && normalData && pIndexing )
                    glDrawElements ( GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, pIndexing );

                glPopClientAttrib();

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

        CubeMapTextureDrawUtility::unbind();
    }

    MStatus hwPhongShader::draw(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)
    {
        TRACE_API_CALLS("draw");

        if ( prim != GL_TRIANGLES && prim != GL_TRIANGLE_STRIP) {
            return MS::kFailure;
        }

        CubeMapTextureDrawUtility::bind( phong_map_id );

        // Draw the surface.
        //
        {
            bool needBlending = false;
            if (mTransparency > 0.0f)
            {
                needBlending = true;
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                glColor4f(1.0, 1.0, 1.0, mTransparency );
            }
            else
                glColor4f(1.0, 1.0, 1.0, 1.0f);

            glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );
            // GL_VERTEX_ARRAY does not necessarily need to be
            // enabled, as it should be enabled before this routine
            // is valled.
            glEnableClientState( GL_VERTEX_ARRAY );
            glEnableClientState( GL_NORMAL_ARRAY );

            glVertexPointer ( 3, GL_FLOAT, 0, &vertexArray[0] );
            glNormalPointer (    GL_FLOAT, 0, &normalArrays[0][0] );

            glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );

            // The client attribute is already being popped. You
            // don't need to reset state here.
            //glDisableClientState( GL_NORMAL_ARRAY );
            //glDisableClientState( GL_VERTEX_ARRAY );
            glPopClientAttrib();

            if (needBlending)
            {
                glColor4f(1.0, 1.0, 1.0, 1.0f);
                glDisable(GL_BLEND);
            }
        }

        CubeMapTextureDrawUtility::unbind();

        return MS::kSuccess;
    }

    /* virtual */
    int hwPhongShader::normalsPerVertex()
    {
        TRACE_API_CALLS("normalsPerVertex");
        return 1;
    }

    /* virtual */
    int hwPhongShader::texCoordsPerVertex()
    {
        TRACE_API_CALLS("texCoordsPerVertex");
        return 0;
    }

    /* virtual */
    int hwPhongShader::getTexCoordSetNames(MStringArray& names)
    {
        return 0;
    }

    /* virtual */
    bool hwPhongShader::hasTransparency()
    {
        return (mTransparency > 0.0f);
    }