hwToonShader_NV20/hwToonShader_NV20.cpp

hwToonShader_NV20/hwToonShader_NV20.cpp
//-
// ==========================================================================
// 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 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 <maya/MIOStream.h>
#include <math.h>
#include <maya/MString.h>
#include <maya/MPlug.h>
#include <maya/MDagPath.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MArrayDataHandle.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnLightDataAttribute.h>
#include <maya/MFloatVector.h>
#include <maya/MFnLight.h>
#include <maya/MFnStringData.h>
#include <maya/MFnPlugin.h>
#include <maya/MGlobal.h>
#include <maya/MSceneMessage.h>
#include <maya/MPoint.h>
#include <maya/MMatrix.h>
#include <maya/MVector.h>
#include <maya/MEulerRotation.h>
#include <maya/MFnNonAmbientLight.h>
// 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 <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include "glh_extensions.h"
#undef GL_NV_vertex_array_range
#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();
if(!MTextureCache::getReferenceCount())
{
m_pTextureCache = 0;
}
}
void hwToonShader_NV20::attachSceneCallbacks()
{
fBeforeNewCB = MSceneMessage::addCallback(MSceneMessage::kBeforeNew, releaseCallback, this);
fBeforeOpenCB = MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, releaseCallback, this);
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, PLUGIN_COMPANY, "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()
{
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])
{
// 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);
if(m_pTextureCache)
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)
{
view.getLightingMode(mode);
{
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;
}
}