//- // ========================================================================== // 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. // // DESCRIPTION: NV20-specific (Geforce3) sample shader. // This shader produces reflection and refraction effects. // // This shader builds on the foundation demonstrated in the hwUnlitShader. // // /////////////////////////////////////////////////////////////////// #ifdef WIN32 #pragma warning( disable : 4786 ) // Disable stupid STL warnings. #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Include NVIDIA's helper libraries. These libraries have // copyright info in them so we cannot release them but we // can use them to verify that the API works correctly. // #define GLH_EXT_SINGLE_FILE #include "glh_extensions.h" #undef GL_NV_vertex_array_range #include "glh_genext.h" #include "glh_obs.h" using namespace glh; #include "hwRefractReflectShader_NV20.h" #include "ShadingConnection.h" MTypeId hwRefractReflectShader_NV20::id( 0x00105445 ); void hwRefractReflectShader_NV20::postConstructor( ) { setMPSafe(false); } // Static attribute instances. // MObject hwRefractReflectShader_NV20::color; MObject hwRefractReflectShader_NV20::colorR; MObject hwRefractReflectShader_NV20::colorG; MObject hwRefractReflectShader_NV20::colorB; MObject hwRefractReflectShader_NV20::refractionIndex; MObject hwRefractReflectShader_NV20::reflectivity; // The Vertex Program for the reflection&refraction shading effect. // // CONSTANTS: // 0- 3 4x4 ModelView-Projection composite matrix // 4- 7 4x4 ModelView inverseTranspose matrix // 8- 11 4x4 ModelView matrix // 12-15 4x4 Texture matrix // // 58 refraction index [rIdx, rIdx*rIdx, 0.0, 0.0] // 59 camera position in eye space [0.0, 0.0, 0.0, 1.0] // (camera could be offseted if necessary, should work but untested) // // 64 misc constants [0.0, 1.0, 2.0, 3.0] // VERTEX REGISTERS (mapped so that standard gl calls work): // 0 - coord // 2 - normal // // RESULTS: // texcoord0 (Refraction coords in eye-space) // texcoord1 (Reflection coords in eye-space) // char vertexProgramString[] = "!!VP1.0 # Refraction and Reflection\n" // Multiply the vertex coords by the GL_MODELVIEW_PROJECTION // composite matrix, to get clip space coordinates. // "DP4 o[HPOS].x, c[0], v[OPOS];" "DP4 o[HPOS].y, c[1], v[OPOS];" "DP4 o[HPOS].z, c[2], v[OPOS];" "DP4 o[HPOS].w, c[3], v[OPOS];" // ===================================================== // The rest of the computations are done in the eyeSpace // ===================================================== // Transform, vertex position to eye space, with the MODELVIEW matrix // "DP4 R9.x, c[8], v[OPOS];" "DP4 R9.y, c[9], v[OPOS];" "DP4 R9.z, c[10], v[OPOS];" "DP4 R9.w, c[11], v[OPOS];" // R9 = eye space Position of this vertex // Using the inverseTranspose of the MODELVIEW matrix, // transform the vertex normal to eye space and normalize it // "DP3 R0.x, c[4], v[NRML];" "DP3 R0.y, c[5], v[NRML];" "DP3 R0.z, c[6], v[NRML];" "DP3 R11.w, R0, R0;" "RSQ R11.w, R11.w;" "MUL R11, R0, R11.w;" // R11 = normalized normal vector in the eyeSpace // Compute the 'vertex->eye' vector and normalize it // "ADD R0, -R9, c[59];" // c[59] = eye position in eye space (0,0,0,1) "DP3 R8.w, R0, R0;" "RSQ R8.w, R8.w;" "MUL R8, R0, R8.w;" // R8 = the eye/incident vector (I) // Calculate REFRACTION: Renderman style // float eta; // the refraction index value // // float IdotN = I.N; // float k = 1 - eta*eta*(1 - IdotN*IdotN); // return k < 0 ? (0,0,0) : eta*I - (eta*IdotN + sqrt(k))*N; // "DP3 R0.x, R11, -R8;" // R0 = N.I // "MAD R1.x, -R0.x, R0.x, c[64].y;" // R1.x = (1 - IdotN * IdotN) == SQR( sin(Ti) ) "MUL R1.x, R1.x, c[58].y;" // R1.x = R1.x * eta*eta "ADD R1.x, c[64].y, -R1.x;" // R1.x = (1 - (R1.x * eta * eta) ) == 1 - SQR( eta * sin(Ti) ) // "RSQ R2.x, R1.x;" // R2.x = 1 / SQRT(R1.x) "RCP R2.x, R2.x;" // R2.x = cos(Tr) = SQRT(R1.x) <=== OK "MAD R2.x, c[58].x, R0.x, R2.x;" // R2.x = eta*(IdotN) + cos(Tr) "MUL R2, R11, R2.x;" // R2 = N * R2.x "MAD R2, c[58].x, -R8, R2;" // R2 is the refracted ray direction // // Transform refracted ray by cubemap transform (texture matrix) // "DP3 o[TEX0].x, c[12], R2;" "DP3 o[TEX0].y, c[13], R2;" "DP3 o[TEX0].z, c[14], R2;" // Calculate REFLECTION in cubeMap space // "MUL R0, R11, c[64].z;" // R0 = 2*N "DP3 R3.w, R11, R8;" // R3.w = N.dot.I "MAD R3, R3.w, R0, -R8;" // R3 = 2*N*(N.dot.I) - I // // Transform reflected ray by cubemap transform (texture matrix) // "DP3 o[TEX1].x, c[12], R3;" "DP3 o[TEX1].y, c[13], R3;" "DP3 o[TEX1].z, c[14], R3;" "END"; void initVertexProgram(const char vertexProgramCode[], GLuint* pVertexProgramId) { // Allocate and initialize the vertex program. glGenProgramsNV(1, pVertexProgramId); GLenum error = glGetError(); assert(error == GL_NO_ERROR); // Load the program. unsigned int length = strlen(vertexProgramCode); glLoadProgramNV(GL_VERTEX_PROGRAM_NV, *pVertexProgramId, length, (const GLubyte *) vertexProgramCode); error = glGetError(); // If an error occured, find the location in the vertex program // code and assert. if (error != GL_NO_ERROR) { // If an error occured, it's most likely due to a syntax or // logic error in the vertex program. The error position // below will contain the index in the vertex program // string that is faulty. See the NV_vertex_program // extension specification for more details. if (error == GL_INVALID_OPERATION) { int error_position = -2; glGetIntegerv(GL_PROGRAM_ERROR_POSITION_NV, &error_position); // Most likely a bug in the vertex program code... assert(0); } } } // Load the vertexProgram and fill in the necessary constants used in the vertex program. // void hwRefractReflectShader_NV20::loadVertexProgramGL( M3dView& view ) { view.beginGL(); { // Don't load/initialize the vertex program more than once. // if (vertex_program_id == 0) initVertexProgram(vertexProgramString, &vertex_program_id); // Set up the constant values. // // CONSTANTS: // 0- 3 4x4 ModelView-Projection composite matrix // 4- 7 4x4 ModelView inverseTranspose matrix // 8- 11 4x4 ModelView matrix // 12-15 4x4 Texture matrix // glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV); glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4, GL_MODELVIEW, GL_INVERSE_TRANSPOSE_NV); glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 8, GL_MODELVIEW, GL_IDENTITY_NV); glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 12, GL_TEXTURE, GL_IDENTITY_NV); float rIdx = fRefractionIndex; glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 58, rIdx, rIdx*rIdx, 0.0, 0.0); // refraction index glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 59, 0.0, 0.0, 0.0, 1.0); // eye position glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 64, 0.0, 1.0, 2.0, 3.0); // misc constants } view.endGL(); } // Load the file textures for the cube maps. // MStatus hwRefractReflectShader_NV20::loadTextures(const MDrawRequest& request, M3dView& view) { // Get the cube map file names // MStringArray decalNames; MString decalName; // Find the cubemap textures by tracing through the connection from the color atttribute // ShadingConnection colorConnection(thisMObject(), request.multiPath().partialPathName(), "color"); // If the color attribute is ultimately connected to a environment, // find its filenames, otherwise use the default color texture. // bool gotAllEnvironmentMaps = TRUE; if (colorConnection.type() == ShadingConnection::TEXTURE && colorConnection.texture().hasFn(MFn::kEnvCube)) { // Get the filenames of the texture. MFnDependencyNode textureNode(colorConnection.texture()); MString attributeName; MString envNames[6] = { "top", "bottom", "left", "right", "front", "back" }; // Scan for connected file textures to the environment map node // for (int i=0; i<6; i++) { ShadingConnection conn(colorConnection.texture(), request.multiPath().partialPathName(), envNames[i]); if (conn.type() == ShadingConnection::TEXTURE && conn.texture().hasFn(MFn::kFileTexture)) { MFnDependencyNode envNode(conn.texture()); MPlug filenamePlug( conn.texture(), envNode.attribute(MString("fileTextureName")) ); filenamePlug.getValue(decalName); if (decalName.length() == 0) decalName = "internalDefaultTexture"; // Append next environment map name decalNames.append( decalName ); } // If any of the environment maps are not mapped put in a fake texture else { decalName = "internalDefaultTexture"; decalNames.append( decalName ); } } } else { // Put in a fake texture for each side decalName = "internalDefaultTexture"; for (int i=0; i<6; i++) { decalNames.append( decalName ); } } // Reload cube maps if the name of the textures // for any of the cube maps changes // bool reload = FALSE; for (int i=0; i<6; i++) { if (currentTextureNames[i] != decalNames[i]) { reload = TRUE; break; } } view.beginGL(); { if ( reload ) { MString ypTexName(decalNames[0]); // y+ == top MString ynTexName(decalNames[1]); // y- == bottom MString xpTexName(decalNames[2]); // x+ == left MString xnTexName(decalNames[3]); // x- == right MString zpTexName(decalNames[4]); // z+ == front MString znTexName(decalNames[5]); // z- == back MStatus stat; if (! (stat = theImage_XP.readFromFile(xpTexName)) ) return MS::kFailure; if (! (stat = theImage_XN.readFromFile(xnTexName)) ) return MS::kFailure; if (! (stat = theImage_YP.readFromFile(ypTexName)) ) return MS::kFailure; if (! (stat = theImage_YN.readFromFile(ynTexName)) ) return MS::kFailure; if (! (stat = theImage_ZP.readFromFile(zpTexName)) ) return MS::kFailure; if (! (stat = theImage_ZN.readFromFile(znTexName)) ) return MS::kFailure; // Only create texture names the first time if (fTextureName == -1) glGenTextures(1, &fTextureName); glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName ); glEnable( GL_TEXTURE_CUBE_MAP_ARB ); // The cubeMap textures have to have the same size // unsigned int width, height; stat = theImage_XP.getSize( width, height ); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XP.pixels() ); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XN.pixels() ); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YP.pixels() ); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YN.pixels() ); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZP.pixels() ); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZN.pixels() ); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); for (i=0; i<6; i++) currentTextureNames[i] = decalNames[i]; } // stage 0 -- cubeMap texture for the refraction // glActiveTextureARB( GL_TEXTURE0_ARB ); glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName ); glEnable( GL_TEXTURE_CUBE_MAP_ARB ); // stage 1 -- cubeMap texture for the reflection // glActiveTextureARB( GL_TEXTURE1_ARB ); glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName ); glEnable( GL_TEXTURE_CUBE_MAP_ARB ); } view.endGL(); return MS::kSuccess; } // Initialize the register combiners setting // void hwRefractReflectShader_NV20::initCombiners(const MDrawRequest& request, M3dView& view) { view.beginGL(); { // Use only the 1st stage of the register combiner stages // glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1); { float refractivity[4], reflectivity[4]; refractivity[0] = refractivity[1] = refractivity[2] = refractivity[3] = 1.0f - fReflectivity; reflectivity[0] = reflectivity[1] = reflectivity[2] = reflectivity[3] = fReflectivity; glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, refractivity); glCombinerParameterfvNV(GL_CONSTANT_COLOR1_NV, reflectivity); } // combiner 0 // a*b+c*d // a is from the refractive color // c is from the refrlective texture glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB); // output: // (stage, portion, abOutput, cdOutput, sumOutput, scale, bias, abDotProduct, cdDotProduct, muxSum) glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE); // final combiner // output: Frgb = A*B + (1-A)*C + D // (variable, input, mapping, componentUsage); // Just pass through the D variable // glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB); } view.endGL(); } // Load the textures, update the necessary variable values, initialize register combiners, // save and load the matrices with the proper values // MStatus hwRefractReflectShader_NV20::preDraw(const MDrawRequest& request, M3dView& view) { MStatus stat = loadTextures( request, view); if( MS::kSuccess != stat ) return stat; // get the reflectivity value // MPlug tPlug(thisMObject(), reflectivity); if( tPlug.getValue( fReflectivity ) ) { if( fReflectivity < 0.01f ) fReflectivity = 0.01f; if( fReflectivity > 1.0f ) fReflectivity = 1.0f; } else fReflectivity = 0.5f; // get the refraction index value // MPlug rPlug(thisMObject(), refractionIndex); if( rPlug.getValue( fRefractionIndex ) ) { if ( fRefractionIndex < 1.0f ) fRefractionIndex = 1.0f; if ( fRefractionIndex > 2.0f ) fRefractionIndex = 2.0f; } else fRefractionIndex = 1.0f; initCombiners( request, view ); // Compute the camera rotation angle and axis // MDagPath cameraPath; MStatus status = view.getCamera( cameraPath ); MMatrix mmatrix = cameraPath.inclusiveMatrix( &status ); MTransformationMatrix tmatrix( mmatrix ); MQuaternion camRotation = tmatrix.rotation(); MVector camAxis; double camTheta; camRotation.getAxisAngle( camAxis, camTheta ); // Convert to degrees from radians camTheta *= 57.295779513082320876798154814105; // == (180 / M_PI) view.beginGL(); glMatrixMode( GL_TEXTURE ); glPushMatrix(); glLoadIdentity(); glScalef(1.0, -1.0, 1.0); glRotated( camTheta, camAxis[0], camAxis[1], camAxis[2]); glMatrixMode( GL_MODELVIEW ); view.endGL(); return stat; } /* virtual */ MStatus hwRefractReflectShader_NV20::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; // Save the current states of the openGL attributes // view.beginGL(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); view.endGL(); MStatus preDrawStatus = preDraw( request, view ); if( MS::kSuccess == preDrawStatus ) { loadVertexProgramGL( view ); view.beginGL(); { glEnable(GL_REGISTER_COMBINERS_NV); // // Load, bind and enable the vertex program // glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id); glEnable(GL_VERTEX_PROGRAM_NV); { // VERTEX REGISTERS (Attributes): // 0 - coord // 2 - normal // glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray ); glVertexAttribPointerNV( 2, 3, GL_FLOAT, 0, normalArrays[0] ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV ); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV ); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV ); } glDisable(GL_VERTEX_PROGRAM_NV); // glDisable(GL_REGISTER_COMBINERS_NV); } view.endGL(); postDraw( request, view ); } // Restore the openGL attributes // view.beginGL(); glPopClientAttrib(); glPopAttrib(); view.endGL(); return preDrawStatus; } // Retore the openGL matrices and the openGL texture objects states // MStatus hwRefractReflectShader_NV20::postDraw( const MDrawRequest& request, M3dView& view ) { view.beginGL(); { glMatrixMode( GL_TEXTURE ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); glActiveTextureARB( GL_TEXTURE1_ARB ); glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 ); glDisable(GL_TEXTURE_CUBE_MAP_ARB); glActiveTextureARB( GL_TEXTURE0_ARB ); glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 ); glDisable(GL_TEXTURE_CUBE_MAP_ARB); } view.endGL(); return MS::kSuccess; } /* virtual */ int hwRefractReflectShader_NV20::normalsPerVertex() { return 1; } /* virtual */ int hwRefractReflectShader_NV20::texCoordsPerVertex() { return 1; } ///////// // Initialize the necessary OpenGL extensions // void hwRefractReflectShader_NV20::init_ext(const char * ext) { if(!glh_init_extension(ext)) { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); } } // The constructor // hwRefractReflectShader_NV20::hwRefractReflectShader_NV20() { // Get an reference to the singleton texture cache. m_pTextureCache = MTextureCache::instance(); init_ext("GL_ARB_multitexture"); init_ext("GL_NV_register_combiners"); init_ext("GL_NV_vertex_program"); // Initialize the cubeMap texture names // fTextureName = -1; currentTextureNames[0] = ""; currentTextureNames[1] = ""; currentTextureNames[2] = ""; currentTextureNames[3] = ""; currentTextureNames[4] = ""; currentTextureNames[5] = ""; // Initialize callbacks. fBeforeNewCB = 0; fBeforeOpenCB = 0; fBeforeRemoveReferenceCB = 0; fMayaExitingCB = 0; attachSceneCallbacks(); vertex_program_id = 0; // handle for the Vertex Program } hwRefractReflectShader_NV20::~hwRefractReflectShader_NV20() { detachSceneCallbacks(); } void releaseVertexProgram(GLuint* pVertexProgramId) { // If the vertex program id is set... if (*pVertexProgramId > 0) { // Unbind any vertex program... glBindProgramNV(GL_VERTEX_PROGRAM_NV, 0); glDeleteProgramsNV(1, pVertexProgramId); // For sanity, set the id to 0. *pVertexProgramId = 0; } } void hwRefractReflectShader_NV20::releaseEverything() { if (fTextureName != -1) glDeleteTextures(1, &fTextureName); releaseVertexProgram(&vertex_program_id); // Release the texture cache through refcounting. m_pTextureCache->release(); if(!MTextureCache::getReferenceCount()) { m_pTextureCache = 0; } } void hwRefractReflectShader_NV20::attachSceneCallbacks() { fBeforeNewCB = MSceneMessage::addCallback(MSceneMessage::kBeforeNew, releaseCallback, this); fBeforeOpenCB = MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, releaseCallback, this); fBeforeRemoveReferenceCB = MSceneMessage::addCallback(MSceneMessage::kBeforeRemoveReference, releaseCallback, this); fMayaExitingCB = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, releaseCallback, this); } /*static*/ void hwRefractReflectShader_NV20::releaseCallback(void* clientData) { hwRefractReflectShader_NV20 *pThis = (hwRefractReflectShader_NV20*) clientData; pThis->releaseEverything(); } void hwRefractReflectShader_NV20::detachSceneCallbacks() { if (fBeforeNewCB) MMessage::removeCallback(fBeforeNewCB); if (fBeforeOpenCB) MMessage::removeCallback(fBeforeOpenCB); if (fBeforeRemoveReferenceCB) MMessage::removeCallback(fBeforeRemoveReferenceCB); if (fMayaExitingCB) MMessage::removeCallback(fMayaExitingCB); fBeforeNewCB = 0; fBeforeOpenCB = 0; fBeforeRemoveReferenceCB = 0; fMayaExitingCB = 0; } MStatus initializePlugin( MObject obj ) { MStatus status; const MString UserClassify( "shader/surface/utility" ); MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.0", "Any"); status = plugin.registerNode( "hwRefractReflectShader_NV20", hwRefractReflectShader_NV20::id, hwRefractReflectShader_NV20::creator, hwRefractReflectShader_NV20::initialize, MPxNode::kHwShaderNode, &UserClassify ); if (!status) { status.perror("registerNode"); return status; } return MS::kSuccess; } MStatus uninitializePlugin( MObject obj ) { MStatus status; MFnPlugin plugin( obj ); status = plugin.deregisterNode( hwRefractReflectShader_NV20::id ); if (!status) { status.perror("deregisterNode"); return status; } return MS::kSuccess; } void * hwRefractReflectShader_NV20::creator() { return new hwRefractReflectShader_NV20(); } // Initialize the plug-in. Called once when the plug-in is loaded. // This mostly involve creating attributes. MStatus hwRefractReflectShader_NV20::initialize() { MFnNumericAttribute nAttr; MStatus status; MFnTypedAttribute sAttr; // For string attributes // Create input attributes colorR = nAttr.create( "colorR", "cr",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); colorG = nAttr.create( "colorG", "cg",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.5f); colorB = nAttr.create( "colorB", "cb",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.5f); color = nAttr.create( "color", "c", colorR, colorG, colorB); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f, 0.5f, 0.5f); nAttr.setUsedAsColor(true); refractionIndex = nAttr.create( "refractionIndex", "ri", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setMin(1.0f); nAttr.setMax(2.0f); nAttr.setDefault(1.1f); reflectivity = nAttr.create( "reflectivity", "rfl", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setMin(0.0f); nAttr.setMax(1.0f); nAttr.setDefault(0.5f); // Add the attributes here addAttribute(color); addAttribute(refractionIndex); addAttribute(reflectivity); attributeAffects (colorR, outColor); attributeAffects (colorG, outColor); attributeAffects (colorB, outColor); attributeAffects (color, outColor); attributeAffects (refractionIndex, outColor); attributeAffects (reflectivity, outColor); return MS::kSuccess; } // This function gets called by Maya to evaluate the texture. // See "Writing a shading node plug-in" in the documentation // for more information. // // CAVEAT: This part of the HW shader plug-in is meant to allow // seamless transition from HW to SW rendering. // Unfortunately, as of 4.0.1 it's somewhat flaky. // Meanwhile, it is recommended to build two shading networks // in parallel (one for SW, one for HW) and use MEL scripts // to switch from one to the other. // MStatus hwRefractReflectShader_NV20::compute( const MPlug& plug, MDataBlock& block ) { // Get color and lightModel from the input block. // Get UV coordinates from the input block. bool k = false; k |= (plug==outColor); k |= (plug==outColorR); k |= (plug==outColorG); k |= (plug==outColorB); if( !k ) return MS::kUnknownParameter; MFloatVector resultColor(0.0,0.0,0.0); // set ouput color attribute MDataHandle outColorHandle = block.outputValue( outColor ); MFloatVector& outColor = outColorHandle.asFloatVector(); outColor = resultColor; outColorHandle.setClean(); return MS::kSuccess; }