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