pnTriangles/pnTrianglesNode.cpp
 
 
 
pnTriangles/pnTrianglesNode.cpp
//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

//
// NOTE: PLEASE READ THE README.TXT FILE FOR INSTRUCTIONS ON
// COMPILING AND USAGE REQUIREMENTS.
//
// File: pnTrianglesNode.cpp
//
// Dependency Graph Node: pnTriangles
//
// Description:
//
//              Hardware shader plugin to perform :
//              1) ATI PN triangle tessellation on geometry, if the extension
//                      ATI_pn_triangles is supported in hardware.
//              2) A simple vertex program using EXT_vertex_program, if the
//                      extension is supported.
//              3) A simple fragement program using AT_fragment_program, if the
//                      extension is supported.
//
//              This is an excerpt from the PN triangle extension
//              specification:
//
//              "When PN Triangle generation is enabled, each triangle-based geometric 
//          primitive is replaced with a new curved surface using the primitive 
//              vertices as control points.  The intent of PN Triangles 
//              are to take a set of triangle-based geometry and algorithmically 
//              tessellate it into a more organic shape with a smoother silhouette. 
//              The new surface can either linearly or quadratically interpolate the 
//              normals across the patch.  The vertices can be either linearly or
//              cubically interpolated across the patch.  Linear interpolation
//              of the points would be useful for getting more sample points for 
//              lighting on the same geometric shape.  All other vertex information
//              (colors, texture coordinates, fog coordinates, and vertex weights) are 
//              interpolated linearly across the patch."
//
//              This plugin is ATI Radeon specific.
//
#include <maya/MIOStream.h>
#include <math.h>

#include "pnTrianglesNode.h"

#include <maya/MFnPlugin.h>
#include <maya/MString.h>

#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFloatVector.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MMaterial.h>
#include <maya/MDrawData.h>
#include <maya/MColor.h>
#include <maya/MDagPath.h>
#include <maya/MPoint.h>
#include <maya/MIOStream.h>

MTypeId     pnTriangles::id( 0x00105448 );

#define         pnDefaultMaxTessellation        0               // Default max tessellation

// Attributes
// 
MObject     pnTriangles::attrSubdivisions;  // Subdivision level      
MObject         pnTriangles::attrColored;               // Display colored
MObject         pnTriangles::attrTextured;              // Display textured
MObject         pnTriangles::attrWireframe;             // Display wireframe
MObject         pnTriangles::attrNormalMode;    // Normal evaluation mode
MObject         pnTriangles::attrPointMode;             // Point evaluation mode
MObject         pnTriangles::attrEnableVertexProgram; // Enable vertex program
MObject         pnTriangles::attrEnablePixelProgram; // Enable pixel program

pnTriangles::pnTriangles() 
:       fSubdivisions( 0 ),                                                                     // Default 0 level of subdivision
        fNumTextureCoords( 0 ),                                                         // Default not-textured
        fNumNormals( 1 ),                                                                       // Default shaded
        fNumColors( 0 ),                                                                        // Default not-colored
        fPointMode( kPointCubic ),                                                      // Default cubic
        fNormalMode( kNormalQuadratic ),                                        // Default quadratic
        fMaxTessellationLevel( pnDefaultMaxTessellation ),      // 0 maximum default
        fWireframe( false ),                                                            // Default filled
        fInTexturedMode( false ),                                                       // Keep track of textured mode
        fTestVertexProgram( false ),
        fTestFragmentProgram( false ),
        fVertexShaderId( -1 ),
        fFragmentShaderId( -1 )
{
        fExtensionSupported[kPNTriangesEXT ] = false;
        fExtensionSupported[kVertexShaderEXT] = false;
        fExtensionSupported[kFragmentShaderEXT] = false;


        // Initialize required extension(s)
        //

        // Initialize PN-triangles
        fExtensionSupported[kPNTriangesEXT] = initialize_ATI_Extension("GL_ATI_pn_triangles");
        // Get the maximum tessellation level supported by the extension
        //
        if (fExtensionSupported[kPNTriangesEXT])
        {
                int val;
                glGetIntegerv( GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI, &val );
                fMaxTessellationLevel = val;
        }

        fExtensionSupported[kVertexShaderEXT] = initialize_ATI_Extension("GL_EXT_vertex_shader");
        fExtensionSupported[kFragmentShaderEXT] = initialize_ATI_Extension("GL_ATI_fragment_shader");
}

