cgfxEffectDef.cpp

//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
//
// cgfxAttrDef holds the "definition" of an attribute on a cgfxShader
// node.  This definition includes all the Maya attributes plus the
// CGeffect parameter index.
//

//

#include <maya/MHardwareRenderer.h>
#include <maya/MGlobal.h>
#include "cgfxEffectDef.h"
#include "cgfxShaderNode.h"
#include "cgfxPassStateSetter.h"

#ifdef _WIN32
#else
#   include <sys/timeb.h>
#   include <string.h>
#
#   define stricmp strcasecmp
#   define strnicmp strncasecmp
#endif

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

//
// A per-vertex attribute on a shader
//

cgfxVertexAttribute::cgfxVertexAttribute()
 : fNext( NULL), fSourceType( kUnknown), fSourceIndex( 0)
{
}


//
// A varying parameter to a pass
//

cgfxVaryingParameter::cgfxVaryingParameter( CGparameter parameter)
 :  fParameter( parameter),
    fVertexAttribute( NULL),
    fVertexStructure( NULL),
    fNext( NULL)
{
    if( parameter)
    {
        fName = cgGetParameterName( parameter);
//      fRegister = cgfxVaryingParameterManager::instance().findRegister( parameter);
    }
}


cgfxVaryingParameter::~cgfxVaryingParameter()
{
    if( fVertexStructure) delete fVertexStructure;
    if( fNext) delete fNext;
}


void cgfxVaryingParameter::setupAttributes( cgfxVertexAttribute*& vertexAttributes, CGprogram program)
{
    // Make sure our parameter name is acceptable is a Maya attribute name
    MString attrName = fName;
    int lastDot = attrName.rindex( '.');
    if( lastDot >= 0)
        attrName = attrName.substring( lastDot + 1, attrName.length() - 1);
    MString semantic = cgGetParameterSemantic( fParameter);
    semantic.toUpperCase();

    // Is this varying parameter packed or atomic?
    CGtype type = cgGetNamedUserType( program, attrName.asChar());
    if( type != CG_UNKNOWN_TYPE)
    {
        // It's packed: explode the inputs into the structure elements
        CGcontext context = cgGetProgramContext( program); 
        CGparameter packing = cgCreateParameter( context, type);
        fVertexStructure = new cgfxVaryingParameterStructure();
        fVertexStructure->fLength = 0;
        fVertexStructure->fSize = 0;
        CGparameter element = cgGetFirstStructParameter( packing);
        while( element)
        {
            MString elementName = cgGetParameterName( element);
            int lastDot = elementName.rindex( '.');
            if( lastDot >= 0)
                elementName = elementName.substring( lastDot + 1, elementName.length() - 1);
            cgfxVertexAttribute* attr = setupAttribute( elementName, semantic, element, vertexAttributes);
            fVertexStructure->fElements[ fVertexStructure->fLength].fVertexAttribute = attr;
            int size = cgGetParameterRows( element) * cgGetParameterColumns( element);
            CGtype type = cgGetParameterBaseType( element);
            if( type == CG_FLOAT) size *= sizeof( GLfloat);
            else if( type == CG_INT) size *= sizeof( GLint);
            fVertexStructure->fElements[ fVertexStructure->fLength].fSize = size;
            fVertexStructure->fLength++;
            fVertexStructure->fSize += size;
            element = cgGetNextParameter( element);
        }
        cgDestroyParameter( packing); 
    }
    else
    {
        // It's atomic - create a single, simple input
        fVertexAttribute = setupAttribute( attrName, semantic, fParameter, vertexAttributes);
    }

    // Now pull apart the semantic string to work out where to bind
    // this value in open GL (as the automagic binding through cgGL
    // didn't work so well when this was written)
    int radix = 1;
    fGLIndex = 0;
    unsigned int length = semantic.length();
    const char* str = semantic.asChar();

    // If sematic is NULL then stop here, bug 327649
    if (length == 0) {
         fGLType = glRegister::kUnknown;
         return;
    }

    for(;;)
    {
        char c = str[ length - 1];
        if( c < '0' || c > '9') break;
        fGLIndex += radix * (c - '0');
        radix *= 10;
        --length;
    }
    if( semantic.length() != length)
        semantic = semantic.substring( 0, length - 1);
    
    // Determine the semantic and setup the gl binding type we should use
    // to set this parameter. If there's a sensible default value, set that
    // while we're here.
    // Note there is no need to set the source type, this gets determined
    // when the vertex attribute sources are analysed
    if( semantic == "POSITION")
    {
        fGLType = glRegister::kPosition;
        fVertexAttribute->fSourceName = "position";
    }
    else if( semantic == "NORMAL")
    {
        fGLType = glRegister::kNormal;
        if( fVertexAttribute) 
            fVertexAttribute->fSourceName = "normal";
    }
    else if( semantic == "TEXCOORD")
    {
        fGLType = glRegister::kTexCoord;
        if( fVertexAttribute) 
        {
            if( attrName.toLowerCase() == "tangent")
                fVertexAttribute->fSourceName = "tangent:map1";
            else if( attrName.toLowerCase() == "binormal")
                fVertexAttribute->fSourceName = "binormal:map1";
            else
                fVertexAttribute->fSourceName = "uv:map1";
        }
    }
    else if( semantic == "TANGENT")
    {
        fGLType = glRegister::kTexCoord;
        fGLIndex += 6; // TANGENT is TEXCOORD6
        if( fVertexAttribute) 
            fVertexAttribute->fSourceName = "tangent:map1";
    }
    else if( semantic == "BINORMAL")
    {
        fGLType = glRegister::kTexCoord;
        fGLIndex += 7; // BINORMAL is TEXCOORD7
        if( fVertexAttribute) 
            fVertexAttribute->fSourceName = "binormal:map1";
    }
    else if( semantic == "COLOR")
    {
        fGLType = fGLIndex == 1 ? glRegister::kSecondaryColor : glRegister::kColor;
    }
    else if( semantic == "ATTR")
    {
        fGLType = glRegister::kVertexAttrib;
    }
    else if( semantic == "PSIZE")
    {
        fGLType = glRegister::kVertexAttrib;
        fGLIndex = 6;
    }
    else
    {
        fGLType = glRegister::kUnknown;
    }
}

