//- // ========================================================================== // 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 allow cartoon-like effects. // It allows the user to specify a base decal texture, // and a lighting look-up texture. // // This shader builds on the foundation demonstrated in the // hwUnlitShader. // // /////////////////////////////////////////////////////////////////// // 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 #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 "hwToonShader_NV20.h" #include "ShadingConnection.h" MTypeId hwToonShader_NV20::id( 0x00105443 ); /*static*/ const unsigned int hwToonShader_NV20::lookup_texture_size(256); void hwToonShader_NV20::postConstructor( ) { setMPSafe(false); } // Static attribute instances. // MObject hwToonShader_NV20::color; MObject hwToonShader_NV20::colorR; MObject hwToonShader_NV20::colorG; MObject hwToonShader_NV20::colorB; MObject hwToonShader_NV20::lightModel; MObject hwToonShader_NV20::lightModelR; MObject hwToonShader_NV20::lightModelG; MObject hwToonShader_NV20::lightModelB; MObject hwToonShader_NV20::camera; MObject hwToonShader_NV20::cameraX; MObject hwToonShader_NV20::cameraY; MObject hwToonShader_NV20::cameraZ; MObject hwToonShader_NV20::uCoord; MObject hwToonShader_NV20::vCoord; MObject hwToonShader_NV20::uvCoord; MObject hwToonShader_NV20::uBias; MObject hwToonShader_NV20::vBias; MObject hwToonShader_NV20::uvFilterSize; MObject hwToonShader_NV20::uvFilterSizeX; MObject hwToonShader_NV20::uvFilterSizeY; MObject hwToonShader_NV20::shininess; MObject hwToonShader_NV20::lightColor; MObject hwToonShader_NV20::lightColorR; MObject hwToonShader_NV20::lightColorG; MObject hwToonShader_NV20::lightColorB; void hwToonShader_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 textured-light-model shading effect. // Meant to be used as a cartoon shader. // // 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, in object space) // 12 camera position in object space (possibly not normalized) // VERTEX REGISTERS (mapped so that standard gl calls work): // 0 - coord // 2 - normal // 3 - primary color // 8 - texcoord0 (lighting look-up table) // 9 - texcoord1 (decal, optional) // REGISTERS: // // R0 - normalized view (surface-to-camera) direction in object space. // R1 - normalized surface normal in object space. // R2 - normalized light (surface-to-light) direction. // // 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 N (normal), 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 = N dot L. // v = N dot V. "DP3 o[TEX0].x, R1, R2;" "DP3 o[TEX0].y, R1, R0;" // Move the texture coordinates from tex 1 if appropriate. "MOV o[TEX1], v[9];" #ifndef DEBUGGING_VERTEX_PROGRAM // Put diffuse lighting into color. "MUL o[COL0], v[3], c[9];" #else // VISUALIZE VECTOR "MOV o[COL0], R2;" // normalized light #endif "END"; 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 N (normal), 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 normalize the L (light) direction. "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 = N dot L. // v = N dot V. "DP3 o[TEX0].x, R1, R2;" "DP3 o[TEX0].y, R1, R0;" // Move the texture coordinates from tex 1 if appropriate. "MOV o[TEX1], v[9];" #ifndef DEBUGGING_VERTEX_PROGRAM // Put diffuse lighting into color. "MUL o[COL0], v[3], c[9];" // "MOV o[COL0], c[9];" #else // VISUALIZE VECTOR "MOV o[COL0], R2;" // normalized light #endif "END"; 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 N (normal), 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 normalize the L (light) direction. "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 = N dot L. // v = N dot V. "DP3 o[TEX0].x, R1, R2;" "DP3 o[TEX0].y, R1, R0;" // Move the texture coordinates from tex 1 if appropriate. "MOV o[TEX1], v[9];" #ifndef DEBUGGING_VERTEX_PROGRAM // 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;" #else // VISUALIZE VECTOR "MOV o[COL0], R2;" // normalized light #endif "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 hwToonShader_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; } // 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... } void hwToonShader_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 hwToonShader_NV20::init_ext(const char * ext) { if(!glh_init_extension(ext)) { cerr << "Failed to initialize " << ext << "!" << endl; exit(0); } } hwToonShader_NV20::hwToonShader_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(); // 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. } hwToonShader_NV20::~hwToonShader_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 hwToonShader_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 hwToonShader_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 hwToonShader_NV20::releaseCallback(void* clientData) { hwToonShader_NV20 *pThis = (hwToonShader_NV20*) clientData; pThis->releaseEverything(); } void hwToonShader_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( "hwToonShader_NV20", hwToonShader_NV20::id, hwToonShader_NV20::creator, hwToonShader_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( hwToonShader_NV20::id ); if (!status) { status.perror("deregisterNode"); return status; } return MS::kSuccess; } void * hwToonShader_NV20::creator() { return new hwToonShader_NV20(); } // Initialize the plug-in. Called once when the plug-in is loaded. // This mostly involve creating attributes. MStatus hwToonShader_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); lightModelR = nAttr.create( "lightModelR", "c2r",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); lightModelG = nAttr.create( "lightModelG", "c2g",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); lightModelB = nAttr.create( "lightModelB", "c2b",MFnNumericData::kFloat); nAttr.setStorable(true); nAttr.setKeyable(true); nAttr.setDefault(1.0f); lightModel = nAttr.create( "lightModel", "c2", lightModelR, lightModelG, lightModelB); 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); 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); // Add the attributes here addAttribute(color); addAttribute(lightModel); addAttribute(uvCoord); addAttribute(uBias); addAttribute(vBias); addAttribute(uvFilterSize); addAttribute(camera); addAttribute(shininess); addAttribute(lightColor); attributeAffects (colorR, outColor); attributeAffects (colorG, outColor); attributeAffects (colorB, outColor); attributeAffects (color, outColor); attributeAffects (lightModelR, outColor); attributeAffects (lightModelG, outColor); attributeAffects (lightModelB, outColor); attributeAffects (lightModel, outColor); attributeAffects (uCoord, outColor); attributeAffects (vCoord, outColor); attributeAffects (uvCoord, outColor); attributeAffects (uBias, outColor); attributeAffects (vBias, 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 texture. // See "Writing a shading node plug-in" in the documentation // for more information. // MStatus hwToonShader_NV20::compute( const MPlug& plug, MDataBlock& block ) { // Get color and lightModel from the input block. // Get UV coordinates from the input block. bool k = false; k |= (plug==outColor); k |= (plug==outColorR); k |= (plug==outColorG); k |= (plug==outColorB); if( !k ) return MS::kUnknownParameter; MFloatVector resultColor(0.0,0.0,0.0); float& u = block.inputValue( uCoord ).asFloat(); float& v = block.inputValue( vCoord ).asFloat(); float& bu = block.inputValue( uBias ).asFloat(); float& bv = block.inputValue( vBias ).asFloat(); if ( bu <= 0.0 ) bu = 0.001f; if ( bv <= 0.0 ) bv = 0.001f; MFloatVector& surfaceColor = block.inputValue( color ).asFloatVector(); MFloatVector& surfaceColor2 = block.inputValue( lightModel ).asFloatVector(); // normalize the UV coords u = u - int(u); v = v - int(v); resultColor = surfaceColor; // set ouput color attribute MDataHandle outColorHandle = block.outputValue( outColor ); MFloatVector& outColor = outColorHandle.asFloatVector(); outColor = resultColor; outColorHandle.setClean(); return MS::kSuccess; } // To get 3 float values from the node attribute // MStatus hwToonShader_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("hwToonShader_NV20::getFloat3 plug.getValue."); return status; } MFnNumericData data(object, &status); if (!status) { status.perror("hwToonShader_NV20::getFloat3 construct data."); return status; } status = data.getData(value[0], value[1], value[2]); if (!status) { status.perror("hwToonShader_NV20::getFloat3 get values."); return status; } return status; } // To get a string value from the node attribute // MStatus hwToonShader_NV20::getString(MObject attr, MString &str) { MPlug plug(thisMObject(), attr); MStatus status = plug.getValue( str ); return status; } /* virtual */ MStatus hwToonShader_NV20::bind(const MDrawRequest& request, M3dView& view) { MStatus status; // Get the decal and lightModel map file names // MString decalName = ""; MString lightModelName = ""; ShadingConnection colorConnection(thisMObject(), request.multiPath().partialPathName(), "color"); ShadingConnection lightModelConnection (thisMObject(), request.multiPath().partialPathName(), "lightModel"); // If the lightModel attribute is ultimately connected to a file texture, find its filename. // otherwise use the default lightModel texture. if (lightModelConnection.type() == ShadingConnection::TEXTURE && lightModelConnection.texture().hasFn(MFn::kFileTexture)) { // Get the filename of the texture. MFnDependencyNode textureNode(lightModelConnection.texture()); MPlug filenamePlug( lightModelConnection.texture(), textureNode.attribute(MString("fileTextureName")) ); filenamePlug.getValue(lightModelName); } // Fail safe quit // if (lightModelName.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); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); view.endGL(); return MS::kSuccess; } view.beginGL(); glPushAttrib( GL_ALL_ATTRIB_BITS ); glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); /* Starts Here... */ // stage 0 -- lighting model texture glActiveTextureARB( GL_TEXTURE0_ARB ); glEnable(GL_TEXTURE_2D); m_pTextureCache->bind(lightModelConnection.texture(), MTexture::RGBA, false); // With light color and intensity // if (colorConnection.type() != ShadingConnection::TEXTURE) { MColor color = colorConnection.constantColor(); glColor4f(color.r, color.g, color.b, color.a); } // The register combiner will do the multiplication between // the fetched light model result and the base (vertex or decal-textured) color // glEnable(GL_REGISTER_COMBINERS_NV); glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1); 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 vertex color // 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_TEXTURE0_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_TEXTURE0_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); #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. May want to add fog later. // 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 hwToonShader_NV20::unbind(const MDrawRequest& request, M3dView& view) { view.beginGL(); glDisable(GL_REGISTER_COMBINERS_NV); glActiveTextureARB( GL_TEXTURE0_ARB ); glDisable(GL_TEXTURE_2D); glPopClientAttrib(); glPopAttrib(); view.endGL(); return MS::kSuccess; } /* virtual */ MStatus hwToonShader_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(); // 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; } } } 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 ) { // Provide the direction to the vertex program (constant 11) glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 11, 0, 0, 1, 1); glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 9, 1,1,1,1); } 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) { // Get rotation in world space MVector lightDir(0,0,1); // origin lightDir *= matrix; // Transform into object space lightDir *= objMatrix; // Provide the direction to the vertex program (constant 11) 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) 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; 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 REGISTERS (Attributes): // 0 - coord // 2 - normal glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray ); glVertexAttribPointerNV( 2, 3, GL_FLOAT, 0, normalArrays[0] ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV ); glEnableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV ); glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV ); glDisableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV ); glDisable(GL_VERTEX_PROGRAM_NV); glClientActiveTextureARB(GL_TEXTURE0_ARB); view.endGL(); return MS::kSuccess; } /* virtual */ int hwToonShader_NV20::normalsPerVertex() { return 1; } /* virtual */ int hwToonShader_NV20::texCoordsPerVertex() { return 1; } // Release the lookup texture/image. void hwToonShader_NV20::release_lookup_texture() { if (lookup_table) { delete lookup_table; lookup_table = NULL; } if (lookup_texture) { delete lookup_texture; lookup_table = NULL; } }