pnTriangles::~pnTriangles() 
//
// Description:
//              Destructor 
{
        // Delete any vertex shader
        //
        if (fExtensionSupported[kVertexShaderEXT])
        {
                if (fVertexShaderId != -1)
                        glDeleteVertexShaderEXT(fVertexShaderId);
        }
}

bool pnTriangles::getInternalValue( const MPlug &plug, MDataHandle &dh ) 
//
// Description:
//              Get internally cached values when attribute is queried
//
{
    if( plug == pnTriangles::attrSubdivisions ) 
        {
                dh.set( int(fSubdivisions) );
                return true;
    }
        else if ( plug == pnTriangles::attrColored)
        {
                dh.set( (fNumColors > 0) );
                return true;
        }
        else if ( plug == pnTriangles::attrWireframe)
        {
                dh.set( fWireframe > 0);
                return true;
        }
        else if ( plug == pnTriangles::attrTextured)
        {
                dh.set( (fNumTextureCoords > 0) );
                return true;
        }
        else if ( plug == pnTriangles::attrNormalMode )
        {
                if (fNormalMode == kNormalLinear)
                        dh.set( 0 );
                else
                        dh.set( 1 );
                return true;
        }
        else if ( plug == pnTriangles::attrPointMode )
        {
                if (fPointMode == kPointLinear)
                        dh.set( 0 );
                else
                        dh.set( 1 );
                return true;
        }
        else if ( plug == pnTriangles::attrEnableVertexProgram)
        {
                dh.set( fTestVertexProgram );
                return true;
        }
        else if ( plug == pnTriangles::attrEnablePixelProgram)
        {
                dh.set( fTestFragmentProgram );
                return true;
        }
        return false;
}

bool pnTriangles::setInternalValue( const MPlug &plug, const MDataHandle &dh ) 
//
// Description:
//              Set internally cached values when attribute changes
//
{
   if( plug == pnTriangles::attrSubdivisions ) 
        {
                fSubdivisions = dh.asInt();

                // Make sure we don't go above the maximum allowed by 
                // the extension
                if (fSubdivisions > fMaxTessellationLevel)
                        fSubdivisions = fMaxTessellationLevel;
                return true;
    }
        else if ( plug == pnTriangles::attrColored)
        {
                if (dh.asBool() == true)
                        fNumColors = 1;
                else
                        fNumColors = 0;
                return true;
        }
        else if ( plug == pnTriangles::attrWireframe)
        {
                if (dh.asBool() == true)
                        fWireframe = 1;
                else
                        fWireframe = 0;
                return true;
        }
        else if ( plug == pnTriangles::attrTextured)
        {
                if (dh.asBool() == true)
                        fNumTextureCoords = 1;
                else
                        fNumTextureCoords = 0;
                return true;
        }
        else if ( plug == pnTriangles::attrNormalMode )
        {
                if (dh.asInt() == 0)
                        fNormalMode = kNormalLinear;
                else
                        fNormalMode = kNormalQuadratic;
                return true;
        }
        else if ( plug == pnTriangles::attrPointMode )
        {
                if (dh.asInt() == 0)
                        fPointMode = kPointLinear;
                else
                        fPointMode = kPointCubic;
                return true;
        }
        else if ( plug == pnTriangles::attrEnableVertexProgram )
        {
                if (dh.asBool() == true)
                {
                        if (fExtensionSupported[kVertexShaderEXT])
                                fTestVertexProgram = 1;
                        else
                                fTestVertexProgram = 0;
                }
                else
                        fTestVertexProgram = 0;

                return true;
        }
        else if ( plug == pnTriangles::attrEnablePixelProgram )
        {
#ifdef _FRAGMENT_PROGRAM_READY_
                if (dh.asBool() == true)
                        if (fExtensionSupported[kFragmentShaderEXT])
                                fTestFragmentProgram = 1;
                        else
                                fTestFragmentProgram = 0;
                else
                        fTestFragmentProgram = 0;
#else
                fTestFragmentProgram = 0;
#endif
                return true;
        }
    return false;
}

void* pnTriangles::creator()
//
//      Description:
//              Create new pnTriangles class
//
//      Return Value:
//              a new object of this type
//
{
        return new pnTriangles();
}

