//- // ========================================================================== // 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. // // ========================================================================== //+ // // cgfxAttrDef holds the "definition" of an attribute on a cgfxShader // node. This definition includes all the Maya attributes plus the // CGeffect parameter index. // // #include "cgfxEffectDef.h" #include "cgfxShaderNode.h" #ifdef _WIN32 #else # include # include # # define stricmp strcasecmp # define strnicmp strncasecmp #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; int length = semantic.length(); const char* str = semantic.asChar(); 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; /// TODO add secondaryColor, vertexWeight, vertexAttrib, fog default: break; } return result; } // // 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; /// TODO add secondaryColor, vertexWeight, vertexAttrib, fog default: break; } } // // A pass in a technique // cgfxPass::cgfxPass( CGpass pass, cgfxEffect* effect) : fPass( pass), fEffect( effect), fProgram( NULL), fParameters( 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) { if( cgGetParameterVariability( parameter) == CG_VARYING) { if( cgGetParameterType( parameter) == CG_STRUCT) { CGparameter input = cgGetFirstStructParameter( parameter); while( input) { if( cgGetParameterVariability( input) == CG_VARYING && cgIsParameterReferenced( input)) { *nextParameter = new cgfxVaryingParameter( input); nextParameter = &(*nextParameter)->fNext; } input = cgGetNextParameter( input); } } else if( cgIsParameterReferenced( parameter)) { *nextParameter = new cgfxVaryingParameter( parameter); nextParameter = &(*nextParameter)->fNext; } } parameter = cgGetNextParameter( parameter); } } } stateAssignment = cgGetNextStateAssignment( stateAssignment); } } } cgfxPass::~cgfxPass() { if( fNext) delete fNext; if( fParameters) 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; } } // // 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; }