//- // ========================================================================== // 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 both decal // (base color) and bump textures. // // This shader builds on the foundation demonstrated in the // 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. // // PS: Thanks go to DAR from nVidia, for his help in making this // shader more robust. ;-) // /////////////////////////////////////////////////////////////////// // Uncomment the #ifdef below if you want to debug the vertex program by // output a COL0 value that corresponds to an intermediate calculation. // The only sane way we could find to debug that thing. //#define DEBUGGING_VERTEX_PROGRAM 1 #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 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 "hwDecalBumpShader_NV20.h" #include "ShadingConnection.h" MTypeId hwDecalBumpShader_NV20::id( 0x00105441 ); /*static*/ const unsigned int hwDecalBumpShader_NV20::lookup_texture_size(256); void hwDecalBumpShader_NV20::postConstructor( ) { setMPSafe(false); } // Static attribute instances. // MObject hwDecalBumpShader_NV20::color; MObject hwDecalBumpShader_NV20::colorR; MObject hwDecalBumpShader_NV20::colorG; MObject hwDecalBumpShader_NV20::colorB; MObject hwDecalBumpShader_NV20::bump; MObject hwDecalBumpShader_NV20::bumpR; MObject hwDecalBumpShader_NV20::bumpG; MObject hwDecalBumpShader_NV20::bumpB; MObject hwDecalBumpShader_NV20::camera; MObject hwDecalBumpShader_NV20::cameraX; MObject hwDecalBumpShader_NV20::cameraY; MObject hwDecalBumpShader_NV20::cameraZ; MObject hwDecalBumpShader_NV20::light; MObject hwDecalBumpShader_NV20::lightX; MObject hwDecalBumpShader_NV20::lightY; MObject hwDecalBumpShader_NV20::lightZ; MObject hwDecalBumpShader_NV20::uCoord; MObject hwDecalBumpShader_NV20::vCoord; MObject hwDecalBumpShader_NV20::uvCoord; MObject hwDecalBumpShader_NV20::uBias; MObject hwDecalBumpShader_NV20::vBias; MObject hwDecalBumpShader_NV20::uvFilterSize; MObject hwDecalBumpShader_NV20::uvFilterSizeX; MObject hwDecalBumpShader_NV20::uvFilterSizeY; MObject hwDecalBumpShader_NV20::shininess; MObject hwDecalBumpShader_NV20::lightColor; MObject hwDecalBumpShader_NV20::lightColorR; MObject hwDecalBumpShader_NV20::lightColorG; MObject hwDecalBumpShader_NV20::lightColorB; void hwDecalBumpShader_NV20::printGlError( const char *call ) { GLenum error; while( (error = glGetError()) != GL_NO_ERROR ) { assert(0); cerr << call << ":" << error << " is " << (const char *)gluErrorString( error ) << "\n"; } } // The Vertex Program for the Decal Bump effect. // // CONSTANTS: // 0- 3 4x4 ModelView-Projection composite matrix // 4- 7 4x4 ModelView matrix // 8-10 light amb/diff/spec // 11 light dir vector (from surface to light) // VERTEX REGISTERS: // 0 - coord // 1 - normal // 2 - texcoord0 // 3 - texcoord1 // 4 - texcoord2 (binorm) // REGISTERS: // 4 = eye space vertex coordinate // 5 = eye space tangent vector // 6 = eye space binormal vector // 7 = eye space normal vector (VERIFIED) // 8 = normalized eye space view vector (VERIFIED) (goes in the direction from vertex position to camera position) // 9 = eye space half-angle vector // char vertexProgramString[] = "!!VP1.0\n" // Multiply the vertex coords by the modelview-projection composite matrix, // to get clip space coordinates. "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];" // Multiply the vertex coords by the modelview matrix, // to get eye-space coordinates. "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];" // Multiply the normals by the modelview matrix, // resulting in eye-space normals. "DP3 R7.x, c[4], v[1];" "DP3 R7.y, c[5], v[1];" "DP3 R7.z, c[6], v[1];" // Re-normalize, in case the model-view matrix's is not a simple rotation. "DP3 R7.w, R7, R7;" "RSQ R7.w, R7.w;" "MUL R7.xyz, R7, R7.w;" // Multiply the binormals by the modelview matrix, // resulting in eye-space binormals. "DP3 R6.x, c[4], v[4];" "DP3 R6.y, c[5], v[4];" "DP3 R6.z, c[6], v[4];" // Re-normalize the binormals. "DP3 R6.w, R6, R6;" "RSQ R6.w, R6.w;" "MUL R6.xyz, R6, R6.w;" // Build tangent: tangent = binormal x normal. "MUL R5, R6.zxyw, R7.yzxw;" "MAD R5, R6.yzxw, R7.zxyw, -R5;" // put the sign in the tangent. "MUL R5.xyz, R5, v[4].w;" // Re-normalize the tangent. "DP3 R5.w, R5, R5;" "RSQ R5.w, R5.w;" "MUL R5.xyz, R5, R5.w;" // Calculate eye space view vector. // (In essence, a normalization of the inverse // of the vertex coordinate in eye-space) "DP3 R8.w, R4, R4;" "RSQ R8.w, R8.w;" "MUL R8.xyz, R4, -R8.w;" // Calculate (and normalize) tangent space half-angle vector. "ADD R9, R8, c[11];" "DP3 R9.w, R9, R9;" "RSQ R9.w, R9.w;" "MUL R9.xyz, R9, R9.w;" // Transform half-angle vector into tangent space. "DP3 o[TEX3].x, R5, R9;" "DP3 o[TEX3].y, R6, R9;" "DP3 o[TEX3].z, R7, R9;" // Transform light direction vector into tangent space. "DP3 o[TEX2].x, R5, c[11];" "DP3 o[TEX2].y, R6, c[11];" "DP3 o[TEX2].z, R7, c[11];" #ifndef DEBUGGING_VERTEX_PROGRAM // Put diffuse lighting into color. "DP3 o[COL0], R7, c[11];" #else // VISUALIZE NORMAL VECTOR IN EYE SPACE "MOV o[COL0], R7;" #endif // Copy texcoords. "MOV o[TEX0], v[2];" "MOV o[TEX1], v[3];" "END"; // Load the vertexProgram and fill in the necessary constants used in the vertex program. // void hwDecalBumpShader_NV20::loadVertexProgramGL() { GLenum error = glGetError(); assert(!error); // If the vertex program hasn't been created yet, do it now. // Note that Maya shares textures, display lists and vertex programs // between all viewports, so this only need to be done once. if (vertex_program_id == 0) { glGenProgramsNV(1, &vertex_program_id); error = glGetError(); assert(!error); // Attempt to load the program. unsigned int length = strlen(vertexProgramString); glLoadProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id, length, (const GLubyte *) vertexProgramString); error = glGetError(); assert(!error); if (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); } } } // Set up the constant values. // // CONSTANTS: // 0- 3 4x4 ModelView-Projection composite matrix // 4- 7 4x4 ModelView matrix // 8-10 light amb/diff/spec // 11 light dir vector (from surface to light) // 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, 8, 1, 1, 1, 1); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, 1, 1, 1, 1); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 10, 1, 1, 1, 1); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, -lightRotation[0], -lightRotation[1], -lightRotation[2], 0); // light dir... } // Illumination (diffuse and specular) lookup table // (This function assumes that lookup_image and lookup_texture have been deallocated.) // void hwDecalBumpShader_NV20::make_lookup_texture() { // Re-calculate the look-up texture, if the shininess value/scale has changed. // float shininessScale = 1.0f; MPlug plug(thisMObject(), shininess); // Get the shininess scaling factor // MStatus status = plug.getValue(shininessScale); if (!status) { status.perror("hwDecalBumpShader_NV20::bind plug.getValue."); return; } if ( shininessScale < 0.01f ) shininessScale = 0.01f; if ( shininessScale > 1.0f ) shininessScale = 1.0f; // shininess factor is between 20 and 180 // float shininessValue = 1.0f; shininessValue = 200.0f * (1.0f - 0.8f * shininessScale); // Only recompute the lookup texture if the values have changed since the last bind. if (shininessValue == currentShininessValue && shininessScale == currentShininessScale) return; currentShininessValue = shininessValue; currentShininessScale = shininessScale; unsigned int imgsize = lookup_texture_size; float imgsizeM1 = (float) (imgsize - 1); // Allocate the lookup_image and lookup_texture. if (lookup_table == NULL) lookup_table = new unsigned char[imgsize*imgsize*2]; if (lookup_texture == NULL) lookup_texture = new tex_object_2D; // Fill it up. unsigned char * ip = lookup_table; for(int j=0; j < imgsize; j++) { unsigned char a = (unsigned char) (shininessScale * (255.0 * pow((j/imgsizeM1), shininessValue))); for(int i=0; i < imgsize; i++) { *ip++ = (unsigned char) ((255.0 - 64) * (i/imgsizeM1)) + 64; *ip++ = a; } } fLookupTextureReprocessed = true; return; } void hwDecalBumpShader_NV20::bind_lookup_table() { make_lookup_texture(); lookup_texture->bind(); if (fLookupTextureReprocessed) { glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, lookup_texture_size, lookup_texture_size, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, lookup_table); fLookupTextureReprocessed = false; } lookup_texture->parameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); lookup_texture->parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); lookup_texture->parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); lookup_texture->parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } // Initialize the necessary OpenGL extensions // void hwDecalBumpShader_NV20::init_ext(const char * ext) { if(!glh_init_extension(ext)) { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); } } hwDecalBumpShader_NV20::hwDecalBumpShader_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"); isDirectionalLight = true; // light's rotation is connected to the lightRotation attr // Set the shininess and shininess scale to absurd values, so that the // look-up table automatically get recomputed during the first update. currentShininessValue = -1.0; currentShininessScale = -1.0; lookup_texture = NULL; lookup_table = NULL; fLookupTextureReprocessed = false; // Initialize callbacks. fBeforeNewCB = 0; fBeforeOpenCB = 0; fBeforeRemoveReferenceCB = 0; fMayaExitingCB = 0; attachSceneCallbacks(); vertex_program_id = 0; // handle for the Vertex Program // The vertex program will get loaded during the first refresh. // We cannot do it here since it's not guaranteed that the GL // context will be current at construction or destruction time // (although it generally is). } hwDecalBumpShader_NV20::~hwDecalBumpShader_NV20() { detachSceneCallbacks(); } void hwDecalBumpShader_NV20::releaseEverything() { release_lookup_texture(); if (vertex_program_id > 0) { glFinish(); // Unbind any program. glBindProgramNV(GL_VERTEX_PROGRAM_NV, 0); // Delete the program used by this shader. glDeleteProgramsNV(1, &vertex_program_id); // For sanity. vertex_program_id = 0; } // Release the texture cache through refcounting. m_pTextureCache->release(); if(!MTextureCache::getReferenceCount()) { m_pTextureCache = 0; } } void hwDecalBumpShader_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 hwDecalBumpShader_NV20::releaseCallback(void* clientData) { hwDecalBumpShader_NV20 *pThis = (hwDecalBumpShader_NV20*) clientData; pThis->releaseEverything(); } void hwDecalBumpShader_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.5", "Any"); status = plugin.registerNode( "hwDecalBumpShader_NV20", hwDecalBumpShader_NV20::id, hwDecalBumpShader_NV20::creator, hwDecalBumpShader_NV20::initialize, MPxNode::kHwShaderNode, &UserClassify ); if (!status) { status.perror("registerNode"); return status; } return MS::kSuccess; } MStatus uninitializePlugin( MObject obj ) { MStatus status; MFnPlugin plugin( obj ); plugin.deregisterNode( hwDecalBumpShader_NV20::id ); if (!status) { status.perror("deregisterNode"); return status; } return MS::kSuccess; } void * hwDecalBumpShader_NV20::creator() { return new hwDecalBumpShader_NV20(); } // Initialize the plug-in. Called once when the plug-in is loaded. // This mostly involve creating attributes. MStatus hwDecalBumpShader_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); 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); lightX = nAttr.create( "lightX", "lgtx",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.0f); lightY = nAttr.create( "lightY", "lgty",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); lightZ = nAttr.create( "lightZ", "lgtz",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); light = nAttr.create( "light", "lgt", lightX, lightY, lightZ); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.0f, 1.0f, 1.0f); cameraX = nAttr.create( "cameraX", "camx",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.0f); cameraY = nAttr.create( "cameraY", "camy",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.0f); cameraZ = nAttr.create( "cameraZ", "camz",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); camera = nAttr.create( "camera", "cam", cameraX, cameraY, cameraZ); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(0.0f, 0.0f, 1.0f); shininess = nAttr.create( "shininess", "sn", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setMin(0.0f); nAttr.setMax(1.0f); nAttr.setDefault(0.5f); lightColorR = nAttr.create( "lightColorR", "lcr", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); lightColorG = nAttr.create( "lightColorG", "lcg", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); lightColorB = nAttr.create( "lightColorB", "lcb", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); lightColor = nAttr.create( "lightColor", "lc", lightColorR, lightColorG, lightColorB); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f, 1.0f, 1.0f); nAttr.setUsedAsColor(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); addAttribute(light); addAttribute(camera); addAttribute(shininess); addAttribute(lightColor); 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); attributeAffects (lightX, outColor); attributeAffects (lightY, outColor); attributeAffects (lightZ, outColor); attributeAffects (light, outColor); attributeAffects (cameraX, outColor); attributeAffects (cameraY, outColor); attributeAffects (cameraZ, outColor); attributeAffects (camera, outColor); attributeAffects (shininess, outColor); attributeAffects (lightColorR, outColor); attributeAffects (lightColorG, outColor); attributeAffects (lightColorB, outColor); attributeAffects (lightColor, outColor); return MS::kSuccess; } // This function gets called by Maya to evaluate the shader. // See "Writing a shading node plug-in" in the documentation // for more information. // // MStatus hwDecalBumpShader_NV20::compute( const MPlug& plug, MDataBlock& block ) { bool k; k = (plug == outColor) || (plug == outColorR) || (plug == outColorG) || (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 hwDecalBumpShader_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("hwDecalBumpShader_NV20::getFloat3 plug.getValue."); return status; } MFnNumericData data(object, &status); if (!status) { status.perror("hwDecalBumpShader_NV20::getFloat3 construct data."); return status; } status = data.getData(value[0], value[1], value[2]); if (!status) { status.perror("hwDecalBumpShader_NV20::getFloat3 get values."); return status; } return status; } // To get a string value from the node attribute // MStatus hwDecalBumpShader_NV20::getString(MObject attr, MString &str) { MPlug plug(thisMObject(), attr); MStatus status = plug.getValue( str ); return status; } /* virtual */ MStatus hwDecalBumpShader_NV20::bind(const MDrawRequest& request, M3dView& view) { MStatus status; // Get the diffuse color // float diffuse_color[4]; status = getFloat3(color, diffuse_color); diffuse_color[3] = 1.0; if (!status) return status; // Get the light color // float light_color[4]; light_color[3] = 1.0f; status = getFloat3(lightColor, light_color); if (!status) return status; // Get the light direction (for directionalLight) // status = getFloat3(light, &lightRotation[0]); if (!status) return status; // Get the bumpScale value // float bumpScaleValue = 2.0f; // Get the bumpMap type // bool isHeightFieldMap = true; // Direction of the directional light // // Convert the light direction (which is assumed in originally be in world space, in euler coordinates) // into an eye space vector. // double scale = M_PI/180.0; // Internal rotations are in radian and not in degrees MEulerRotation lightRot( lightRotation[0] * scale, lightRotation[1] * scale, lightRotation[2] * scale ); MVector light_v = MVector(0, 0, -1).rotateBy( lightRot ); // WS light vector MDagPath camDag; view.getCamera(camDag); light_v = light_v * camDag.inclusiveMatrixInverse(); lightRotation[0] = (float) light_v[0]; lightRotation[1] = (float) light_v[1]; lightRotation[2] = (float) light_v[2]; // Get the camera position // status = getFloat3(camera, &cameraPos[0]); if (!status) return status; // Get the decal and bump map file names // 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 file texture, find its filename. // otherwise use the default color texture. if (colorConnection.type() == ShadingConnection::TEXTURE && colorConnection.texture().hasFn(MFn::kFileTexture)) { // Get the filename of the texture. MFnDependencyNode textureNode(colorConnection.texture()); MPlug filenamePlug( colorConnection.texture(), textureNode.attribute(MString("fileTextureName")) ); filenamePlug.getValue(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(colorConnection.texture()); MPlug filenamePlug( bumpConnection.texture(), textureNode.attribute(MString("fileTextureName")) ); filenamePlug.getValue(bumpName); } // Fail safe quit // if (bumpName.length() == 0 || decalName.length() == 0) { view.beginGL(); glPushAttrib( GL_ALL_ATTRIB_BITS ); // This might be too conservative glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glColor4fv(diffuse_color); view.endGL(); return MS::kSuccess; } view.beginGL(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); /* Starts Here... */ glEnable(GL_TEXTURE_SHADER_NV); // stage 0 -- decal map glActiveTextureARB( GL_TEXTURE0_ARB ); if(m_pTextureCache) m_pTextureCache->bind(colorConnection.texture(), MTexture::RGBA, false); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D); // stage 1 -- bumpped normal map glActiveTextureARB( GL_TEXTURE1_ARB ); // 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, false); } else { if(m_pTextureCache) m_pTextureCache->bind(bumpConnection.texture(), MTexture::RGBA, false); } glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D); // stage 2 -- dot product (diffuse component) glActiveTextureARB( GL_TEXTURE2_ARB ); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV, GL_EXPAND_NORMAL_NV); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE1_ARB); // stage 3 -- dot product (specular component) glActiveTextureARB( GL_TEXTURE3_ARB ); bind_lookup_table(); // 2D texture to get the diffuse and specular illumination glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_TEXTURE_2D_NV); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV, GL_EXPAND_NORMAL_NV); glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE1_ARB); // With light color and intensity // glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, diffuse_color); glCombinerParameterfvNV(GL_CONSTANT_COLOR1_NV, light_color); // The register combiner will do the multiplication between // the illumination and the decal color // glEnable(GL_REGISTER_COMBINERS_NV); #ifndef DEBUGGING_VERTEX_PROGRAM glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 2); #else // For testing, only use one general register combiner. glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1); #endif float constColor0[4]; constColor0[0] = constColor0[1] = constColor0[2] = constColor0[3] = 1.0; glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, constColor0); #ifndef DEBUGGING_VERTEX_PROGRAM // Combiner stage 0 does the illumination modulation on the surface decal color // 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_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA); glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA); glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE); // Combiner stage 1, modulate the surface color by the light color // glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB); glCombinerOutputNV(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE); #else // Simplified register combiners to help debugging vertex program. glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_PRIMARY_COLOR_NV, 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_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA); glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA); glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE); #endif // DEBUGGING_VERTEX_PROGRAM // 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_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB); view.endGL(); return MS::kSuccess; } /* virtual */ MStatus hwDecalBumpShader_NV20::unbind(const MDrawRequest& request, M3dView& view) { view.beginGL(); glDisable(GL_REGISTER_COMBINERS_NV); glDisable(GL_TEXTURE_SHADER_NV); glActiveTextureARB( GL_TEXTURE0_ARB ); glPopClientAttrib(); glPopAttrib(); view.endGL(); return MS::kSuccess; } // Compute array of binormals. Return the pointer to the array if succesful, // NULL otherwise. float* hwDecalBumpShader_NV20::computeBinormals(int indexCount, const unsigned int * indexArray, int vertexCount, const float* vertexArray, const float* normalArray, const float* texCoordArray) { // Allocate the array of binormals. float *biNormalArray = new float[3*vertexCount]; // Allocate space for a triangle of vertices, textures, normals, binormals. // This triangle will be used to compute the binormal vector. // const float * v[3]; // Vertex (x,y,z) const float * t[3]; // Texture (s,t) const float * n[3]; // Normal (x,y,z) float * b[3]; // BiNormal (x,y,z) for (int i = 0; i < indexCount; i += 3) { int i0 = indexArray[i+0]; int i1 = indexArray[i+1]; int i2 = indexArray[i+2]; // Get the xyz coords of the corners of the triangle. // v[0] = vertexArray + 3 * i0; v[1] = vertexArray + 3 * i1; v[2] = vertexArray + 3 * i2; // Get the st coords of the corners of the triangle. // t[0] = texCoordArray + 2 * i0; t[1] = texCoordArray + 2 * i1; t[2] = texCoordArray + 2 * i2; // Get the normals at the corners of the triangle. // n[0] = normalArray + 3 * i0; n[1] = normalArray + 3 * i1; n[2] = normalArray + 3 * i2; // Get pointers to the binormal vectors. // b[0] = biNormalArray + 3 * i0; b[1] = biNormalArray + 3 * i1; b[2] = biNormalArray + 3 * i2; // ********************************************* // ******** compute the binormal vector ******** // ********************************************* vec3f plane[3]; vec3f c0, c1, c2; vec3f s0, s1; // Calculate plane equations for the planes defined // by the (x, s, t), (y, s, t), and (z, s, t) coords. int idx; for (idx = 0; idx < 3; ++idx) { // Set up the three corners c0.set_value(v[0][idx], t[0][0], t[0][1]); c1.set_value(v[1][idx], t[1][0], t[1][1]); c2.set_value(v[2][idx], t[2][0], t[2][1]); // Calculate two sides s0 = c0 - c2; s1 = c1 - c2; // Calculate the normal of the plane plane[idx] = s1.cross(s0); } // Now solve for the texture gradients dsdx, dsty, dsdt, ... vec3f ds, dt, dn; for (idx = 0; idx < 3; ++idx) { ds[idx] = -plane[idx][1]/plane[idx][0]; dt[idx] = -plane[idx][2]/plane[idx][0]; } dt.normalize(); dn = ds.cross(dt); // Make sure that our computed normal vector points in the // same direction as the input normal vector. vec3f normal(n[0]); if (normal.dot(dn) < 0) { // They pointed in different directions, negate ds.negate(); dt.negate(); dn.negate(); } // Compute the biNormal vector and store them in the biNormal array for (idx = 0; idx < 3; ++idx) { vec3f vn = n[idx]; vec3f vb = vn.cross( dt ); vb.normalize(); for( int j = 0; j < 3; ++j ) b[idx][j] = vb[j]; } } return biNormalArray; } /* virtual */ MStatus hwDecalBumpShader_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; view.beginGL(); // Bind the vertex program. Note that this will automatically // create and compile the vertex program, the first time // this function gets called. // loadVertexProgramGL(); // Bind and enable the vertex program // glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id); glEnable(GL_VERTEX_PROGRAM_NV); // VERTEX REGISTERS (Attributes): // 0 - coord // 1 - normal // 2 - texcoord0 (decal 2D texture) // 3 - texcoord1 (bump 2D texture) // 4 - texcoord2 (binormal vector (in object space)) 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); glClientActiveTextureARB(GL_TEXTURE0_ARB); view.endGL(); return MS::kSuccess; } /* virtual */ int hwDecalBumpShader_NV20::normalsPerVertex() { return 3; } /* virtual */ int hwDecalBumpShader_NV20::texCoordsPerVertex() { return 1; } // Release the lookup texture/image. void hwDecalBumpShader_NV20::release_lookup_texture() { if (lookup_table) { delete lookup_table; lookup_table = NULL; } if (lookup_texture) { delete lookup_texture; lookup_table = NULL; } }