MStatus pnTriangles::initialize()
//
//      Description:
//              Initialize class
//      Return Values:
//              MS::kSuccess
//              MS::kFailure
//              
{
        MFnNumericAttribute nAttr;
        MStatus                         stat;

        // Subivision attribute
        attrSubdivisions = nAttr.create( "subdivisions", "sd", MFnNumericData::kInt, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
        nAttr.setMin(0);
    nAttr.setMax(10);
    nAttr.setDefault(1);
        nAttr.setKeyable(true);

        attrColored = nAttr.create( "colored", "cl", MFnNumericData::kBoolean, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
    nAttr.setDefault( false );
        nAttr.setKeyable(true);

        attrTextured = nAttr.create( "textured", "tx", MFnNumericData::kBoolean, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
    nAttr.setDefault( false );
        nAttr.setKeyable(true);

        attrWireframe = nAttr.create( "wireframe", "wf", MFnNumericData::kBoolean, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
    nAttr.setDefault( false );
        nAttr.setKeyable(true);
        
        // Normal mode attribute
        attrNormalMode = nAttr.create( "quadraticNormals", "qn", MFnNumericData::kInt, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
        nAttr.setMin(0); // Linear
    nAttr.setMax(1); // Quadratic
    nAttr.setDefault(1);
        nAttr.setKeyable(true);

        // Point mode attribute
        attrPointMode= nAttr.create( "cubicPoints", "cp", MFnNumericData::kInt, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
        nAttr.setMin(0); // Linear
    nAttr.setMax(1); // Cubic
    nAttr.setDefault(1);
        nAttr.setKeyable(true);

        // Enable vertex program
        attrEnableVertexProgram = nAttr.create( "vertexProgram", "vp", MFnNumericData::kBoolean, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
    nAttr.setDefault( false );
        nAttr.setKeyable(true);
                
        // Enable pixel program
        attrEnablePixelProgram = nAttr.create( "pixelProgram", "pp", MFnNumericData::kBoolean, 1 );
        nAttr.setStorable(true);
        nAttr.setInternal(true);
    nAttr.setDefault( false );
        nAttr.setKeyable(true);

        // Add the attributes we have created to the node
        //
        stat = addAttribute( attrSubdivisions );
        if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( attrColored );
        if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( attrWireframe );
        if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( attrTextured );
        if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( attrNormalMode );
        if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( attrPointMode );
        if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( attrEnableVertexProgram );
        if (!stat) { stat.perror("addAttribute"); return stat;}
        stat = addAttribute( attrEnablePixelProgram );
        if (!stat) { stat.perror("addAttribute"); return stat;}

        return MS::kSuccess;
}

MStatus pnTriangles::getFloat3(MObject attr, float value[3])
{
        MStatus status;
        // Get the attr to use
        //
        MPlug   plug(thisMObject(), attr);

        MObject object;

        status = plug.getValue(object);
        if (!status)
        {
                status.perror("pnTrianglesNode::bind plug.getValue.");
                return status;
        }


        MFnNumericData data(object, &status);
        if (!status)
        {
                status.perror("pnTrianglesNode::bind construct data.");
                return status;
        }

        status = data.getData(value[0], value[1], value[2]);
        if (!status)
        {
                status.perror("pnTrianglesNode::bind get values.");
                return status;
        }

        return MS::kSuccess;
}

#define DEG_TO_RAD(_x_) (3.14159265358979323846264338327950F / 180.0F * (_x_))


void pnTriangles::bindVertexProgram(const MColor diffuse, 
                                                                        const MColor specular, 
                                                                        const MColor emission, 
                                                                        const MColor ambient)
//
// Description:
//              Sample vertex program which sets up a yellow diffuse
//              shader.
//                                                                      
{
        // See if extension is supported
        //
        if (!fExtensionSupported[kVertexShaderEXT])
                return;

        // Enable vertex shaders
        glEnable(GL_VERTEX_SHADER_EXT);

        // Generate a new vertex shader id, if none has been created.
        if (fVertexShaderId != -1)
        {
                glBindVertexShaderEXT (fVertexShaderId); 
                return;
        }

        fVertexShaderId = glGenVertexShadersEXT (1);

        //Initialize global parameter bindings
        GLuint Modelview = glBindParameterEXT (GL_MODELVIEW_MATRIX);
        GLuint Projection = glBindParameterEXT (GL_PROJECTION_MATRIX);
        // Q: does this read properly from vertex arrays or
        // do I need to define a variant pointer via glVariantPointerEXT ?
        //
        GLuint Vertex = glBindParameterEXT (GL_CURRENT_VERTEX_EXT);
        // GL_CURRENT_NORMAL comes from the glGetPointerTarget enums
        GLuint Normal = glBindParameterEXT (GL_CURRENT_NORMAL); 
        
        //GLuint lightPos = glBindLightParameterEXT( 0, GL_POSITION );

        // Bind a diffuse shader 
        glBindVertexShaderEXT (fVertexShaderId); 
        glBeginVertexShaderEXT ();
        {
                float direction[4] = { 0.57735f, 0.57735f, 0.57735f, 0.0f}; //direction vector (1,1,1) normalized
                GLuint lightDirection;
                GLuint diffMaterial;
                GLuint sceneAmbient;
                GLuint eyeVertex;
                GLuint clipVertex;
                GLuint eyeNormal;
                GLuint intensity;

                // generate local values
                eyeVertex = glGenSymbolsEXT (GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
                clipVertex = glGenSymbolsEXT (GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
                eyeNormal = glGenSymbolsEXT (GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
                intensity = glGenSymbolsEXT (GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);

                // generate constant values
                lightDirection = glGenSymbolsEXT (GL_VECTOR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1);
                diffMaterial = glGenSymbolsEXT (GL_VECTOR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1);
                sceneAmbient = glGenSymbolsEXT (GL_VECTOR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1);
                glSetLocalConstantEXT (lightDirection, GL_FLOAT, direction);
                glSetLocalConstantEXT (diffMaterial, GL_FLOAT, (void *)&(diffuse.r));
                glSetLocalConstantEXT (sceneAmbient, GL_FLOAT, (void *)&(ambient.r));
                glShaderOp2EXT (GL_OP_MULTIPLY_MATRIX_EXT, eyeVertex, Modelview, Vertex);
                glShaderOp2EXT (GL_OP_MULTIPLY_MATRIX_EXT, GL_OUTPUT_VERTEX_EXT, Projection, eyeVertex);

                // assumes no scaling/shearing in modelview matrix
                glShaderOp2EXT (GL_OP_MULTIPLY_MATRIX_EXT, eyeNormal, Modelview, Normal);
                glShaderOp2EXT (GL_OP_DOT3_EXT, intensity, lightDirection, eyeNormal);
                glShaderOp2EXT (GL_OP_ADD_EXT, intensity, sceneAmbient, intensity);
                glShaderOp2EXT (GL_OP_MUL_EXT, GL_OUTPUT_COLOR0_EXT, diffMaterial, intensity);
        }
        glEndVertexShaderEXT ();

}

void pnTriangles::bindFragmentProgram()
//
// Description:
//
//      THIS IS JUST SAMPLE CODE, MORE NEEDS TO BE DONE TO ACTUALLY USE
//      INCORPORATE A FRAGMENT SHADER.
//
{
        // See if extension is supported
        //
        if (!fExtensionSupported[kFragmentShaderEXT])
                return;

        // Enable vertex shaders
        glEnable(GL_FRAGMENT_SHADER_ATI);

        // Generate a new fragment shader id, if none has been created.
        if (fFragmentShaderId != -1)
        {
                glBindFragmentShaderATI (fFragmentShaderId);
                return;
        }

        fFragmentShaderId = glGenFragmentShadersATI(1);

        glBindFragmentShaderATI (fFragmentShaderId);
        glBeginFragmentShaderATI();

        // Place sample fragment shader code here ....

        glEndFragmentShaderATI();
}

/* virtual */
MStatus pnTriangles::bind(const MDrawRequest& request, M3dView& view)
//
// Description:
//              This bind demonstrates the usage of internal material
//              and texture properties. This shader must be connected
//              to the "hardwareShader" attribute of a lambert derived
//              shader.
//
{
        // Setup the view
        view.beginGL();
        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);

        MColor diffuse(1.0F, 1.0F, 0.0F, 1.0F);
        MColor specular(1.0F, 1.0F, 1.0F, 1.0F);
        MColor emission(0.0F, 0.0F, 0.0F, 1.0F);
        MColor ambient(0.2F, 0.2F, 0.2F, 1.0F);

        // Get the diffuse and specular colors
        //
        float shininess;
        bool hasTransparency = false;

        MMaterial material = request.material();
        fInTexturedMode = material.materialIsTextured();

        // Setting this to true will get the default "green" material back
        // since it will try and evaluate this shader, which internally
        // Maya does not understand -> thus giving the "green" material back
        bool useInternalMaterialSetting = false;
        
        if (!useInternalMaterialSetting)
        {
                material.getEmission( emission );
                material.getSpecular( specular );
                shininess = 13.0;
        }
        material.getHasTransparency( hasTransparency ); 

        if (!fInTexturedMode)
        {
                if (!fTestVertexProgram && !useInternalMaterialSetting)
                        material.getDiffuse( diffuse );
        }
        // In textured mode. Diffuse material is always white
        // for texture blends
        else
        {
                if (!useInternalMaterialSetting)
                        diffuse.r = diffuse.g = diffuse.b = diffuse.a = 1.0;
        }

        // Use a vertex program to set up shading
        //
        if (fTestVertexProgram)
        {
                bindVertexProgram(diffuse, specular, emission, ambient);
        }
        else if (fTestFragmentProgram)
        {
                bindFragmentProgram();
        }

        // Don't use a vertex program to set up shading
        //
        else
        {
                // Set up the material state
                //
                glEnable(GL_COLOR_MATERIAL);
                glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
                glColor4fv(&ambient.r);

                if (fInTexturedMode)
                {
                        glEnable( GL_TEXTURE_2D );
                        MDrawData drawData = request.drawData();
                        material.applyTexture( view, drawData );

                        float scaleS, scaleT, translateS, translateT, rotate;

                        material.getTextureTransformation(scaleS, scaleT, translateS,
                                                                                          translateT, rotate);

                        rotate = DEG_TO_RAD(-rotate);
                        float c = cosf(rotate);
                        float s = sinf(rotate);
                        translateS += ((c+s)/2.0F);
                        translateT += ((c-s)/2.0F);

                        glMatrixMode(GL_TEXTURE);
                        glPushMatrix();
                        glLoadIdentity();

                        if(scaleS != 1.0f || scaleT != 1.0f)
                                glScalef(1.0f/scaleS, 1.0f/scaleT, 1.0f);
                        if(translateS != 0.0f || translateT != 0.0f)
                                glTranslatef(0.5f-translateS, 0.5f-translateT, 0.0f);
                        else
                                glTranslatef(0.5f, 0.5f, 0.0f);

                        if(rotate != 0.0f)
                                glRotatef(-rotate, 0.0f, 0.0f, 1.0f);

                        glMatrixMode(GL_MODELVIEW);
                }

                if (!useInternalMaterialSetting)
                {
                        glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
                        glColor4fv(&diffuse.r);
                        glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
                        glColor4fv(&specular.r);
                        glColorMaterial(GL_FRONT_AND_BACK, GL_EMISSION);
                        glColor4fv(&emission.r);
                        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
                }
                else
                {
                        const MDagPath dagPath =  request.multiPath();
                        material.evaluateMaterial( view, dagPath );
                        material.setMaterial(dagPath, hasTransparency);
                }
        }

        // Do PN triangles in hardware, or do nothing
        // if LOD = 0
        if (fExtensionSupported[kPNTriangesEXT] || (fSubdivisions == 0))
        {
                if (fSubdivisions != 0)
                {
                        glEnable( GL_PN_TRIANGLES_ATI );

                        // Set point mode
                        //
                        if (fPointMode == kPointLinear)
                                glPNTrianglesiATI( GL_PN_TRIANGLES_POINT_MODE_ATI, 
                                                                   GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI);
                        else
                                glPNTrianglesiATI( GL_PN_TRIANGLES_POINT_MODE_ATI, 
                                                                   GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI);

                        // Set normal mode
                        //
                        if (fNormalMode == kNormalLinear)
                                glPNTrianglesiATI( GL_PN_TRIANGLES_NORMAL_MODE_ATI, 
                                                                   GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI);
                        else
                                glPNTrianglesiATI( GL_PN_TRIANGLES_NORMAL_MODE_ATI, 
                                                                   GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI);

                        // Set tessellation level
                        //
                        glPNTrianglesiATI( GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI,
                                                          fSubdivisions );
                }
        }

        view.endGL();

        return MS::kSuccess;
}

/* virtual */
MStatus pnTriangles::unbind(const MDrawRequest& request,
                                                    M3dView& view)
//
// Description:
//              
//
{
        view.beginGL();

        // Disable vertex shader
        if (fTestVertexProgram)
        {
                glDisable(GL_VERTEX_SHADER_EXT);
        }
        // Disable fragment shader
        else if (fTestFragmentProgram)
        {
                glDisable(GL_FRAGMENT_SHADER_ATI);
        }
        // Disable regular material state
        else
        {
                glDisable( GL_TEXTURE_2D );
                glDisable(GL_COLOR_MATERIAL);

                if (fInTexturedMode)
                {
                        glMatrixMode(GL_TEXTURE);
                        glPopMatrix();
                        glMatrixMode(GL_MODELVIEW);
                }
        }

        // Disable PN triangles extension if needed
        //
        if (fExtensionSupported[kPNTriangesEXT] || (fSubdivisions == 0))
        {
                if (fSubdivisions != 0)
                        glDisable( GL_PN_TRIANGLES_ATI );
        }
        
        glPopClientAttrib();
        glPopAttrib();

        view.endGL();

        return MS::kSuccess;
}

unsigned int pnTriangles:: computePNTriangles(const float * vertexArray,
                                                                          const float * normalArray,
                                                                          const float * texCoordArray,
                                                                          float * pnVertexArray,
                                                                          float * pnNormalArray,
                                                                          float * pnTexCoordArray,
                                                                          float * pnColorArray)
{
        unsigned int triangleCount = 0;
        // Software implementation left to the user...
        return triangleCount;
}

/* virtual */
MStatus pnTriangles::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)
{
        // We assume triangles here.
        //
        if (prim != GL_TRIANGLES)
                return MS::kSuccess;    // Should call parent class here !

        view.beginGL();

        if (fWireframe)
                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

        bool needColors = (fNumColors && colorCount);
        bool needTexCoords = (fNumTextureCoords && texCoordCount);
        bool needNormals = (fNumNormals && normalCount);

        if (!fExtensionSupported[kPNTriangesEXT])
        {
                // Write code here to support PN-triangles in software...
        }

        glVertexPointer(3, GL_FLOAT, 0, vertexArray);
        glEnableClientState(GL_VERTEX_ARRAY);

        if (needNormals)
        {
                glNormalPointer(GL_FLOAT, 0, normalArrays[0]);
                glEnableClientState(GL_NORMAL_ARRAY);
        }
        if (needTexCoords)
        {
                glTexCoordPointer(2, GL_FLOAT, 0, texCoordArrays[0]);
                glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        }
        if (needColors)
        {
                // Override material color
                glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
                glColorPointer(4, GL_FLOAT, 0, colorArrays[0]);
                glEnableClientState(GL_COLOR_ARRAY);
        }

        glDrawElements(prim, indexCount, GL_UNSIGNED_INT, indexArray);

        if (fWireframe)
                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

        view.endGL();


        return MS::kSuccess;
}

/* virtual */
int     pnTriangles::normalsPerVertex()
{
        return fNumNormals;
}

/* virtual */
int     pnTriangles::texCoordsPerVertex()
{
        if (fInTexturedMode)
                return fNumTextureCoords;
        else
                return 0;
}

/* virtual */
int     pnTriangles::colorsPerVertex()
{
        return fNumColors;
}

// Main plugin interface
MStatus initializePlugin( MObject obj )
//
//      Description:
//              Initialize plugin
//
//      Arguments:
//              obj - a handle to the plug-in object (use MFnPlugin to access it)
//
{ 
        MStatus   status;

        const MString UserClassify( "shader/surface/utility" );
        
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");

        status = plugin.registerNode( "pnTriangles", pnTriangles::id, pnTriangles::creator,
                                                                  pnTriangles::initialize, MPxNode::kHwShaderNode, &UserClassify  );
        if (!status) {
                status.perror("registerNode");
                return status;
        }

        return status;
}

MStatus uninitializePlugin( MObject obj)
//
//      Description:
//              Deregister node
//
//      Arguments:
//              obj - a handle to the plug-in object (use MFnPlugin to access it)
//
{
        MStatus   status;
        MFnPlugin plugin( obj );

        status = plugin.deregisterNode( pnTriangles::id );
        if (!status) {
                status.perror("deregisterNode");
                return status;
        }

        return status;
}