cgfxVertexAttribute* cgfxVaryingParameter::setupAttribute( MString name, 
                        const MString& semantic, 
                        CGparameter parameter, 
                        cgfxVertexAttribute*& vertexAttributes)
{
    // Does a varying parameter of this name already exist?
    cgfxVertexAttribute** attribute = &vertexAttributes;
    while( *attribute)
    {
        if( (*attribute)->fName == name)
        {
            return *attribute;
        }
        attribute = &(*attribute)->fNext;
    }

    // Add a new input for this parameter
    cgfxVertexAttribute* attr = new cgfxVertexAttribute();
    *attribute = attr;

    // Setup the varying parameter description
    attr->fName = name;
    attr->fType = cgGetTypeString( cgGetParameterType( parameter));
    attr->fSemantic = semantic;
    return attr;
}


void cgfxVaryingParameter::bind( const MDagPath& shape, cgfxStructureCache** cache,
                    int vertexCount, const float * vertexArray,
                    int normalsPerVertex, int normalCount, const float ** normalArrays,
                    int colorCount, const float ** colorArrays,
                    int texCoordCount, const float ** texCoordArrays)
{
    bool result = false;
    if( fVertexAttribute && fParameter)
    {
        switch( fVertexAttribute->fSourceType)
        {
            case cgfxVertexAttribute::kPosition:
                result = bind( vertexArray, 3);
                break;

            case cgfxVertexAttribute::kNormal:
                if( normalCount > 0 && normalArrays[ 0])
                    result = bind( normalArrays[0], 3);
                break;

            case cgfxVertexAttribute::kUV:
                if( texCoordCount > fVertexAttribute->fSourceIndex && texCoordArrays[ fVertexAttribute->fSourceIndex])
                    result = bind( texCoordArrays[ fVertexAttribute->fSourceIndex], 2);
                break;

            case cgfxVertexAttribute::kTangent:
                if( normalCount >= normalsPerVertex * fVertexAttribute->fSourceIndex + 1 && normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 1])
                    result = bind( normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 1], 3);
                break;

            case cgfxVertexAttribute::kBinormal:
                if( normalCount >= normalsPerVertex * fVertexAttribute->fSourceIndex + 2 && normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 2])
                    result = bind( normalArrays[ normalsPerVertex * fVertexAttribute->fSourceIndex + 2], 3);
                break;

            case cgfxVertexAttribute::kColor:
                if( colorCount > fVertexAttribute->fSourceIndex && colorArrays[ fVertexAttribute->fSourceIndex])
                    result = bind( colorArrays[ fVertexAttribute->fSourceIndex], 4);
                break;

            default:
                break;
        }
    }
    else if( fVertexStructure && fParameter && vertexCount)
    {
        // Build a unique name for the contents of this structure
        MString structureName;
        structureName += fVertexStructure->fSize;
        for( int i = 0; i < fVertexStructure->fLength; i++)
        {
                cgfxVertexAttribute* vertexAttribute = fVertexStructure->fElements[ i].fVertexAttribute;
                if( vertexAttribute) structureName += fVertexStructure->fElements[ i].fVertexAttribute->fSourceName;
                structureName += fVertexStructure->fElements[ i].fSize;
        }

        // See if this data already exists in the cache
        char* data = NULL;
        while( *cache)
        {
            if( !(*cache)->fShape.isValid() || !(*cache)->fShape.isAlive())
            {
                //printf( "Found stale cache data %s in the cache - deleting it\n", structureName.asChar());
                cgfxStructureCache* staleCache = *cache;
                *cache = staleCache->fNext;
                delete staleCache;
            }
            else 
            {
                if( (*cache)->fShape == shape.node() && (*cache)->fName == structureName)
                {
                    //printf( "Found existing data in the cache for %s on %s\n", structureName.asChar(), shape.fullPathName().asChar());
                    data = (*cache)->fData;
                    break;
                }
                cache = &(*cache)->fNext;
            }
        }

        // If we couldn't find it, add it to the cache
        if( !data)
        {
            // Allocate storage for this structure
            //printf( "Added new cache entry for %s on %s\n", structureName.asChar(), shape.fullPathName().asChar());
            cgfxStructureCache* cacheEntry = new cgfxStructureCache( shape, structureName, fVertexStructure->fSize, vertexCount);
            cacheEntry->fNext = *cache;
            *cache = cacheEntry;
            data = cacheEntry->fData;
            char* dest = data;
            for( int i = 0; i < fVertexStructure->fLength; i++)
            {
                cgfxVertexAttribute* vertexAttribute = fVertexStructure->fElements[ i].fVertexAttribute;
                if( vertexAttribute)
                {
                    const char* src = NULL;
                    int size = 0;
                    switch( vertexAttribute->fSourceType)
                    {
                        case cgfxVertexAttribute::kPosition:
                            src = (const char*)vertexArray;
                            size = 3 * sizeof( float);
                            break;

                        case cgfxVertexAttribute::kNormal:
                            if( normalCount > 0 && normalArrays[ 0])
                            {
                                src = (const char*)normalArrays[0];
                                size = 3 * sizeof( float);
                            }
                            break;

                        case cgfxVertexAttribute::kUV:
                            if( texCoordCount > vertexAttribute->fSourceIndex && texCoordArrays[ vertexAttribute->fSourceIndex])
                            {
                                src = (const char*)texCoordArrays[ vertexAttribute->fSourceIndex];
                                size = 2 * sizeof( float);
                            }
                            break;

                        case cgfxVertexAttribute::kTangent:
                            if( normalCount >= normalsPerVertex * vertexAttribute->fSourceIndex + 1 && normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 1])
                            {
                                src = (const char*)normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 1];
                                size = 3 * sizeof( float);
                            }
                            break;

                        case cgfxVertexAttribute::kBinormal:
                            if( normalCount >= normalsPerVertex * vertexAttribute->fSourceIndex + 2 && normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 2])
                            {
                                src = (const char*)normalArrays[ normalsPerVertex * vertexAttribute->fSourceIndex + 2];
                                size = 3 * sizeof( float);
                            }
                            break;

                        case cgfxVertexAttribute::kColor:
                            if( colorCount > vertexAttribute->fSourceIndex && colorArrays[ vertexAttribute->fSourceIndex])
                            {
                                src = (const char*)colorArrays[ vertexAttribute->fSourceIndex];
                                size = 4 * sizeof( float);
                            }
                            break;

                        default:
                            break;
                    }

                    // Do we have a valid input?
                    if( src && size)
                    {
                        // Setup this element
                        int srcSkip = 0;
                        if( size > fVertexStructure->fElements[ i].fSize)
                        {
                            srcSkip = size - fVertexStructure->fElements[ i].fSize;
                            size = fVertexStructure->fElements[ i].fSize;
                        }
                        int dstSkip = fVertexStructure->fSize - size;
                        char* dst = dest;
                        for( int v = 0; v < vertexCount; v++)
                        {
                            for( int b = 0; b < size; b++)
                                *dst++ = *src++;
                            src += srcSkip;
                            dst += dstSkip;
                        }
                    }
                    else
                    {
                        // NULL this element
                        size = fVertexStructure->fElements[ i].fSize;
                        int dstSkip = fVertexStructure->fSize - size;
                        char* dst = dest;
                        for( int v = 0; v < vertexCount; v++)
                        {
                            for( int b = 0; b < size; b++)
                                *dst++ = 0;
                            dst += dstSkip;
                        }
                    }
                }
                dest += fVertexStructure->fElements[ i].fSize;
            }
        }

        result = bind( (const float*)data, fVertexStructure->fSize / sizeof( float));
    }

    // If we were unable to bind a stream of data to this register, set a friendly NULL value
    if( !result)
        null();
}


