hwRefractReflectShader_NV20/hwRefractReflectShader_NV20.cpp

hwRefractReflectShader_NV20/hwRefractReflectShader_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 produces reflection and refraction effects.
//
// This shader builds on the foundation demonstrated in the hwUnlitShader.
//
//
#ifdef WIN32
#pragma warning( disable : 4786 ) // Disable stupid STL warnings.
#endif
#include <maya/MIOStream.h>
#include <math.h>
#include <maya/MString.h>
#include <maya/MPlug.h>
#include <maya/MDagPath.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFloatVector.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/MQuaternion.h>
#include <maya/MEulerRotation.h>
#include <GL/gl.h>
#include <GL/glext.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 "glh_extensions.h"
#undef GL_NV_vertex_array_range
#include "glh_obs.h"
using namespace glh;
#include "hwRefractReflectShader_NV20.h"
#include "ShadingConnection.h"
MTypeId hwRefractReflectShader_NV20::id( 0x00105445 );
void hwRefractReflectShader_NV20::postConstructor( )
{
setMPSafe(false);
}
// Static attribute instances.
//
MObject hwRefractReflectShader_NV20::color;
MObject hwRefractReflectShader_NV20::colorR;
MObject hwRefractReflectShader_NV20::colorG;
MObject hwRefractReflectShader_NV20::colorB;
MObject hwRefractReflectShader_NV20::refractionIndex;
MObject hwRefractReflectShader_NV20::reflectivity;
// The Vertex Program for the reflection&refraction shading effect.
//
// CONSTANTS:
// 0- 3 4x4 ModelView-Projection composite matrix
// 4- 7 4x4 ModelView inverseTranspose matrix
// 8- 11 4x4 ModelView matrix
// 12-15 4x4 Texture matrix
//
// 58 refraction index [rIdx, rIdx*rIdx, 0.0, 0.0]
// 59 camera position in eye space [0.0, 0.0, 0.0, 1.0]
// (camera could be offseted if necessary, should work but untested)
//
// 64 misc constants [0.0, 1.0, 2.0, 3.0]
// VERTEX REGISTERS (mapped so that standard gl calls work):
// 0 - coord
// 2 - normal
//
// RESULTS:
// texcoord0 (Refraction coords in eye-space)
// texcoord1 (Reflection coords in eye-space)
//
char vertexProgramString[] =
"!!VP1.0 # Refraction and Reflection\n"
// Multiply the vertex coords by the GL_MODELVIEW_PROJECTION
// composite matrix, to get clip space coordinates.
//
"DP4 o[HPOS].x, c[0], v[OPOS];"
"DP4 o[HPOS].y, c[1], v[OPOS];"
"DP4 o[HPOS].z, c[2], v[OPOS];"
"DP4 o[HPOS].w, c[3], v[OPOS];"
// =====================================================
// The rest of the computations are done in the eyeSpace
// =====================================================
// Transform, vertex position to eye space, with the MODELVIEW matrix
//
"DP4 R9.x, c[8], v[OPOS];"
"DP4 R9.y, c[9], v[OPOS];"
"DP4 R9.z, c[10], v[OPOS];"
"DP4 R9.w, c[11], v[OPOS];" // R9 = eye space Position of this vertex
// Using the inverseTranspose of the MODELVIEW matrix,
// transform the vertex normal to eye space and normalize it
//
"DP3 R0.x, c[4], v[NRML];"
"DP3 R0.y, c[5], v[NRML];"
"DP3 R0.z, c[6], v[NRML];"
"DP3 R11.w, R0, R0;"
"RSQ R11.w, R11.w;"
"MUL R11, R0, R11.w;" // R11 = normalized normal vector in the eyeSpace
// Compute the 'vertex->eye' vector and normalize it
//
"ADD R0, -R9, c[59];" // c[59] = eye position in eye space (0,0,0,1)
"DP3 R8.w, R0, R0;"
"RSQ R8.w, R8.w;"
"MUL R8, R0, R8.w;" // R8 = the eye/incident vector (I)
// Calculate REFRACTION: Renderman style
// float eta; // the refraction index value
//
// float IdotN = I.N;
// float k = 1 - eta*eta*(1 - IdotN*IdotN);
// return k < 0 ? (0,0,0) : eta*I - (eta*IdotN + sqrt(k))*N;
//
"DP3 R0.x, R11, -R8;" // R0 = N.I
//
"MAD R1.x, -R0.x, R0.x, c[64].y;" // R1.x = (1 - IdotN * IdotN) == SQR( sin(Ti) )
"MUL R1.x, R1.x, c[58].y;" // R1.x = R1.x * eta*eta
"ADD R1.x, c[64].y, -R1.x;" // R1.x = (1 - (R1.x * eta * eta) ) == 1 - SQR( eta * sin(Ti) )
//
"RSQ R2.x, R1.x;" // R2.x = 1 / SQRT(R1.x)
"RCP R2.x, R2.x;" // R2.x = cos(Tr) = SQRT(R1.x) <=== OK
"MAD R2.x, c[58].x, R0.x, R2.x;" // R2.x = eta*(IdotN) + cos(Tr)
"MUL R2, R11, R2.x;" // R2 = N * R2.x
"MAD R2, c[58].x, -R8, R2;" // R2 is the refracted ray direction
//
// Transform refracted ray by cubemap transform (texture matrix)
//
"DP3 o[TEX0].x, c[12], R2;"
"DP3 o[TEX0].y, c[13], R2;"
"DP3 o[TEX0].z, c[14], R2;"
// Calculate REFLECTION in cubeMap space
//
"MUL R0, R11, c[64].z;" // R0 = 2*N
"DP3 R3.w, R11, R8;" // R3.w = N.dot.I
"MAD R3, R3.w, R0, -R8;" // R3 = 2*N*(N.dot.I) - I
//
// Transform reflected ray by cubemap transform (texture matrix)
//
"DP3 o[TEX1].x, c[12], R3;"
"DP3 o[TEX1].y, c[13], R3;"
"DP3 o[TEX1].z, c[14], R3;"
"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 hwRefractReflectShader_NV20::loadVertexProgramGL( M3dView& view )
{
view.beginGL();
{
// Don't load/initialize the vertex program more than once.
//
if (vertex_program_id == 0)
initVertexProgram(vertexProgramString, &vertex_program_id);
// Set up the constant values.
//
// CONSTANTS:
// 0- 3 4x4 ModelView-Projection composite matrix
// 4- 7 4x4 ModelView inverseTranspose matrix
// 8- 11 4x4 ModelView matrix
// 12-15 4x4 Texture matrix
//
glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV);
glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4, GL_MODELVIEW, GL_INVERSE_TRANSPOSE_NV);
glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 8, GL_MODELVIEW, GL_IDENTITY_NV);
glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 12, GL_TEXTURE, GL_IDENTITY_NV);
float rIdx = fRefractionIndex;
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 58, rIdx, rIdx*rIdx, 0.0, 0.0); // refraction index
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 59, 0.0, 0.0, 0.0, 1.0); // eye position
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 64, 0.0, 1.0, 2.0, 3.0); // misc constants
}
view.endGL();
}
// Load the file textures for the cube maps.
//
MStatus hwRefractReflectShader_NV20::loadTextures(const MDrawRequest& request, M3dView& view)
{
// Get the cube map file names
//
MStringArray decalNames;
MString decalName;
// Find the cubemap textures by tracing through the connection from the color atttribute
//
ShadingConnection colorConnection(thisMObject(), request.multiPath().partialPathName(), "color");
// If the color attribute is ultimately connected to a environment,
// find its filenames, otherwise use the default color texture.
//
bool gotAllEnvironmentMaps = TRUE;
if (colorConnection.type() == ShadingConnection::TEXTURE &&
colorConnection.texture().hasFn(MFn::kEnvCube))
{
// Get the filenames of the texture.
MFnDependencyNode textureNode(colorConnection.texture());
MString attributeName;
MString envNames[6] = { "top", "bottom", "left", "right", "front", "back" };
// Scan for connected file textures to the environment map node
//
for (int i=0; i<6; i++)
{
ShadingConnection conn(colorConnection.texture(), request.multiPath().partialPathName(),
envNames[i]);
if (conn.type() == ShadingConnection::TEXTURE &&
conn.texture().hasFn(MFn::kFileTexture))
{
MFnDependencyNode envNode(conn.texture());
MPlug filenamePlug( conn.texture(), envNode.attribute(MString("fileTextureName")) );
filenamePlug.getValue(decalName);
if (decalName.length() == 0) decalName = "internalDefaultTexture";
// Append next environment map name
decalNames.append( decalName );
}
// If any of the environment maps are not mapped put in a fake texture
else
{
decalName = "internalDefaultTexture";
decalNames.append( decalName );
}
}
}
else
{
// Put in a fake texture for each side
decalName = "internalDefaultTexture";
for (int i=0; i<6; i++)
{
decalNames.append( decalName );
}
}
// Reload cube maps if the name of the textures
// for any of the cube maps changes
//
bool reload = FALSE;
for (int i=0; i<6; i++)
{
if (currentTextureNames[i] != decalNames[i])
{
reload = TRUE;
break;
}
}
view.beginGL();
{
if ( reload )
{
MString ypTexName(decalNames[0]); // y+ == top
MString ynTexName(decalNames[1]); // y- == bottom
MString xpTexName(decalNames[2]); // x+ == left
MString xnTexName(decalNames[3]); // x- == right
MString zpTexName(decalNames[4]); // z+ == front
MString znTexName(decalNames[5]); // z- == back
MStatus stat;
if (! (stat = theImage_XP.readFromFile(xpTexName)) ) return MS::kFailure;
if (! (stat = theImage_XN.readFromFile(xnTexName)) ) return MS::kFailure;
if (! (stat = theImage_YP.readFromFile(ypTexName)) ) return MS::kFailure;
if (! (stat = theImage_YN.readFromFile(ynTexName)) ) return MS::kFailure;
if (! (stat = theImage_ZP.readFromFile(zpTexName)) ) return MS::kFailure;
if (! (stat = theImage_ZN.readFromFile(znTexName)) ) return MS::kFailure;
// Only create texture names the first time
if (fTextureName == -1) glGenTextures(1, &fTextureName);
glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName );
glEnable( GL_TEXTURE_CUBE_MAP_ARB );
// The cubeMap textures have to have the same size
//
unsigned int width, height;
stat = theImage_XP.getSize( width, height );
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XP.pixels() );
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XN.pixels() );
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YP.pixels() );
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YN.pixels() );
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZP.pixels() );
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZN.pixels() );
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
for (i=0; i<6; i++) currentTextureNames[i] = decalNames[i];
}
// stage 0 -- cubeMap texture for the refraction
//
glActiveTextureARB( GL_TEXTURE0_ARB );
glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName );
glEnable( GL_TEXTURE_CUBE_MAP_ARB );
// stage 1 -- cubeMap texture for the reflection
//
glActiveTextureARB( GL_TEXTURE1_ARB );
glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, fTextureName );
glEnable( GL_TEXTURE_CUBE_MAP_ARB );
}
view.endGL();
return MS::kSuccess;
}
// Initialize the register combiners setting
//
void hwRefractReflectShader_NV20::initCombiners(const MDrawRequest& request, M3dView& view)
{
view.beginGL();
{
// Use only the 1st stage of the register combiner stages
//
glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
{
float refractivity[4], reflectivity[4];
refractivity[0] = refractivity[1] = refractivity[2] = refractivity[3] = 1.0f - fReflectivity;
reflectivity[0] = reflectivity[1] = reflectivity[2] = reflectivity[3] = fReflectivity;
glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, refractivity);
glCombinerParameterfvNV(GL_CONSTANT_COLOR1_NV, reflectivity);
}
// combiner 0
// a*b+c*d
// a is from the refractive color
// c is from the refrlective texture
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
// output:
// (stage, portion, abOutput, cdOutput, sumOutput, scale, bias, abDotProduct, cdDotProduct, muxSum)
glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
// final combiner
// output: Frgb = A*B + (1-A)*C + D
// (variable, input, mapping, componentUsage);
// Just pass through the D variable
//
glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
}
view.endGL();
}
// Load the textures, update the necessary variable values, initialize register combiners,
// save and load the matrices with the proper values
//
MStatus hwRefractReflectShader_NV20::preDraw(const MDrawRequest& request, M3dView& view)
{
MStatus stat = loadTextures( request, view);
if( MS::kSuccess != stat ) return stat;
// get the reflectivity value
//
MPlug tPlug(thisMObject(), reflectivity);
if( tPlug.getValue( fReflectivity ) )
{
if( fReflectivity < 0.01f ) fReflectivity = 0.01f;
if( fReflectivity > 1.0f ) fReflectivity = 1.0f;
}
else fReflectivity = 0.5f;
// get the refraction index value
//
MPlug rPlug(thisMObject(), refractionIndex);
if( rPlug.getValue( fRefractionIndex ) )
{
if ( fRefractionIndex < 1.0f ) fRefractionIndex = 1.0f;
if ( fRefractionIndex > 2.0f ) fRefractionIndex = 2.0f;
}
else fRefractionIndex = 1.0f;
initCombiners( request, view );
// Compute the camera rotation angle and axis
//
MDagPath cameraPath;
MStatus status = view.getCamera( cameraPath );
MMatrix mmatrix = cameraPath.inclusiveMatrix( &status );
MTransformationMatrix tmatrix( mmatrix );
MQuaternion camRotation = tmatrix.rotation();
MVector camAxis;
double camTheta;
camRotation.getAxisAngle( camAxis, camTheta );
// Convert to degrees from radians
camTheta *= 57.295779513082320876798154814105; // == (180 / M_PI)
view.beginGL();
glMatrixMode( GL_TEXTURE );
glPushMatrix();
glLoadIdentity();
glScalef(1.0, -1.0, 1.0);
glRotated( camTheta, camAxis[0], camAxis[1], camAxis[2]);
glMatrixMode( GL_MODELVIEW );
view.endGL();
return stat;
}
/* virtual */
MStatus hwRefractReflectShader_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;
// Save the current states of the openGL attributes
//
view.beginGL();
glPushAttrib( GL_ALL_ATTRIB_BITS );
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
view.endGL();
MStatus preDrawStatus = preDraw( request, view );
if( MS::kSuccess == preDrawStatus )
{
loadVertexProgramGL( view );
view.beginGL();
{
glEnable(GL_REGISTER_COMBINERS_NV);
//
// Load, bind and enable the vertex program
//
glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id);
glEnable(GL_VERTEX_PROGRAM_NV);
{
// 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);
//
glDisable(GL_REGISTER_COMBINERS_NV);
}
view.endGL();
postDraw( request, view );
}
// Restore the openGL attributes
//
view.beginGL();
glPopClientAttrib();
glPopAttrib();
view.endGL();
return preDrawStatus;
}
// Retore the openGL matrices and the openGL texture objects states
//
MStatus hwRefractReflectShader_NV20::postDraw(
const MDrawRequest& request,
M3dView& view )
{
view.beginGL();
{
glMatrixMode( GL_TEXTURE );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glActiveTextureARB( GL_TEXTURE1_ARB );
glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 );
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glActiveTextureARB( GL_TEXTURE0_ARB );
glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 );
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
}
view.endGL();
return MS::kSuccess;
}
/* virtual */
int hwRefractReflectShader_NV20::normalsPerVertex()
{
return 1;
}
/* virtual */
int hwRefractReflectShader_NV20::texCoordsPerVertex()
{
return 1;
}
// Initialize the necessary OpenGL extensions
//
void hwRefractReflectShader_NV20::init_ext(const char * ext)
{
if(!glh_init_extension(ext))
{ cerr << "Failed to initialize " << ext << "!" << endl; exit(0); }
}
// The constructor
//
hwRefractReflectShader_NV20::hwRefractReflectShader_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");
// Initialize the cubeMap texture names
//
fTextureName = -1;
currentTextureNames[0] = "";
currentTextureNames[1] = "";
currentTextureNames[2] = "";
currentTextureNames[3] = "";
currentTextureNames[4] = "";
currentTextureNames[5] = "";
// Initialize callbacks.
fBeforeNewCB = 0;
fBeforeOpenCB = 0;
fBeforeRemoveReferenceCB = 0;
fMayaExitingCB = 0;
attachSceneCallbacks();
vertex_program_id = 0; // handle for the Vertex Program
}
hwRefractReflectShader_NV20::~hwRefractReflectShader_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 hwRefractReflectShader_NV20::releaseEverything()
{
if (fTextureName != -1) glDeleteTextures(1, &fTextureName);
releaseVertexProgram(&vertex_program_id);
// Release the texture cache through refcounting.
m_pTextureCache->release();
if(!MTextureCache::getReferenceCount())
{
m_pTextureCache = 0;
}
}
void hwRefractReflectShader_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 hwRefractReflectShader_NV20::releaseCallback(void* clientData)
{
hwRefractReflectShader_NV20 *pThis = (hwRefractReflectShader_NV20*) clientData;
pThis->releaseEverything();
}
void hwRefractReflectShader_NV20::detachSceneCallbacks()
{
if (fBeforeNewCB) MMessage::removeCallback(fBeforeNewCB);
if (fBeforeOpenCB) MMessage::removeCallback(fBeforeOpenCB);
if (fBeforeRemoveReferenceCB) MMessage::removeCallback(fBeforeRemoveReferenceCB);
if (fMayaExitingCB) MMessage::removeCallback(fMayaExitingCB);
fBeforeNewCB = 0;
fBeforeOpenCB = 0;
fBeforeRemoveReferenceCB = 0;
fMayaExitingCB = 0;
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
const MString UserClassify( "shader/surface/utility" );
MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.0", "Any");
status = plugin.registerNode( "hwRefractReflectShader_NV20", hwRefractReflectShader_NV20::id,
hwRefractReflectShader_NV20::creator, hwRefractReflectShader_NV20::initialize,
MPxNode::kHwShaderNode, &UserClassify );
if (!status) {
status.perror("registerNode");
return status;
}
return MS::kSuccess;
}
MStatus uninitializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj );
status = plugin.deregisterNode( hwRefractReflectShader_NV20::id );
if (!status) {
status.perror("deregisterNode");
return status;
}
return MS::kSuccess;
}
void * hwRefractReflectShader_NV20::creator()
{
return new hwRefractReflectShader_NV20();
}
// Initialize the plug-in. Called once when the plug-in is loaded.
// This mostly involve creating attributes.
MStatus hwRefractReflectShader_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);
refractionIndex = nAttr.create( "refractionIndex", "ri", MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setMin(1.0f);
nAttr.setMax(2.0f);
nAttr.setDefault(1.1f);
reflectivity = nAttr.create( "reflectivity", "rfl", MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setMin(0.0f);
nAttr.setMax(1.0f);
nAttr.setDefault(0.5f);
// Add the attributes here
addAttribute(color);
addAttribute(refractionIndex);
addAttribute(reflectivity);
attributeAffects (colorR, outColor);
attributeAffects (colorG, outColor);
attributeAffects (colorB, outColor);
attributeAffects (color, outColor);
attributeAffects (refractionIndex, outColor);
attributeAffects (reflectivity, 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.
//
// CAVEAT: This part of the HW shader plug-in is meant to allow
// seamless transition from HW to SW rendering.
// Unfortunately, as of 4.0.1 it's somewhat flaky.
// Meanwhile, it is recommended to build two shading networks
// in parallel (one for SW, one for HW) and use MEL scripts
// to switch from one to the other.
//
MStatus hwRefractReflectShader_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);
// set ouput color attribute
MDataHandle outColorHandle = block.outputValue( outColor );
MFloatVector& outColor = outColorHandle.asFloatVector();
outColor = resultColor;
outColorHandle.setClean();
return MS::kSuccess;
}