//- // ========================================================================== // 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 can simultaneously display a // bumpy, reflective surface. The bump // is controlled through a user-specified // 2D texture, while the reflection map // is a cube map. // // This shader builds on the foundation demonstrated in // hwUnlitShader. // // Additionally, this sample demonstrates how to: // - Use vendor-specific extensions, namely vertex programs, // texture shaders and register combiners, to achieve // effects that are impossible in standard OpenGL. // - Convert height field bump format (used by Maya) into // a normal map format, for real-time rendering. // // Many parameters are easily customizable: // - The MNormalMapConverter::convertToNormalMap_InPlace() // bumpScale parameter is currently constant. You can change // it to a different value to increase or decrease the // bumpiness. // /////////////////////////////////////////////////////////////////// #ifdef WIN32 #pragma warning( disable : 4786 ) // Disable STL warnings. #endif #include #include #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. // #include #include #include #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 "hwReflectBumpShader_NV20.h" #include "ShadingConnection.h" MTypeId hwReflectBumpShader_NV20::id( 0x00105442 ); void hwReflectBumpShader_NV20::postConstructor( ) { setMPSafe(false); } // // DESCRIPTION: /////////////////////////////////////////////////////// MObject hwReflectBumpShader_NV20::color; MObject hwReflectBumpShader_NV20::colorR; MObject hwReflectBumpShader_NV20::colorG; MObject hwReflectBumpShader_NV20::colorB; MObject hwReflectBumpShader_NV20::bump; MObject hwReflectBumpShader_NV20::bumpR; MObject hwReflectBumpShader_NV20::bumpG; MObject hwReflectBumpShader_NV20::bumpB; MObject hwReflectBumpShader_NV20::uCoord; MObject hwReflectBumpShader_NV20::vCoord; MObject hwReflectBumpShader_NV20::uvCoord; MObject hwReflectBumpShader_NV20::uBias; MObject hwReflectBumpShader_NV20::vBias; MObject hwReflectBumpShader_NV20::uvFilterSize; MObject hwReflectBumpShader_NV20::uvFilterSizeX; MObject hwReflectBumpShader_NV20::uvFilterSizeY; char gszErrMsg[100]; // Global error message text void hwReflectBumpShader_NV20::printGlError( const char *call ) { GLenum error; while( (error = glGetError()) != GL_NO_ERROR ) { cerr << call << ":" << error << " is " << (const char *)gluErrorString( error ) << "\n"; } } // Verify that the configuration of the texture shaders are consistent // void hwReflectBumpShader_NV20::verify_shader_config(M3dView& view) { int consistent; view.beginGL(); glActiveTextureARB( GL_TEXTURE0_ARB ); glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent); if(consistent == GL_FALSE) cerr << "Shader stage 0 is inconsistent!" << endl; glActiveTextureARB( GL_TEXTURE1_ARB ); glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent); if(consistent == GL_FALSE) cerr << "Shader stage 1 is inconsistent!" << endl; glActiveTextureARB( GL_TEXTURE2_ARB ); glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent); if(consistent == GL_FALSE) cerr << "Shader stage 2 is inconsistent!" << endl; glActiveTextureARB( GL_TEXTURE3_ARB ); glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent); if(consistent == GL_FALSE) cerr << "Shader stage 3 is inconsistent!" << endl; glActiveTextureARB( GL_TEXTURE0_ARB ); view.endGL(); } // The very simple VertexProgram for the Reflective Bump effect. This one is faster // (it doesn't require the tangent space calculation) but is world-aligned. // Therefore it could be useful for some effects (ex: ground or wall), but for // a character it would be unnacceptable. // // CONSTANTS: // 0- 3 4x4 ModelView-Projection composite matrix // 4- 7 4x4 ModelView matrix // 24-27 4x4 view transpose // // VERTEX REGISTERS: // 0 - coord // 1 - normal // 2 - texcoord0 // // REGISTERS: // 4 = skinned (eye space) coord // 5 = skinned (eye space) tangent // 6 = skinned (eye space) binormal // 7 = skinned (eye space) normal // char superEasyVertexProgramString[] = "!!VP1.0\n" // final projection transformation // transform the skinned coords by the projection matrix "DP4 o[HPOS].x, c[0], v[0];" "DP4 o[HPOS].y, c[1], v[0];" "DP4 o[HPOS].z, c[2], v[0];" "DP4 o[HPOS].w, c[3], v[0];" // transform the coords to the eye-space "DP4 R4.x, c[4], v[0];" "DP4 R4.y, c[5], v[0];" "DP4 R4.z, c[6], v[0];" "DP4 R4.w, c[7], v[0];" // transform the normals to eye-space "DP3 R7.x, c[4], v[1];" "DP3 R7.y, c[5], v[1];" "DP3 R7.z, c[6], v[1];" "DP3 R7.w, c[7], v[1];" // transform the normals from eye-space to world-space "DP3 o[TEX1].x, R7, c[24];" "DP3 o[TEX2].y, R7, c[25];" "DP3 o[TEX3].z, R7, c[26];" // put view dir into w of tex[1..3] "DP4 o[TEX1].w, R4, c[24];" "DP4 o[TEX2].w, R4, c[25];" "DP4 o[TEX3].w, R4, c[26];" // copy texcoords "MOV o[TEX0], v[2];" // done "END"; // More complex vertex program. It uses tangent space transformations to // achieve a more realistic bump. // // CONSTANTS: // 0- 3 4x4 Projection matrix // 4- 7 4x4 ModelView matrix // 20-22 light amb/diff/spec // 23 light dir // 24-27 4x4 view transpose // // VERTEX REGISTERS: // 0 - coord // 1 - normal // 2 - texcoord0 // 3 - texcoord1 // 4 - texcoord2 (binorm) // // REGISTERS: // 4 = skinned (eye space) coord // 5 = skinned (eye space) tangent // 6 = skinned (eye space) binormal // 7 = skinned (eye space) normal // 8 = eye space view vector // 9 = eye space half-angle vector char originalVertexProgramString[] = "!!VP1.0\n" // skin the vertices "DP4 R4.x, c[4], v[0];" "DP4 R4.y, c[5], v[0];" "DP4 R4.z, c[6], v[0];" "DP4 R4.w, c[7], v[0];" // final projection transformation // transform the skinned coords by the projection matrix "DP4 o[HPOS].x, c[0], v[0];" "DP4 o[HPOS].y, c[1], v[0];" "DP4 o[HPOS].z, c[2], v[0];" "DP4 o[HPOS].w, c[3], v[0];" // skin the binormals // skin binormals for bone0 "DP3 R6.x, c[4], v[4];" "DP3 R6.y, c[5], v[4];" "DP3 R6.z, c[6], v[4];" "DP3 R6.w, c[7], v[4];" // skin the normals // skin normals for bone0 "DP3 R7.x, c[4], v[1];" "DP3 R7.y, c[5], v[1];" "DP3 R7.z, c[6], v[1];" "DP3 R7.w, c[7], v[1];" // renormalize and orthogonalize binormal, tangent & normal // build tangent "MUL R5, R6.zxyw, R7.yzxw;" "MAD R5, R6.yzxw, R7.zxyw, -R5;" // normalize tangent "DP3 R5.w, R5, R5;" "RSQ R5.w, R5.w;" "MUL R5.xyz, R5, R5.w;" // put the sign in the tangent "MUL R5.xyz, R5, v[4].w;" // fill texture coords with tangent space matrix for pixel shaders // rotate tangent space matrix by view transpose "DP3 o[TEX1].x, -R5, c[24];" "DP3 o[TEX2].x, -R5, c[25];" "DP3 o[TEX3].x, -R5, c[26];" "DP3 o[TEX1].y, -R6, c[24];" "DP3 o[TEX2].y, -R6, c[25];" "DP3 o[TEX3].y, -R6, c[26];" "DP3 o[TEX1].z, R7, c[24];" "DP3 o[TEX2].z, R7, c[25];" "DP3 o[TEX3].z, R7, c[26];" // put view dir into w of tex[1..3] "DP4 o[TEX1].w, -R4, c[24];" "DP4 o[TEX2].w, -R4, c[25];" "DP4 o[TEX3].w, -R4, c[26];" // misc // put diffuse lighting into color "DP3 o[COL0], R7, c[23];" "MOV o[COL0].w, c[50];" // copy texcoords "MOV o[TEX0], v[3];" // done "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 hwReflectBumpShader_NV20::loadVertexProgramGL() { // Only load this vertex program once. if (vertex_program_id == 0) initVertexProgram(originalVertexProgramString, &vertex_program_id); // CONSTANTS: // 0- 3 4x4 ModelView-Projection composite matrix // 4- 7 4x4 ModelView matrix // 20-22 light amb/diff/spec // 23 light dir // 24-27 4x4 view transpose // // VERTEX REGISTERS: // 0 - coord // 1 - normal // 2 - texcoord0 // // In this example, the upper-left 3x3 of the modelview matrix (M) and // the upper-left 3x3 of the inverse transpose of the modelview matrix (M-t) // are used interchangeably. This is because the modelview matrix contains // only rigid-body transformations (rotation and translation), and in this // case the matrices are identical. // glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV); glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4, GL_MODELVIEW, GL_IDENTITY_NV); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 20, 1.0, 1.0, 1.0, 1.0); // light amb color glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 21, 1.0, 1.0, 1.0, 1.0); // light diff color glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 22, 1.0, 1.0, 1.0, 1.0); // light spec color glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 23, 0.0, 0.0, 1.0, 0.0); // light direction, for now. // Get the modelView matrix // GLfloat modelViewMatrix[16]; glGetFloatv(GL_MODELVIEW_MATRIX, modelViewMatrix); float stupidMatrix[4][4]; for (int i=0; i<16; i++) { stupidMatrix[i/4][i%4] = modelViewMatrix[i]; } MMatrix mvMatrix(stupidMatrix); // Calculate the view transpose matrix. // MMatrix mv = m_ModelMatrix.inverse() * mvMatrix; mv = mv.transpose(); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 24, mv[0][0], mv[1][0], mv[2][0], mv[3][0]); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 25, mv[0][1], mv[1][1], mv[2][1], mv[3][1]); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 26, mv[0][2], mv[1][2], mv[2][2], mv[3][2]); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 27, mv[0][3], mv[1][3], mv[2][3], mv[3][3]); } // Initialize the necessary OpenGL extensions // void hwReflectBumpShader_NV20::init_ext(const char * ext) { if(!glh_init_extension(ext)) { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); } } hwReflectBumpShader_NV20::hwReflectBumpShader_NV20() { m_pTextureCache = MTextureCache::instance(); init_ext("GL_ARB_multitexture"); init_ext("GL_NV_register_combiners"); init_ext("GL_NV_texture_shader"); init_ext("GL_NV_vertex_program"); bumpScale = 1.0; cubeMapOnly = FALSE; texNames[0] = texNames[1] = texNames[2] = texNames[3] = texNames[4] = texNames[5] = 0; currentTextureNames[0] = ""; currentTextureNames[1] = ""; currentTextureNames[2] = ""; currentTextureNames[3] = ""; currentTextureNames[4] = ""; currentTextureNames[5] = ""; attachSceneCallbacks(); vertex_program_id = 0; } hwReflectBumpShader_NV20::~hwReflectBumpShader_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 hwReflectBumpShader_NV20::releaseEverything() { if (texNames[0] != 0) glDeleteTextures(6, &texNames[0]); releaseVertexProgram(&vertex_program_id); // Release the texture cache through refcounting. m_pTextureCache->release(); if(!MTextureCache::getReferenceCount()) { m_pTextureCache = 0; } } void hwReflectBumpShader_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 hwReflectBumpShader_NV20::releaseCallback(void* clientData) { hwReflectBumpShader_NV20 *pThis = (hwReflectBumpShader_NV20*) clientData; pThis->releaseEverything(); } void hwReflectBumpShader_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( "hwReflectBumpShader_NV20", hwReflectBumpShader_NV20::id, hwReflectBumpShader_NV20::creator, hwReflectBumpShader_NV20::initialize, MPxNode::kHwShaderNode, &UserClassify ); if (!status) { status.perror("registerNode"); return status; } return MS::kSuccess; } MStatus uninitializePlugin( MObject obj ) { MStatus status; MFnPlugin plugin( obj ); // Unregister all chamelion shader nodes plugin.deregisterNode( hwReflectBumpShader_NV20::id ); if (!status) { status.perror("deregisterNode"); return status; } return MS::kSuccess; } void * hwReflectBumpShader_NV20::creator() { return new hwReflectBumpShader_NV20(); } MStatus hwReflectBumpShader_NV20::initialize() { MFnNumericAttribute nAttr; MStatus status; MFnTypedAttribute sAttr; // For string attributes // Create input attrubutes 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); bumpR = nAttr.create( "bumpR", "c2r",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); bumpG = nAttr.create( "bumpG", "c2g",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); bumpB = nAttr.create( "bumpB", "c2b",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); bump = nAttr.create( "bump", "c2", bumpR, bumpG, bumpB); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f, 1.0f, 1.0f); nAttr.setUsedAsColor(true); uCoord = nAttr.create( "uCoord", "u", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.5f); vCoord = nAttr.create( "vCoord", "v", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.5f); uvCoord = nAttr.create( "uvCoord","uv", uCoord, vCoord); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.5f, 0.5f ); nAttr.setHidden(true); uBias = nAttr.create( "uBias", "bu", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setMin(0.0f); nAttr.setMax(1.0f); nAttr.setKeyable(true); nAttr.setDefault(0.5f); vBias = nAttr.create( "vBias", "bv", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setMin(0.0f); nAttr.setMax(1.0f); nAttr.setDefault(0.5f); uvFilterSizeX = nAttr.create( "uvFilterSizeX", "fsx", MFnNumericData::kFloat); nAttr.setStorable(false); nAttr.setReadable(true); nAttr.setWritable(true); nAttr.setHidden(true); uvFilterSizeY = nAttr.create( "uvFilterSizeY", "fsy", MFnNumericData::kFloat); nAttr.setStorable(false); nAttr.setReadable(true); nAttr.setWritable(true); nAttr.setHidden(true); uvFilterSize = nAttr.create("uvFilterSize","fs",uvFilterSizeX,uvFilterSizeY); nAttr.setStorable(false); nAttr.setReadable(true); nAttr.setWritable(true); nAttr.setHidden(true); // create output attributes here // outColor is the only output attribute and it is inherited // so we do not need to create or add it. // // Add the attributes here addAttribute(color); addAttribute(bump); addAttribute(uvCoord); addAttribute(uBias); addAttribute(vBias); addAttribute(uvFilterSize); attributeAffects (colorR, outColor); attributeAffects (colorG, outColor); attributeAffects (colorB, outColor); attributeAffects (color, outColor); attributeAffects (bumpR, outColor); attributeAffects (bumpG, outColor); attributeAffects (bumpB, outColor); attributeAffects (bump, outColor); attributeAffects (uCoord, outColor); attributeAffects (vCoord, outColor); attributeAffects (uvCoord, outColor); attributeAffects (uBias, outColor); attributeAffects (vBias, outColor); return MS::kSuccess; } // DESCRIPTION: // See hwDecalBumpShader_NV20::compute(). // MStatus hwReflectBumpShader_NV20::compute( const MPlug& plug, MDataBlock& block ) { bool k = false; k |= (plug==outColor); k |= (plug==outColorR); k |= (plug==outColorG); k |= (plug==outColorB); if( !k ) return MS::kUnknownParameter; // set output color attribute MDataHandle outColorHandle = block.outputValue( outColor ); MFloatVector& outColor = outColorHandle.asFloatVector(); outColor.x = 1.0; outColor.y = 0.5; outColor.z = 0.5; outColorHandle.setClean(); return MS::kSuccess; } // To get 3 float values from the node attribute // MStatus hwReflectBumpShader_NV20::getFloat3(MObject attr, float value[3]) { MStatus status = MS::kSuccess; // Get the attr to use // MPlug plug(thisMObject(), attr); MObject object; status = plug.getValue(object); if (!status) { status.perror("hwReflectBumpShader_NV20::bind plug.getValue."); return status; } MFnNumericData data(object, &status); if (!status) { status.perror("hwReflectBumpShader_NV20::bind construct data."); return status; } status = data.getData(value[0], value[1], value[2]); if (!status) { status.perror("hwReflectBumpShader_NV20::bind get values."); return status; } return status; } // To get a string value from the node attribute // MStatus hwReflectBumpShader_NV20::getString(MObject attr, MString &str) { MPlug plug(thisMObject(), attr); MStatus status = plug.getValue( str ); return status; } /* virtual */ MStatus hwReflectBumpShader_NV20::bind(const MDrawRequest& request, M3dView& view) { MStatus status; bool isHeightFieldMap = true; // Should be set to the value of an attribute m_ModelMatrix = request.multiPath().inclusiveMatrix(); // Get the cube mapand bump map file names MStringArray decalNames; MString decalName; MString bumpName; ShadingConnection colorConnection(thisMObject(), request.multiPath().partialPathName(), "color"); ShadingConnection bumpConnection (thisMObject(), request.multiPath().partialPathName(), "bump"); // 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 = "D:/chameleon/textures/Cham_body_color_real.tga"; // Append next environment map name decalNames.append( decalName ); } // If any of the environment maps are not mapped put in a fake texture else { decalName = "D:/chameleon/textures/Cham_body_color_real.tga"; decalNames.append( decalName ); } } } else { // Put in a fake texture for each side decalName = "D:/chameleon/textures/Cham_body_color_real.tga"; for (int i=0; i<6; i++) { decalNames.append( decalName ); } } // If the bump attribute is ultimately connected to a file texture, find its filename. // otherwise use the default bump texture. if (bumpConnection.type() == ShadingConnection::TEXTURE && bumpConnection.texture().hasFn(MFn::kFileTexture)) { // Get the filename of the texture. MFnDependencyNode textureNode(bumpConnection.texture()); MPlug filenamePlug( bumpConnection.texture(), textureNode.attribute(MString("fileTextureName")) ); filenamePlug.getValue(bumpName); } else { bumpName = ""; } // See if we are doing cube-map only. i.e. no bump // cubeMapOnly = (bumpName.length() == 0); // Reload cube maps if the name of the textures // for any of the cube maps changes // unsigned int width, height; bool reload = FALSE; for (int i=0; i<6; i++) { if (currentTextureNames[i] != decalNames[i]) { reload = TRUE; break; } } if ( reload ) { MString xpTexName(decalNames[2]); MString xnTexName(decalNames[3]); MString ypTexName(decalNames[0]); MString ynTexName(decalNames[1]); MString zpTexName(decalNames[4]); MString znTexName(decalNames[5]); MStatus stat; if (! (stat = theImage_XP.readFromFile(xpTexName)) ) return MS::kFailure; stat = theImage_XP.getSize( width, height ); 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 (texNames[0] == 0) glGenTextures(6, &texNames[0]); glBindTexture( GL_TEXTURE_2D, texNames[0] ); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XP.pixels() ); glBindTexture( GL_TEXTURE_2D, texNames[1] ); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XN.pixels() ); glBindTexture( GL_TEXTURE_2D, texNames[2] ); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YP.pixels() ); glBindTexture( GL_TEXTURE_2D, texNames[3] ); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YN.pixels() ); glBindTexture( GL_TEXTURE_2D, texNames[4] ); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZP.pixels() ); glBindTexture( GL_TEXTURE_2D, texNames[5] ); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZN.pixels() ); for (i=0; i<6; i++) { currentTextureNames[i] = decalNames[i]; } } // Get camera information needed // MDagPath cameraPath; status = view.getCamera( cameraPath ); // Get rotation angle and axis // MVector camAxis; double camTheta; MMatrix mmatrix = cameraPath.inclusiveMatrix( &status ); MTransformationMatrix tmatrix( mmatrix ); m_CameraRotation = tmatrix.rotation(); m_CameraRotation.getAxisAngle( camAxis, camTheta ); // Convert to degrees from radians camTheta *= 57.295779513082320876798154814105; // == (180 / M_PI) view.beginGL(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); // Background color is always white glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glColor4f( 1, 1, 1, 1 ); if (cubeMapOnly) { glActiveTextureARB( GL_TEXTURE0_ARB ); glEnable(GL_TEXTURE_CUBE_MAP_ARB); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); for (i=0; i<6; i++) glBindTexture( GL_TEXTURE_2D, texNames[i] ); 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_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // Flip from Maya to OGL coordinates + // rotate the Textures according to the camera orientation // glMatrixMode( GL_TEXTURE ); glPushMatrix(); glLoadIdentity(); glRotated( 180.0, 1.0, 0.0, 0.0 ); glRotated( camTheta, camAxis[0], camAxis[1], camAxis[2]); // Pop the matrix is done during unbind, not here //glPopMatrix(); glMatrixMode( GL_MODELVIEW ); } else { loadVertexProgramGL(); // Setup texture combiners // glEnable(GL_TEXTURE_SHADER_NV); // stage 0 -- bump normal map (input is u,v and normal map) glActiveTextureARB( GL_TEXTURE0_ARB ); glEnable(GL_TEXTURE_2D); // // We need to be able to pass the bumpScaleValue // to the texture cache and rebuild the bump or normal map if( isHeightFieldMap ) { // convert the HeightField to the NormalMap if(m_pTextureCache) m_pTextureCache->bind(bumpConnection.texture(), MTexture::NMAP, true); } else { if(m_pTextureCache) m_pTextureCache->bind(bumpConnection.texture(), MTexture::RGBA, true); } glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D); // stage 1 -- dot product (input is strq) glActiveTextureARB( GL_TEXTURE1_ARB ); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB); // stage 2 -- dot product (input is strq) glActiveTextureARB( GL_TEXTURE2_ARB ); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB); // stage 3 -- dot product reflect cube map (input is strq, and cube maps) // ====================================================================== glActiveTextureARB( GL_TEXTURE3_ARB ); glEnable(GL_TEXTURE_CUBE_MAP_ARB); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // Bind the 6 textures // for (i=0; i<6; i++) glBindTexture( GL_TEXTURE_2D, texNames[i] ); // Specify the texture parameters // 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_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Done setting the texture unit 3 // glActiveTextureARB( GL_TEXTURE0_ARB ); // define a white color // float white_color[4] = {1.0, 1.0, 1.0, 1.0}; glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, white_color); // The register combiner will do the multiplication between // the illumination and the decal color // glEnable(GL_REGISTER_COMBINERS_NV); glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1); // Combiner stage 0 get the input from texture stage3, pass through // glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE3_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_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE); // The final Combiner just pass through // 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); verify_shader_config( view ); } view.endGL(); return MS::kSuccess; } /* virtual */ MStatus hwReflectBumpShader_NV20::unbind(const MDrawRequest& request, M3dView& view) { view.beginGL(); if (cubeMapOnly) { // Pop the texture matrix pushed during bind glActiveTextureARB( GL_TEXTURE0_ARB ); glMatrixMode( GL_TEXTURE ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); glDisable( GL_TEXTURE_GEN_S ); glDisable( GL_TEXTURE_GEN_T ); glDisable( GL_TEXTURE_GEN_R ); glDisable( GL_TEXTURE_CUBE_MAP_ARB ); } else { glDisable(GL_TEXTURE_SHADER_NV); glActiveTextureARB( GL_TEXTURE0_ARB ); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP_ARB); glActiveTextureARB( GL_TEXTURE1_ARB ); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP_ARB); glActiveTextureARB( GL_TEXTURE2_ARB ); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP_ARB); glActiveTextureARB( GL_TEXTURE3_ARB ); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP_ARB); glDisable(GL_REGISTER_COMBINERS_NV); glDisable(GL_VERTEX_PROGRAM_NV); } glActiveTextureARB( GL_TEXTURE0_ARB ); glPopClientAttrib(); glPopAttrib(); view.endGL(); return MS::kSuccess; } /* virtual */ MStatus hwReflectBumpShader_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::kFailure; view.beginGL(); glEnable(GL_VERTEX_ARRAY); if (cubeMapOnly) { glVertexPointer(3, GL_FLOAT, 0, vertexArray); glEnable(GL_VERTEX_ARRAY); if (normalCount > 0) { glNormalPointer(GL_FLOAT, 0, normalArrays[0]); glEnable(GL_NORMAL_ARRAY); } glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray); glDisable(GL_VERTEX_ARRAY); glDisable(GL_NORMAL_ARRAY); } else { // Bind and enable the vertex program glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id); glEnable(GL_VERTEX_PROGRAM_NV); // VERTEX REGISTERS: // 0 - coord // 1 - normal // 2 - texcoord0 // 3 - texcoord1 // 4 - texcoord2 (binorm) glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray ); glVertexAttribPointerNV( 1, 3, GL_FLOAT, 0, normalArrays[0] ); glVertexAttribPointerNV( 2, 2, GL_FLOAT, 0, texCoordArrays[0] ); glVertexAttribPointerNV( 3, 2, GL_FLOAT, 0, texCoordArrays[0] ); glVertexAttribPointerNV( 4, 3, GL_FLOAT, 0, normalArrays[2] ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY1_NV ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY3_NV ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY4_NV ); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV ); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY1_NV ); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV ); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY3_NV ); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY4_NV ); glDisable(GL_VERTEX_PROGRAM_NV); } glDisable(GL_VERTEX_ARRAY); view.endGL(); return MS::kSuccess; } /* virtual */ int hwReflectBumpShader_NV20::normalsPerVertex() { return 3; } /* virtual */ int hwReflectBumpShader_NV20::texCoordsPerVertex() { return 1; }