//
// Bind data to GL
//
bool cgfxVaryingParameter::bind( const float* data, int stride)
{
    bool result = false;
    switch( fGLType)
    {
        case glRegister::kPosition:
            glStateCache::instance().enablePosition();
            glVertexPointer( stride, GL_FLOAT, 0, data);
            result = true;
            break;

        case glRegister::kNormal:
            if( stride == 3)
            {
                glStateCache::instance().enableNormal();
                glNormalPointer( GL_FLOAT, 0, data);
                result = true;
            }
            break;

        case glRegister::kTexCoord:
            if( fGLIndex < glStateCache::sMaxTextureUnits)
            {
                glStateCache::instance().enableAndActivateTexCoord( fGLIndex);
                glTexCoordPointer( stride, GL_FLOAT, 0, data);
                result = true;
            }
            break;

        case glRegister::kColor:
            if( stride > 2)
            {
                glStateCache::instance().enableColor();
                glColorPointer( stride, GL_FLOAT, 0, data);
                result = true;
            }
            break;

        case glRegister::kSecondaryColor:
            if( stride > 2)
            {
                glStateCache::instance().enableSecondaryColor();
                if( glStateCache::glVertexAttribPointer) 
                    glStateCache::glSecondaryColorPointer( stride, GL_FLOAT, 0, (GLvoid*)data);
                result = true;
            }
            break;

        case glRegister::kVertexAttrib:
            glStateCache::instance().enableVertexAttrib( fGLIndex);
            if( glStateCache::glVertexAttribPointer) 
                glStateCache::glVertexAttribPointer( fGLIndex, stride, GL_FLOAT, GL_FALSE, 0, data);
            result = true;
            break;

        default:
            break;
    }
    return result;
}

