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

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