//- // ========================================================================== // Copyright (C) 1995 - 2005 Alias Systems Corp. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files are provided by Alias // Systems Corp. ("Alias") and/or its licensors for the exclusive use of the // Customer (as defined in the Alias Software License Agreement that // accompanies this Alias software). Such Customer has the right to use, // modify, and incorporate the Data into other products and to distribute such // products for use by end-users. // // THE DATA IS PROVIDED "AS IS". ALIAS HEREBY DISCLAIMS ALL WARRANTIES // RELATING TO THE DATA, INCLUDING, WITHOUT LIMITATION, ANY AND ALL EXPRESS OR // IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. IN NO EVENT SHALL ALIAS BE LIABLE FOR ANY DAMAGES // WHATSOEVER, WHETHER DIRECT, INDIRECT, SPECIAL, OR PUNITIVE, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, OR IN EQUITY, // ARISING OUT OF ACCESS TO, USE OF, OR RELIANCE UPON THE DATA. // ========================================================================== //+ /////////////////////////////////////////////////////////////////// // // NOTE: PLEASE READ THE README.TXT FILE FOR INSTRUCTIONS ON // COMPILING AND USAGE REQUIREMENTS. // // DESCRIPTION: NV20-specific (Geforce3) sample shader. // This shader is meant to produce a anisotropic shading effect. // It allows the user to change the parameters of a anisotropic // lookup table. // // This shader builds on the foundation demonstrated in the // hwUnlitShader. // // PS: Thanks go to DAR from nVidia, for his help in making this // shader more robust. ;-) // /////////////////////////////////////////////////////////////////// #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 // 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 "hwAnisotropicShader_NV20.h" #include "ShadingConnection.h" #define TANGENT_INDEX 1 // macro to make normall array indexing a bit clearer MTypeId hwAnisotropicShader_NV20::id( 0x00105444 ); // Lookup table. Fixed at 256x256 resolution. /*static*/ const unsigned int hwAnisotropicShader_NV20::lookup_texture_size(256); void hwAnisotropicShader_NV20::postConstructor( ) { setMPSafe(false); } // Static attribute instances. // MObject hwAnisotropicShader_NV20::color; MObject hwAnisotropicShader_NV20::colorR; MObject hwAnisotropicShader_NV20::colorG; MObject hwAnisotropicShader_NV20::colorB; MObject hwAnisotropicShader_NV20::roughness; MObject hwAnisotropicShader_NV20::kDiffuse; MObject hwAnisotropicShader_NV20::kSpecular; void hwAnisotropicShader_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 model shading effect. // Meant to be used as an anisotropic shader. // // Version for point lights with a decay factor // // CONSTANTS: (c) // c0- c3 4x4 ModelView-Projection composite matrix // c4 (.5, 1, 0, 0) vector for compute u,v // c9 light color // c11 light position (in object space) // c12 camera position in object space (possibly not normalized) // VERTEX REGISTERS (mapped so that standard gl calls work): // v0 - position // v2 - tangent // v3 - primary color // REGISTERS: // // R0 - normalized view (surface-to-camera) direction in object space. // R1 - normalized surface tangent in object space. // R2 - normalized light (surface-to-light) direction. // R3 - temporary computation of u,v values for lookup-texture to be // placed int TEX[0] // R4,R5 - temporaries use to compute light decay // char vertexProgramStringPointDecay[] = "!!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];" // Normalize the T (tangent), in case the modelview matrix is not a simple rotation. "MOV R1, v[2];" "DP3 R1.w, R1, R1;" "RSQ R1.w, R1.w;" "MUL R1.xyz, R1, R1.w;" // Compute the normalized light vector in object space "ADD R2, c[11], -v[0];" "DP3 R2.w, R2, R2;" "RSQ R2.w, R2.w;" "MUL R2.xyz, R2, R2.w;" // The vertex position, normal and light positions are expressed in object space at // this point. We need to find the view direction in object space too. "ADD R0, c[12], -v[0];" // view direction, from surface to camera. "DP3 R0.w, R0, R0;" // normalize the view direction. "RSQ R0.w, R0.w;" "MUL R0.xyz, R0, R0.w;" // Find the texture coordinates to fetch from the toon-param texture: // u = 1/2 (T dot L) + 1/2 // v = 1/2 (T dot V) + 1/2 // // a) Do (T dot L) and (T dot V) and store in register R3 "DP3 R3.x, R1, R2;" "DP3 R3.y, R1, R0;" // b) Scale by 1/2 and add 1/2 and store as texture coordinates // "MAD o[TEX0].x, R3.x, c[4].x, c[4].x;" "MAD o[TEX0].y, R3.y, c[4].x, c[4].x;" // Multiply diffuse lighting into material color. // Take into account decay "MOV R4, c[9];" "MOV R5, c[9];" "MUL R5.xyz, R4, R2.w;" "MUL o[COL0], v[3], R5;" "END"; // Version for point lights // // CONSTANTS: (c) // c0- c3 4x4 ModelView-Projection composite matrix // c4 (.5, 1, 0, 0) vector for compute u,v // c9 light color // c11 light position (in object space) // c12 camera position in object space (possibly not normalized) // VERTEX REGISTERS (mapped so that standard gl calls work): // v0 - position // v2 - tangent // v3 - primary color // REGISTERS: // // R0 - normalized view (surface-to-camera) direction in object space. // R1 - normalized surface tangent in object space. // R2 - normalized light (surface-to-light) direction. // R3 - temporary computation of u,v values for lookup-texture to be // placed int TEX[0] // char vertexProgramStringPoint[] = "!!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];" // Normalize the T (tangent), in case the modelview matrix is not a simple rotation. "MOV R1, v[2];" "DP3 R1.w, R1, R1;" "RSQ R1.w, R1.w;" "MUL R1.xyz, R1, R1.w;" // Compute the normalized light vector in object space "ADD R2, c[11], -v[0];" "DP3 R2.w, R2, R2;" "RSQ R2.w, R2.w;" "MUL R2.xyz, R2, R2.w;" // The vertex position, normal and light positions are expressed in object space at // this point. We need to find the view direction in object space too. "ADD R0, c[12], -v[0];" // view direction, from surface to camera. "DP3 R0.w, R0, R0;" // normalize the view direction. "RSQ R0.w, R0.w;" "MUL R0.xyz, R0, R0.w;" // Find the texture coordinates to fetch from the toon-param texture: // u = 1/2 (T dot L) + 1/2 // v = 1/2 (T dot V) + 1/2 // // a) Do (T dot L) and (T dot V) and store in register R3 "DP3 R3.x, R1, R2;" "DP3 R3.y, R1, R0;" // b) Scale by 1/2 and add 1/2 and store as texture coordinates // "MAD o[TEX0].x, R3.x, c[4].x, c[4].x;" "MAD o[TEX0].y, R3.y, c[4].x, c[4].x;" // Multiply diffuse lighting into material color. "MUL o[COL0], v[3], c[9];" "END"; // Version for directional lights // // CONSTANTS: (c) // c0- c3 4x4 ModelView-Projection composite matrix // c4 (.5, 1, 0, 0) vector for compute u,v // c9 light color // c11 light direction (in object space) // c12 camera position in object space (possibly not normalized) // VERTEX REGISTERS (mapped so that standard gl calls work): // v0 - position // v2 - tangent // v3 - primary color // REGISTERS: // // R0 - normalized view (surface-to-camera) direction in object space. // R1 - normalized surface tangent in object space. // R2 - normalized light (surface-to-light) direction. // R3 - temporary computation of u,v values for lookup-texture to be // placed int TEX[0] // 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];" // Normalize the T (tangent), in case the modelview matrix is not a simple rotation. "MOV R1, v[2];" "DP3 R1.w, R1, R1;" "RSQ R1.w, R1.w;" "MUL R1.xyz, R1, R1.w;" // Normalize the L (light) direction. "MOV R2, c[11];" "DP3 R2.w, R2, R2;" "RSQ R2.w, R2.w;" "MUL R2.xyz, R2, R2.w;" // The vertex position, normal and light positions are expressed in object space at // this point. We need to find the view direction in object space too. "ADD R0, c[12], -v[0];" // view direction, from surface to camera. "DP3 R0.w, R0, R0;" // normalize the view direction. "RSQ R0.w, R0.w;" "MUL R0.xyz, R0, R0.w;" // Find the texture coordinates to fetch from the toon-param texture: // u = 1/2 (N dot L) + 1/2 // v = 1/2 (T dot V) + 1/2 // // a) Do (T dot L) and (T dot V) and store in register R3 "DP3 R3.x, R1, R2;" "DP3 R3.y, R1, R0;" // b) Scale by 1/2 and add 1/2 and store as texture coordinates // "MAD o[TEX0].x, R3.x, c[4].x, c[4].x;" "MAD o[TEX0].y, R3.y, c[4].x, c[4].x;" // Multiply diffuse lighting into material color. "MUL o[COL0], v[3], c[9];" "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 hwAnisotropicShader_NV20::loadVertexProgramGL() { GLenum error = glGetError(); assert(!error); // If the vertex programs haven't been loaded yet, // do it now. (Note that they are shared between all contexts.) if (!fVertexProgramsLoaded) { initVertexProgram(vertexProgramString, &fVertexProgramDirectional); initVertexProgram(vertexProgramStringPointDecay, &fVertexProgramPointDecay); initVertexProgram(vertexProgramStringPoint, &fVertexProgramPointNoDecay); fVertexProgramsLoaded = true; } } // Anisotropic material lookup texture // (This function assumes that lookup_image and lookup_texture have been deallocated.) // void hwAnisotropicShader_NV20::make_lookup_texture() { // Re-calculate the look-up texture, if any of the material paramters // have changed // float t_roughness = 0.025f; float t_kd = 0.8f; float t_ks = 0.2f; //MPlug colorPlug(thisMObject(), color); MPlug roughPlug(thisMObject(), roughness); MPlug diffPlug(thisMObject(), kDiffuse); MPlug specPlug(thisMObject(), kSpecular); roughPlug.getValue(t_roughness); diffPlug.getValue(t_kd); specPlug.getValue(t_ks); if ( t_roughness < 0.0f ) t_roughness = 0.0f; if ( t_roughness > 1.0f ) t_roughness = 1.0f; if ( t_kd < 0.000000000001f ) t_kd = 0.000000000001f; // To prevent underflow in computation if ( t_kd> 1.0f ) t_kd= 1.0f; if ( t_ks < 0.0f ) t_ks= 0.0f; if ( t_ks > 1.0f ) t_ks = 1.0f; // Only recompute the lookup texture if the values have changed since the last bind. boolean dirty = false; if (currentKd != t_kd) { dirty = true; currentKd = t_kd; } if (currentKs != t_ks) { dirty = true; currentKs = t_ks; } if (currentRoughness != t_roughness) { dirty = true; currentRoughness = t_roughness; } if (!dirty) return; 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*3]; if (lookup_texture == NULL) lookup_texture = new tex_object_2D; // Fill it up. #ifdef _DEBUG_DUMP_LOOKUP_TEXTURE // Debug flag to dump out the texture to file. // Define this, if you want to write the lookup table // out as a file texture. static boolean firstTime = true; #else static boolean firstTime = false; #endif if (firstTime) { MImage image; image.create(lookup_texture_size, lookup_texture_size); unsigned char *ip = image.pixels(); float diff, spec, phong; float lt, vt; float h; int i, j; float invRough = 1.0 / t_roughness; for (i=0; i < imgsize; i++) { // Compute sqrt( 1- (L dot T)*(L dot T) ) lt = 1.0 - 2.0 * (float)i/(float)(imgsize-1); diff= sqrt( 1.0-lt*lt ); for(j=0; j < imgsize; j++) { vt= 2.0 * (float)j/(float)(imgsize-1) - 1.0; spec= diff*sqrt( 1-vt*vt ) - lt*vt; // Compute standard phong model.... // phong= Kd*diff + Ks*powf( spec< 0.0 ? 0.0 : spec, 1.0/roughness ); phong= t_ks*powf( spec < 0.0 ? 0.0 : spec, invRough ); float Kddiff = t_kd*diff; h= (Kddiff+phong)*255.0 + 0.5; *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h); h= (Kddiff+phong)*255.0 + 0.5; *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h); h= (Kddiff+phong)*255.0 + 0.5; *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h); *ip++ = 1.0; } } image.writeToFile("c:/aniso.iff"); firstTime = false; } unsigned char *ip = lookup_table; float diff, spec, phong; float lt, vt; float h; int i, j; float invRough = 1.0 / t_roughness; for (i=0; i < imgsize; i++) { // Compute sqrt( 1- (L dot T)*(L dot T) ) lt = 1.0 - 2.0 * (float)i/(float)(imgsize-1); diff= sqrt( 1.0-lt*lt ); for(j=0; j < imgsize; j++) { vt= 2.0 * (float)j/(float)(imgsize-1) - 1.0; spec= diff*sqrt( 1-vt*vt ) - lt*vt; // Compute standard phong model.... // phong= Kd*diff + Ks*powf( spec< 0.0 ? 0.0 : spec, 1.0/roughness ); phong= t_ks*powf( spec < 0.0 ? 0.0 : spec, invRough ); float Kddiff = t_kd*diff; h= (Kddiff+phong)*255.0 + 0.5; *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h); h= (Kddiff+phong)*255.0 + 0.5; *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h); h= (Kddiff+phong)*255.0 + 0.5; *ip++ = (h < 0.0) ? 0 : (h > 255.0 ? 255 : (int)h); } } fLookupTextureReprocessed = true; return; } void hwAnisotropicShader_NV20::bind_lookup_table() { // Update the material table if needed make_lookup_texture(); // Bind the lookup table texture lookup_texture->bind(); if (fLookupTextureReprocessed) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, lookup_texture_size, lookup_texture_size, 0, GL_RGB, 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 hwAnisotropicShader_NV20::init_ext(const char * ext) { if(!glh_init_extension(ext)) { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); } } hwAnisotropicShader_NV20::hwAnisotropicShader_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 isNonAmbientLight = false; // Does not have decay // Set the lookup values to absurd values, so that the // look-up table automatically get recomputed during the first update. currentColor[0] = currentColor[1] = currentColor[2] = -1.0; currentRoughness = -1.0; currentKd = -1.0; currentKs = -1.0; lookup_texture = NULL; lookup_table = NULL; fLookupTextureReprocessed = false; // Initialize callbacks. fBeforeNewCB = 0; fBeforeOpenCB = 0; fBeforeRemoveReferenceCB = 0; fMayaExitingCB = 0; attachSceneCallbacks(); // Initialize the vertex program ids... fVertexProgramsLoaded = false; fVertexProgramDirectional = 0; fVertexProgramPointDecay = 0; fVertexProgramPointNoDecay = 0; // All vertex programs will get allocated and loaded // during the first refresh. } hwAnisotropicShader_NV20::~hwAnisotropicShader_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 hwAnisotropicShader_NV20::releaseEverything() { release_lookup_texture(); // Release all loaded vertex programs. if (fVertexProgramsLoaded) { releaseVertexProgram(&fVertexProgramDirectional); releaseVertexProgram(&fVertexProgramPointDecay); releaseVertexProgram(&fVertexProgramPointNoDecay); fVertexProgramsLoaded = false; } // Release the texture cache through refcounting. m_pTextureCache->release(); } void hwAnisotropicShader_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 hwAnisotropicShader_NV20::releaseCallback(void* clientData) { hwAnisotropicShader_NV20 *pThis = (hwAnisotropicShader_NV20*) clientData; pThis->releaseEverything(); } void hwAnisotropicShader_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, "Alias", "4.5", "Any"); status = plugin.registerNode( "hwAnisotropicShader_NV20", hwAnisotropicShader_NV20::id, hwAnisotropicShader_NV20::creator, hwAnisotropicShader_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( hwAnisotropicShader_NV20::id ); if (!status) { status.perror("deregisterNode"); return status; } return MS::kSuccess; } void * hwAnisotropicShader_NV20::creator() { return new hwAnisotropicShader_NV20(); } // Initialize the plug-in. Called once when the plug-in is loaded. // This mostly involve creating attributes. MStatus hwAnisotropicShader_NV20::initialize() { MFnNumericAttribute nAttr; MStatus status; MFnTypedAttribute sAttr; // For string attributes // Material color 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); // Material parameters. Set defaults to be "shiny" // kDiffuse = nAttr.create( "kDiffuse", "kd", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setMin(0.0f); nAttr.setMax(1.0f); nAttr.setDefault(0.2f); kSpecular = nAttr.create( "kSpecular", "ks", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setMin(0.0f); nAttr.setMax(1.0f); nAttr.setDefault(0.9f); roughness = nAttr.create( "roughness", "rn", MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setMin(0.0f); nAttr.setMax(1.0f); nAttr.setDefault(0.025f); // Add the attributes here addAttribute(color); addAttribute(roughness); addAttribute(kDiffuse); addAttribute(kSpecular); attributeAffects (colorR, outColor); attributeAffects (colorG, outColor); attributeAffects (colorB, outColor); attributeAffects (color, outColor); attributeAffects (roughness, outColor); attributeAffects (kDiffuse, outColor); attributeAffects (kSpecular, outColor); return MS::kSuccess; } // To get 3 float values from the node attribute // MStatus hwAnisotropicShader_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("hwAnisotropicShader_NV20::getFloat3 plug.getValue."); return status; } MFnNumericData data(object, &status); if (!status) { status.perror("hwAnisotropicShader_NV20::getFloat3 construct data."); return status; } status = data.getData(value[0], value[1], value[2]); if (!status) { status.perror("hwAnisotropicShader_NV20::getFloat3 get values."); return status; } return status; } // To get a string value from the node attribute // MStatus hwAnisotropicShader_NV20::getString(MObject attr, MString &str) { MPlug plug(thisMObject(), attr); MStatus status = plug.getValue( str ); return status; } /* virtual */ MStatus hwAnisotropicShader_NV20::bind(const MDrawRequest& request, M3dView& view) { MStatus status; // Get the material color float t_color[3]; getFloat3(color, t_color); view.beginGL(); { glPushAttrib( GL_ALL_ATTRIB_BITS ); glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); // Set the material color glColor4f(t_color[0], t_color[1], t_color[2], 1.0f); // Bind anisotropic texture in texture unit 1 glActiveTextureARB( GL_TEXTURE0_ARB ); glEnable(GL_TEXTURE_2D); bind_lookup_table(); } view.endGL(); return MS::kSuccess; } /* virtual */ MStatus hwAnisotropicShader_NV20::unbind(const MDrawRequest& request, M3dView& view) { view.beginGL(); glActiveTextureARB( GL_TEXTURE0_ARB ); glDisable(GL_TEXTURE_2D); glPopClientAttrib(); glPopAttrib(); view.endGL(); return MS::kSuccess; } /* virtual */ MStatus hwAnisotropicShader_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; // [claforte August 9th 2001] Should this be MS::kFailure? view.beginGL(); // Find out if we have a directional light before // loading the vertex program since we use a different // vertex program depending on whether the light is a directional // one or not // isDirectionalLight = true; // Assume is directional isNonAmbientLight = false; boolean useDefaultLight = false; unsigned int numLights; MDagPath lightPath; view.getLightCount( numLights ); if (numLights) { M3dView::LightingMode mode; view.getLightingMode(mode); if (mode == M3dView::kLightDefault) { useDefaultLight = true; isDirectionalLight = true; } else { view.getLightPath( 0, lightPath ); MObject lightObj = lightPath.node(); isDirectionalLight = lightObj.hasFn( MFn::kDirectionalLight ); isNonAmbientLight = lightObj.hasFn( MFn::kNonAmbientLight ); if (isNonAmbientLight) { MFnNonAmbientLight mNonAmbientLight(lightObj); if (mNonAmbientLight.decayRate() == 0) isNonAmbientLight = false; } } } // Load the appropriate vertex program loadVertexProgramGL(); // Bind and enable the appropriate vertex program, // depending on light type. // if (isDirectionalLight) glBindProgramNV(GL_VERTEX_PROGRAM_NV, fVertexProgramDirectional); else if (isNonAmbientLight) glBindProgramNV(GL_VERTEX_PROGRAM_NV, fVertexProgramPointDecay); else glBindProgramNV(GL_VERTEX_PROGRAM_NV, fVertexProgramPointNoDecay); // Assert if an error occurs after binding the vertex programs. GLenum error = glGetError(); assert(error == GL_NO_ERROR); // Enable the vertex program. glEnable(GL_VERTEX_PROGRAM_NV); // Get object's inverse matrix (ie: from world to object space.) MDagPath objPath = request.multiPath(); MMatrix objMatrix = objPath.inclusiveMatrixInverse(); // Get the light direction in object space. // This code assumes that there is a directional light in the scene, // and that it is the first light in DAG order. // if (numLights) { // Handle default lighting mode if (useDefaultLight ) { glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, 0, 0, 1, 1); // Light dir = 0,0,1 glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, 1.0, 1.0, 1.0, 1); // Light color = 1,1,1,1 } // Handle other lighting modes else { view.getLightPath( 0, lightPath ); MMatrix matrix = lightPath.inclusiveMatrix(); isDirectionalLight = lightPath.node().hasFn( MFn::kDirectionalLight ); // Get rotation of a directional light in object space if (isDirectionalLight) { MVector lightDir(0,0,1); lightDir *= matrix; // Transform into object space lightDir *= objMatrix; // Provide the direction to the vertex program (constant 11) // Need to send it over negated since Maya reverses light direction !!! glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, -lightDir.x, -lightDir.y, -lightDir.z, 1); } // Get the position of a non-directional light in object space else { MPoint lightPos(0,0,0); // origin lightPos *= matrix; // Transform into object space lightPos *= objMatrix; // Provide the position to the vertex program (constant 11) // Need to send it over negated since Maya reverses light positions !!! glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, -lightPos.x, -lightPos.y, -lightPos.z, 1); } MFnLight mLight(lightPath.node()); // Set the light's color. MColor lightColor = mLight.color(); float intensity = mLight.intensity(); lightColor.r *= intensity; lightColor.g *= intensity; lightColor.b *= intensity; // This should NOT be clamped here, so the vertex program // does the correct computation ! Don't uncomment these // lines. //if (lightColor.r > 1.0) lightColor.r = 1.0; //if (lightColor.g > 1.0) lightColor.g = 1.0; //if (lightColor.b > 1.0) lightColor.b = 1.0; glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, lightColor.r, lightColor.g, lightColor.b, 1); } } else { // Set some default values glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, 0, 0, 1, 1); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, 1.0, 1.0, 1.0, 1); } // Find the camera position in geometry's object space float cameraPos[4] = {0.0f, 0.0f, 0.0f, 0.0f}; { MDagPath camDag; view.getCamera(camDag); MPoint cameraInObject(0,0,0); MMatrix cameraToWorldMatrix = camDag.inclusiveMatrix(); cameraInObject *= cameraToWorldMatrix; // to world cameraInObject *= objMatrix; glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 12, cameraInObject.x, cameraInObject.y, cameraInObject.z, 1); } // VERTEX CONSTANTS: // c0- 3 4x4 ModelView-Projection composite matrix // c4 (1/2, 1, 0, 0) vector needed to compute (u,v) // glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 4, 0.5, 1.0, 0.0, 0.0); // VERTEX REGISTERS (Attributes): // 0 - coord // 2 - tangent glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray ); glVertexAttribPointerNV( 2, 3, GL_FLOAT, 0, normalArrays[TANGENT_INDEX] ); 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); view.endGL(); return MS::kSuccess; } /* virtual */ int hwAnisotropicShader_NV20::normalsPerVertex() { // Need the normal and tangents for this computation return 2; } /* virtual */ int hwAnisotropicShader_NV20::texCoordsPerVertex() { return 1; } // Release the lookup texture/image. void hwAnisotropicShader_NV20::release_lookup_texture() { if (lookup_table) { delete lookup_table; lookup_table = NULL; } if (lookup_texture) { delete lookup_texture; lookup_table = NULL; } }