bool cgfxVaryingParameter::bind(const sourceStreamInfo& source)
{
    // should assert(dataBufferId > 0) here
    // ...
    
    static MGLFunctionTable *gGLFT = 0;
    if ( 0 == gGLFT )
        gGLFT = MHardwareRenderer::theRenderer()->glFunctionTable();
    
    const unsigned int stride = source.fStride;
    const unsigned int offset = source.fOffset;
    const unsigned int dimension  = source.fDimension;
    const unsigned int elementSize  = source.fElementSize;
    const GLuint bufferId = source.fDataBufferId;

    gGLFT->glBindBufferARB(MGL_ARRAY_BUFFER_ARB, bufferId);

    #define GLOBJECT_BUFFER_OFFSET(i) ((char *)NULL + (i)) // For GLObject offsets

    switch( fGLType)
    {
        case glRegister::kPosition:
            glStateCache::instance().enablePosition();
            glVertexPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;

        case glRegister::kNormal:
            glStateCache::instance().enableNormal();
            glNormalPointer(GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;

        case glRegister::kTexCoord:
            if( fGLIndex < glStateCache::sMaxTextureUnits)
            {
                glStateCache::instance().enableAndActivateTexCoord( fGLIndex);
                glTexCoordPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            }
            break;

        case glRegister::kColor:
            glStateCache::instance().enableColor();
            glColorPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;

        case glRegister::kSecondaryColor:
            glStateCache::instance().enableSecondaryColor();
            if( glStateCache::glVertexAttribPointer) 
                glStateCache::glSecondaryColorPointer(dimension, GL_FLOAT, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;

        case glRegister::kVertexAttrib:
            glStateCache::instance().enableVertexAttrib( fGLIndex);
            if( glStateCache::glVertexAttribPointer) 
                glStateCache::glVertexAttribPointer( fGLIndex, dimension, GL_FLOAT, GL_FALSE, stride*elementSize, GLOBJECT_BUFFER_OFFSET(offset));
            break;

        default:
            return false;  //these we don't support yet
    }

    return true;
}



//
// Send null data to GL
//
void cgfxVaryingParameter::null()
{
    switch( fGLType)
    {
        case glRegister::kPosition:
            glVertex4f( 0.0f, 0.0f, 0.0f, 1.0f);
            break;

        case glRegister::kNormal:
            glNormal3f( 0.0f, 0.0f, 1.0f);
            break;

        case glRegister::kTexCoord:
            glStateCache::instance().activeTexture( fGLIndex);
            glStateCache::glMultiTexCoord4fARB( GL_TEXTURE0 + fGLIndex, 0.0f, 0.0f, 0.0f, 0.0f );
            break;

        case glRegister::kColor:
            glColor4f( 1.0f, 1.0f, 1.0f, 1.0f);
            break;

        case glRegister::kSecondaryColor:
            if( glStateCache::glSecondaryColor3f) 
                glStateCache::glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
            break;

        case glRegister::kVertexAttrib:
            if( glStateCache::glVertexAttrib4f) 
                glStateCache::glVertexAttrib4f( fGLIndex, 0.0f, 0.0f, 0.0f, 0.0f);
            break;

        default:
            break;
    }
}



inline void addVaryingParametersRecursive( CGparameter parameter, cgfxVaryingParameter**& nextParameter)
{
    if( cgGetParameterVariability( parameter) == CG_VARYING)
    {
        if( cgGetParameterType( parameter) == CG_STRUCT)
        {
            CGparameter input = cgGetFirstStructParameter( parameter);
            while( input)
            {
                addVaryingParametersRecursive( input, nextParameter);
                input = cgGetNextParameter( input);
            }
        }
        else if( cgIsParameterReferenced( parameter))
        {
            *nextParameter = new cgfxVaryingParameter( parameter);
            nextParameter = &(*nextParameter)->fNext;
        }
    }
}


//
// A pass in a technique
//

cgfxPass::cgfxPass( CGpass pass, cgfxEffect* effect)
 :  fPass( pass),
    fEffect( effect),
    fProgram( NULL),
    fParameters( NULL),
    fPassState( NULL),
    fNext( NULL)
{
    if( pass)
    {
        fName = cgGetPassName( pass);
        CGstateassignment stateAssignment = cgGetFirstStateAssignment( pass);
        cgfxVaryingParameter** nextParameter = &fParameters;
        while( stateAssignment )
        {
            CGstate state = cgGetStateAssignmentState( stateAssignment);
            if( cgGetStateType( state) == CG_PROGRAM_TYPE && 
                    ( stricmp( cgGetStateName( state), "vertexProgram") == 0 ||
                      stricmp( cgGetStateName( state), "vertexShader") == 0))
            {
                fProgram = cgGetProgramStateAssignmentValue( stateAssignment);
                if( fProgram)
                {
                    CGparameter parameter = cgGetFirstParameter( fProgram, CG_PROGRAM);
                    while( parameter)
                    {
                        addVaryingParametersRecursive( parameter, nextParameter);
                        parameter = cgGetNextParameter( parameter);
                    }
                }
            }
            stateAssignment = cgGetNextStateAssignment( stateAssignment);
        }
    }
}


cgfxPass::~cgfxPass()
{
    delete fNext;
    delete fPassState;
    delete fParameters;
}


void cgfxPass::setupAttributes( cgfxVertexAttribute*& vertexAttributes)
{
    cgfxVaryingParameter* parameter = fParameters;
    while( parameter)
    {
        parameter->setupAttributes( vertexAttributes, fProgram);
        parameter = parameter->fNext;
    }
}


void cgfxPass::bind( const MDagPath& shape, int vertexCount, const float * vertexArray,
                    int normalsPerVertex, int normalCount, const float ** normalArrays,
                    int colorCount, const float ** colorArrays,
                    int texCoordCount, const float ** texCoordArrays)
{
    cgfxVaryingParameter* parameter = fParameters;
    while( parameter)
    {
        parameter->bind( shape, &fEffect->fCache, 
                        vertexCount, vertexArray, 
                         normalsPerVertex, normalCount, normalArrays, 
                         colorCount, colorArrays, 
                         texCoordCount, texCoordArrays);
        parameter = parameter->fNext;
    }
}

void cgfxPass::bind(const sourceStreamInfo dataSources[], const int sourceCount)
{
    TRACE_API_CALLS("cgfxPass::bind");

    cgfxVaryingParameter* parameter = fParameters;
    while( parameter)
    {
        // Here we only deal with fVertexAttribute.  How to do fVertexStructure?
        if (parameter->fVertexAttribute) {
            //find the corresponding data buffer
            int index = 0;
            for(index = 0; index < sourceCount; ++index)
            {
                // printf(
                // "    Compare param %s (0x%p) [%s] name (type=%d) with data source name [%s] (type=%d)\n", 
                //         parameter->fName.asChar(),
                //         parameter,
                //         parameter->fVertexAttribute->fSourceName.asChar(), 
                //         parameter->fVertexAttribute->fSourceType,
                //         dataSources[index].fSourceName.asChar(),
                //         dataSources[index].fSourceType);

                if (dataSources[index].fSourceName == parameter->fVertexAttribute->fSourceName )
                {
                    // we find the correct vertex stream
                    break;
                }
            }   

            if(index < sourceCount)
            {
                // printf("    Binding source name [%s]\n", dataSources[index].fSourceName.asChar());
                if(!parameter->bind(dataSources[index])) {

                    // This is a true error. Binding should normally
                    // always succeed here as the geometry
                    // requirements are verified in
                    // cgfxShaderOverride::initialize().
                    parameter->null();

                    MString s = "cgfxShader : Couldn't bind source \"";
                    s += dataSources[index].fSourceName;
                    s += "\" for vertex attribute \"";
                    s += parameter->fVertexAttribute->fSourceName;;
                    s +="\".";
                    MGlobal::displayError(s);
                }
            }
            else
            {
                // printf("    Can't find the source for source name [%s] for parameter [%s]\n", 
                //        parameter->fVertexAttribute->fSourceName.asChar(),
                //        parameter->fVertexAttribute->fName.asChar());

                // There is no matching source for this parameter. We
                // therefore bind null data for this parameter. Note
                // that this fact should have already been reported
                // to the user in cgfxShaderOverride::initialize(). We
                // don't report it to the user here because it would
                // get repetitively reported for each redraw.
                parameter->null();
            }
        }
        
        parameter = parameter->fNext;
    }

    // printf("    Successfully bound sources\n");
}

//
// A technique in an effect
//

cgfxTechnique::cgfxTechnique( CGtechnique technique, cgfxEffect* effect)
 :  fTechnique( technique),
    fEffect( effect),
    fPasses( NULL),
    fNext( NULL)
{
    if( technique)
    {
        fName = cgGetTechniqueName( technique);
        CGpass pass = cgGetFirstPass( technique);
        cgfxPass** nextPass = &fPasses;
        while( pass)
        {
            // If there's no state assigned in a pass, it's either a no-op, or
            // it failed to compile (i.e. no vertex program). Either way, skip it
            if( cgGetFirstStateAssignment( pass))
            {
                *nextPass = new cgfxPass( pass, fEffect);
                nextPass = &(*nextPass)->fNext;
            }
            pass = cgGetNextPass( pass);
        }
    }
    fMultiPass = fPasses && fPasses->fNext;
}


cgfxTechnique::~cgfxTechnique()
{
    if( fNext) delete fNext;
    if( fPasses) delete fPasses;
}


void cgfxTechnique::setupAttributes( cgfxVertexAttribute*& vertexAttributes)
{
    cgfxPass* pass = fPasses;
    while( pass)
    {
        pass->setupAttributes( vertexAttributes);
        pass = pass->fNext;
    }
}



//
// An effect
//

cgfxEffect::cgfxEffect()
:   fEffect( NULL),
    fTechniques( NULL),
    fTechnique( NULL),
    fCache( NULL)
{
}


cgfxEffect::~cgfxEffect()
{
    setEffect( NULL);
}


void cgfxEffect::setEffect( CGeffect effect)
{
    if( effect != fEffect)
    {
//      if( fEffect) delete fEffect;
        if( fTechniques) delete fTechniques;
        fTechniques = NULL;
        fTechnique = NULL;
        flushCache();
        fEffect = effect;
        if( fEffect)
        {
            CGtechnique technique = cgGetFirstTechnique( effect);
            cgfxTechnique** nextTechnique = &fTechniques;
            while( technique)
            {
                *nextTechnique = new cgfxTechnique( technique, this);
                nextTechnique = &(*nextTechnique)->fNext;
                technique = cgGetNextTechnique( technique);
            }
        }
    }
}


void cgfxEffect::setupAttributes( cgfxShaderNode* shader)
{
    cgfxVertexAttribute* vertexAttributes = NULL;
    cgfxTechnique* technique = fTechniques;
    MString currentTechnique = shader->getTechnique();
    while( technique)
    {
        if( technique->fName == currentTechnique)
        {
            fTechnique = technique;
            technique->setupAttributes( vertexAttributes);
            break;
        }
        technique = technique->fNext;
    }
    shader->setVertexAttributes( vertexAttributes);

    // We've changed technique, so flush any cached data we had as the
    // structure definitions may have changed
    flushCache();
}


void cgfxEffect::flushCache()
{
    //printf( "Flushing all cached data\n");
    if( fCache) 
    {
        delete fCache;
        fCache = NULL;
    }
}


void cgfxEffect::flushCache( const MDagPath& shape)
{
    //printf( "Flushing cached data for shape %s\n", shape.fullPathName().asChar());
    cgfxStructureCache** cacheEntry = &fCache;
    while( *cacheEntry)
    {
        if( !(*cacheEntry)->fShape.isValid() || !(*cacheEntry)->fShape.isAlive() || (*cacheEntry)->fShape == shape.node())
        {
            //printf( "Found stale cache data %s in the cache - deleting it\n", (*cacheEntry)->fName.asChar());
            cgfxStructureCache* staleEntry = (*cacheEntry);
            *cacheEntry = staleEntry->fNext;
            staleEntry->fNext = NULL;
            delete staleEntry;
        }
        else
        {
            cacheEntry = &(*cacheEntry)->fNext;
        }
    }
}


cgfxStructureCache::cgfxStructureCache( const MDagPath& shape, const MString& name, int stride, int count)
: fShape( shape.node()), fName( name), fElementSize( stride), fVertexCount( count), fData( new char[ stride * count])
{
}


cgfxStructureCache::~cgfxStructureCache()
{
    if( fData) delete[] fData;
}