hwReflectBumpShader_NV20/hwReflectBumpShader_NV20.cpp

hwReflectBumpShader_NV20/hwReflectBumpShader_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 can simultaneously display a
// bumpy, reflective surface. The bump
// is controlled through a user-specified
// 2D texture, while the reflection map
// is a cube map.
//
// This shader builds on the foundation demonstrated in
// hwUnlitShader.
//
// Additionally, this sample demonstrates how to:
// - Use vendor-specific extensions, namely vertex programs,
// texture shaders and register combiners, to achieve
// effects that are impossible in standard OpenGL.
// - Convert height field bump format (used by Maya) into
// a normal map format, for real-time rendering.
//
// Many parameters are easily customizable:
// - The MNormalMapConverter::convertToNormalMap_InPlace()
// bumpScale parameter is currently constant. You can change
// it to a different value to increase or decrease the
// bumpiness.
//
#ifdef WIN32
#pragma warning( disable : 4786 ) // Disable STL warnings.
#endif
#include <maya/MIOStream.h>
#include <math.h>
#include <maya/MString.h>
#include <maya/MStringArray.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MArrayDataHandle.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/MPoint.h>
#include <maya/MMatrix.h>
#include <maya/MVector.h>
#include <maya/MEulerRotation.h>
#include <maya/MQuaternion.h>
#include <maya/MDagPath.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MSceneMessage.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 "hwReflectBumpShader_NV20.h"
#include "ShadingConnection.h"
MTypeId hwReflectBumpShader_NV20::id( 0x00105442 );
void hwReflectBumpShader_NV20::postConstructor( )
{
setMPSafe(false);
}
//
// DESCRIPTION:
MObject hwReflectBumpShader_NV20::color;
MObject hwReflectBumpShader_NV20::colorR;
MObject hwReflectBumpShader_NV20::colorG;
MObject hwReflectBumpShader_NV20::colorB;
MObject hwReflectBumpShader_NV20::bump;
MObject hwReflectBumpShader_NV20::bumpR;
MObject hwReflectBumpShader_NV20::bumpG;
MObject hwReflectBumpShader_NV20::bumpB;
MObject hwReflectBumpShader_NV20::uCoord;
MObject hwReflectBumpShader_NV20::vCoord;
MObject hwReflectBumpShader_NV20::uvCoord;
MObject hwReflectBumpShader_NV20::uBias;
MObject hwReflectBumpShader_NV20::vBias;
MObject hwReflectBumpShader_NV20::uvFilterSize;
MObject hwReflectBumpShader_NV20::uvFilterSizeX;
MObject hwReflectBumpShader_NV20::uvFilterSizeY;
char gszErrMsg[100]; // Global error message text
void hwReflectBumpShader_NV20::printGlError( const char *call )
{
GLenum error;
while( (error = glGetError()) != GL_NO_ERROR ) {
cerr << call << ":" << error << " is " << (const char *)gluErrorString( error ) << "\n";
}
}
// Verify that the configuration of the texture shaders are consistent
//
void hwReflectBumpShader_NV20::verify_shader_config(M3dView& view)
{
int consistent;
view.beginGL();
glActiveTextureARB( GL_TEXTURE0_ARB );
glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
if(consistent == GL_FALSE)
cerr << "Shader stage 0 is inconsistent!" << endl;
glActiveTextureARB( GL_TEXTURE1_ARB );
glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
if(consistent == GL_FALSE)
cerr << "Shader stage 1 is inconsistent!" << endl;
glActiveTextureARB( GL_TEXTURE2_ARB );
glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
if(consistent == GL_FALSE)
cerr << "Shader stage 2 is inconsistent!" << endl;
glActiveTextureARB( GL_TEXTURE3_ARB );
glGetTexEnviv(GL_TEXTURE_SHADER_NV, GL_SHADER_CONSISTENT_NV, & consistent);
if(consistent == GL_FALSE)
cerr << "Shader stage 3 is inconsistent!" << endl;
glActiveTextureARB( GL_TEXTURE0_ARB );
view.endGL();
}
// The very simple VertexProgram for the Reflective Bump effect. This one is faster
// (it doesn't require the tangent space calculation) but is world-aligned.
// Therefore it could be useful for some effects (ex: ground or wall), but for
// a character it would be unnacceptable.
//
// CONSTANTS:
// 0- 3 4x4 ModelView-Projection composite matrix
// 4- 7 4x4 ModelView matrix
// 24-27 4x4 view transpose
//
// VERTEX REGISTERS:
// 0 - coord
// 1 - normal
// 2 - texcoord0
//
// REGISTERS:
// 4 = skinned (eye space) coord
// 5 = skinned (eye space) tangent
// 6 = skinned (eye space) binormal
// 7 = skinned (eye space) normal
//
char superEasyVertexProgramString[] =
"!!VP1.0\n"
// final projection transformation
// transform the skinned coords by the projection matrix
"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];"
// transform the coords to the eye-space
"DP4 R4.x, c[4], v[0];"
"DP4 R4.y, c[5], v[0];"
"DP4 R4.z, c[6], v[0];"
"DP4 R4.w, c[7], v[0];"
// transform the normals to eye-space
"DP3 R7.x, c[4], v[1];"
"DP3 R7.y, c[5], v[1];"
"DP3 R7.z, c[6], v[1];"
"DP3 R7.w, c[7], v[1];"
// transform the normals from eye-space to world-space
"DP3 o[TEX1].x, R7, c[24];"
"DP3 o[TEX2].y, R7, c[25];"
"DP3 o[TEX3].z, R7, c[26];"
// put view dir into w of tex[1..3]
"DP4 o[TEX1].w, R4, c[24];"
"DP4 o[TEX2].w, R4, c[25];"
"DP4 o[TEX3].w, R4, c[26];"
// copy texcoords
"MOV o[TEX0], v[2];"
// done
"END";
// More complex vertex program. It uses tangent space transformations to
// achieve a more realistic bump.
//
// CONSTANTS:
// 0- 3 4x4 Projection matrix
// 4- 7 4x4 ModelView matrix
// 20-22 light amb/diff/spec
// 23 light dir
// 24-27 4x4 view transpose
//
// VERTEX REGISTERS:
// 0 - coord
// 1 - normal
// 2 - texcoord0
// 3 - texcoord1
// 4 - texcoord2 (binorm)
//
// REGISTERS:
// 4 = skinned (eye space) coord
// 5 = skinned (eye space) tangent
// 6 = skinned (eye space) binormal
// 7 = skinned (eye space) normal
// 8 = eye space view vector
// 9 = eye space half-angle vector
char originalVertexProgramString[] =
"!!VP1.0\n"
// skin the vertices
"DP4 R4.x, c[4], v[0];"
"DP4 R4.y, c[5], v[0];"
"DP4 R4.z, c[6], v[0];"
"DP4 R4.w, c[7], v[0];"
// final projection transformation
// transform the skinned coords by the projection matrix
"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];"
// skin the binormals
// skin binormals for bone0
"DP3 R6.x, c[4], v[4];"
"DP3 R6.y, c[5], v[4];"
"DP3 R6.z, c[6], v[4];"
"DP3 R6.w, c[7], v[4];"
// skin the normals
// skin normals for bone0
"DP3 R7.x, c[4], v[1];"
"DP3 R7.y, c[5], v[1];"
"DP3 R7.z, c[6], v[1];"
"DP3 R7.w, c[7], v[1];"
// renormalize and orthogonalize binormal, tangent & normal
// build tangent
"MUL R5, R6.zxyw, R7.yzxw;"
"MAD R5, R6.yzxw, R7.zxyw, -R5;"
// normalize tangent
"DP3 R5.w, R5, R5;"
"RSQ R5.w, R5.w;"
"MUL R5.xyz, R5, R5.w;"
// put the sign in the tangent
"MUL R5.xyz, R5, v[4].w;"
// fill texture coords with tangent space matrix for pixel shaders
// rotate tangent space matrix by view transpose
"DP3 o[TEX1].x, -R5, c[24];"
"DP3 o[TEX2].x, -R5, c[25];"
"DP3 o[TEX3].x, -R5, c[26];"
"DP3 o[TEX1].y, -R6, c[24];"
"DP3 o[TEX2].y, -R6, c[25];"
"DP3 o[TEX3].y, -R6, c[26];"
"DP3 o[TEX1].z, R7, c[24];"
"DP3 o[TEX2].z, R7, c[25];"
"DP3 o[TEX3].z, R7, c[26];"
// put view dir into w of tex[1..3]
"DP4 o[TEX1].w, -R4, c[24];"
"DP4 o[TEX2].w, -R4, c[25];"
"DP4 o[TEX3].w, -R4, c[26];"
// misc
// put diffuse lighting into color
"DP3 o[COL0], R7, c[23];"
"MOV o[COL0].w, c[50];"
// copy texcoords
"MOV o[TEX0], v[3];"
// done
"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 hwReflectBumpShader_NV20::loadVertexProgramGL()
{
// Only load this vertex program once.
if (vertex_program_id == 0)
initVertexProgram(originalVertexProgramString, &vertex_program_id);
// CONSTANTS:
// 0- 3 4x4 ModelView-Projection composite matrix
// 4- 7 4x4 ModelView matrix
// 20-22 light amb/diff/spec
// 23 light dir
// 24-27 4x4 view transpose
//
// VERTEX REGISTERS:
// 0 - coord
// 1 - normal
// 2 - texcoord0
//
// In this example, the upper-left 3x3 of the modelview matrix (M) and
// the upper-left 3x3 of the inverse transpose of the modelview matrix (M-t)
// are used interchangeably. This is because the modelview matrix contains
// only rigid-body transformations (rotation and translation), and in this
// case the matrices are identical.
//
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, 20, 1.0, 1.0, 1.0, 1.0); // light amb color
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 21, 1.0, 1.0, 1.0, 1.0); // light diff color
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 22, 1.0, 1.0, 1.0, 1.0); // light spec color
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 23, 0.0, 0.0, 1.0, 0.0); // light direction, for now.
// Get the modelView matrix
//
GLfloat modelViewMatrix[16];
glGetFloatv(GL_MODELVIEW_MATRIX, modelViewMatrix);
float stupidMatrix[4][4];
for (int i=0; i<16; i++)
{
stupidMatrix[i/4][i%4] = modelViewMatrix[i];
}
MMatrix mvMatrix(stupidMatrix);
// Calculate the view transpose matrix.
//
MMatrix mv = m_ModelMatrix.inverse() * mvMatrix;
mv = mv.transpose();
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 24, mv[0][0], mv[1][0], mv[2][0], mv[3][0]);
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 25, mv[0][1], mv[1][1], mv[2][1], mv[3][1]);
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 26, mv[0][2], mv[1][2], mv[2][2], mv[3][2]);
glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV, 27, mv[0][3], mv[1][3], mv[2][3], mv[3][3]);
}
// Initialize the necessary OpenGL extensions
//
void hwReflectBumpShader_NV20::init_ext(const char * ext)
{
if(!glh_init_extension(ext))
{ cerr << "Failed to initialize " << ext << "!" << endl; exit(0); }
}
hwReflectBumpShader_NV20::hwReflectBumpShader_NV20()
{
m_pTextureCache = MTextureCache::instance();
init_ext("GL_ARB_multitexture");
init_ext("GL_NV_register_combiners");
init_ext("GL_NV_texture_shader");
init_ext("GL_NV_vertex_program");
bumpScale = 1.0;
cubeMapOnly = FALSE;
texNames[0] = texNames[1] = texNames[2] = texNames[3] = texNames[4] = texNames[5] = 0;
currentTextureNames[0] = "";
currentTextureNames[1] = "";
currentTextureNames[2] = "";
currentTextureNames[3] = "";
currentTextureNames[4] = "";
currentTextureNames[5] = "";
attachSceneCallbacks();
vertex_program_id = 0;
}
hwReflectBumpShader_NV20::~hwReflectBumpShader_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 hwReflectBumpShader_NV20::releaseEverything()
{
if (texNames[0] != 0)
glDeleteTextures(6, &texNames[0]);
releaseVertexProgram(&vertex_program_id);
// Release the texture cache through refcounting.
m_pTextureCache->release();
if(!MTextureCache::getReferenceCount())
{
m_pTextureCache = 0;
}
}
void hwReflectBumpShader_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 hwReflectBumpShader_NV20::releaseCallback(void* clientData)
{
hwReflectBumpShader_NV20 *pThis = (hwReflectBumpShader_NV20*) clientData;
pThis->releaseEverything();
}
void hwReflectBumpShader_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( "hwReflectBumpShader_NV20", hwReflectBumpShader_NV20::id,
hwReflectBumpShader_NV20::creator, hwReflectBumpShader_NV20::initialize,
MPxNode::kHwShaderNode, &UserClassify );
if (!status) {
status.perror("registerNode");
return status;
}
return MS::kSuccess;
}
MStatus uninitializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj );
// Unregister all chamelion shader nodes
plugin.deregisterNode( hwReflectBumpShader_NV20::id );
if (!status) {
status.perror("deregisterNode");
return status;
}
return MS::kSuccess;
}
void * hwReflectBumpShader_NV20::creator()
{
return new hwReflectBumpShader_NV20();
}
MStatus hwReflectBumpShader_NV20::initialize()
{
MStatus status;
MFnTypedAttribute sAttr; // For string attributes
// Create input attrubutes
colorR = nAttr.create( "colorR", "cr",MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f);
colorG = nAttr.create( "colorG", "cg",MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.5f);
colorB = nAttr.create( "colorB", "cb",MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.5f);
color = nAttr.create( "color", "c", colorR, colorG, colorB);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f, 0.5f, 0.5f);
nAttr.setUsedAsColor(true);
bumpR = nAttr.create( "bumpR", "c2r",MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f);
bumpG = nAttr.create( "bumpG", "c2g",MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f);
bumpB = nAttr.create( "bumpB", "c2b",MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f);
bump = nAttr.create( "bump", "c2", bumpR, bumpG, bumpB);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f, 1.0f, 1.0f);
nAttr.setUsedAsColor(true);
uCoord = nAttr.create( "uCoord", "u", MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.5f);
vCoord = nAttr.create( "vCoord", "v", MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.5f);
uvCoord = nAttr.create( "uvCoord","uv", uCoord, vCoord);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.5f, 0.5f );
nAttr.setHidden(true);
uBias = nAttr.create( "uBias", "bu", MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setMin(0.0f);
nAttr.setMax(1.0f);
nAttr.setKeyable(true);
nAttr.setDefault(0.5f);
vBias = nAttr.create( "vBias", "bv", MFnNumericData::kFloat);
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setMin(0.0f);
nAttr.setMax(1.0f);
nAttr.setDefault(0.5f);
uvFilterSizeX = nAttr.create( "uvFilterSizeX", "fsx", MFnNumericData::kFloat);
nAttr.setStorable(false);
nAttr.setReadable(true);
nAttr.setWritable(true);
nAttr.setHidden(true);
uvFilterSizeY = nAttr.create( "uvFilterSizeY", "fsy", MFnNumericData::kFloat);
nAttr.setStorable(false);
nAttr.setReadable(true);
nAttr.setWritable(true);
nAttr.setHidden(true);
uvFilterSize = nAttr.create("uvFilterSize","fs",uvFilterSizeX,uvFilterSizeY);
nAttr.setStorable(false);
nAttr.setReadable(true);
nAttr.setWritable(true);
nAttr.setHidden(true);
// create output attributes here
// outColor is the only output attribute and it is inherited
// so we do not need to create or add it.
//
// Add the attributes here
addAttribute(color);
addAttribute(bump);
addAttribute(uvCoord);
addAttribute(uBias);
addAttribute(vBias);
addAttribute(uvFilterSize);
attributeAffects (colorR, outColor);
attributeAffects (colorG, outColor);
attributeAffects (colorB, outColor);
attributeAffects (color, outColor);
attributeAffects (bumpR, outColor);
attributeAffects (bumpG, outColor);
attributeAffects (bumpB, outColor);
attributeAffects (bump, outColor);
attributeAffects (uCoord, outColor);
attributeAffects (vCoord, outColor);
attributeAffects (uvCoord, outColor);
attributeAffects (uBias, outColor);
attributeAffects (vBias, outColor);
return MS::kSuccess;
}
// DESCRIPTION:
// See hwDecalBumpShader_NV20::compute().
//
MStatus hwReflectBumpShader_NV20::compute(
const MPlug& plug,
MDataBlock& block )
{
bool k = false;
k |= (plug==outColor);
k |= (plug==outColorR);
k |= (plug==outColorG);
k |= (plug==outColorB);
if( !k ) return MS::kUnknownParameter;
// set output color attribute
MDataHandle outColorHandle = block.outputValue( outColor );
MFloatVector& outColor = outColorHandle.asFloatVector();
outColor.x = 1.0;
outColor.y = 0.5;
outColor.z = 0.5;
outColorHandle.setClean();
return MS::kSuccess;
}
// To get 3 float values from the node attribute
//
MStatus hwReflectBumpShader_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("hwReflectBumpShader_NV20::bind plug.getValue.");
return status;
}
MFnNumericData data(object, &status);
if (!status)
{
status.perror("hwReflectBumpShader_NV20::bind construct data.");
return status;
}
status = data.getData(value[0], value[1], value[2]);
if (!status)
{
status.perror("hwReflectBumpShader_NV20::bind get values.");
return status;
}
return status;
}
// To get a string value from the node attribute
//
MStatus hwReflectBumpShader_NV20::getString(MObject attr, MString &str)
{
MPlug plug(thisMObject(), attr);
MStatus status = plug.getValue( str );
return status;
}
/* virtual */
MStatus hwReflectBumpShader_NV20::bind(const MDrawRequest& request,
M3dView& view)
{
MStatus status;
bool isHeightFieldMap = true; // Should be set to the value of an attribute
m_ModelMatrix = request.multiPath().inclusiveMatrix();
// Get the cube mapand bump map file names
MStringArray decalNames;
MString decalName;
MString bumpName;
ShadingConnection colorConnection(thisMObject(),
request.multiPath().partialPathName(), "color");
ShadingConnection bumpConnection (thisMObject(),
request.multiPath().partialPathName(), "bump");
// If the color attribute is ultimately connected to a 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 = "D:/chameleon/textures/Cham_body_color_real.tga";
// Append next environment map name
decalNames.append( decalName );
}
// If any of the environment maps are not mapped put in a fake texture
else
{
decalName = "D:/chameleon/textures/Cham_body_color_real.tga";
decalNames.append( decalName );
}
}
}
else
{
// Put in a fake texture for each side
decalName = "D:/chameleon/textures/Cham_body_color_real.tga";
for (int i=0; i<6; i++)
{
decalNames.append( decalName );
}
}
// If the bump attribute is ultimately connected to a file texture, find its filename.
// otherwise use the default bump texture.
if (bumpConnection.type() == ShadingConnection::TEXTURE &&
bumpConnection.texture().hasFn(MFn::kFileTexture))
{
// Get the filename of the texture.
MFnDependencyNode textureNode(bumpConnection.texture());
MPlug filenamePlug( bumpConnection.texture(), textureNode.attribute(MString("fileTextureName")) );
filenamePlug.getValue(bumpName);
}
else
{
bumpName = "";
}
// See if we are doing cube-map only. i.e. no bump
//
cubeMapOnly = (bumpName.length() == 0);
// Reload cube maps if the name of the textures
// for any of the cube maps changes
//
unsigned int width, height;
bool reload = FALSE;
for (int i=0; i<6; i++)
{
if (currentTextureNames[i] != decalNames[i])
{
reload = TRUE;
break;
}
}
if ( reload )
{
MString xpTexName(decalNames[2]);
MString xnTexName(decalNames[3]);
MString ypTexName(decalNames[0]);
MString ynTexName(decalNames[1]);
MString zpTexName(decalNames[4]);
MString znTexName(decalNames[5]);
MStatus stat;
if (! (stat = theImage_XP.readFromFile(xpTexName)) )
return MS::kFailure;
stat = theImage_XP.getSize( width, height );
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 (texNames[0] == 0)
glGenTextures(6, &texNames[0]);
glBindTexture( GL_TEXTURE_2D, texNames[0] );
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XP.pixels() );
glBindTexture( GL_TEXTURE_2D, texNames[1] );
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_XN.pixels() );
glBindTexture( GL_TEXTURE_2D, texNames[2] );
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YP.pixels() );
glBindTexture( GL_TEXTURE_2D, texNames[3] );
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_YN.pixels() );
glBindTexture( GL_TEXTURE_2D, texNames[4] );
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZP.pixels() );
glBindTexture( GL_TEXTURE_2D, texNames[5] );
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, theImage_ZN.pixels() );
for (i=0; i<6; i++) {
currentTextureNames[i] = decalNames[i];
}
}
// Get camera information needed
//
MDagPath cameraPath;
status = view.getCamera( cameraPath );
// Get rotation angle and axis
//
MVector camAxis;
double camTheta;
MMatrix mmatrix = cameraPath.inclusiveMatrix( &status );
MTransformationMatrix tmatrix( mmatrix );
m_CameraRotation = tmatrix.rotation();
m_CameraRotation.getAxisAngle( camAxis, camTheta );
// Convert to degrees from radians
camTheta *= 57.295779513082320876798154814105; // == (180 / M_PI)
view.beginGL();
glPushAttrib( GL_ALL_ATTRIB_BITS );
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
// Background color is always white
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glColor4f( 1, 1, 1, 1 );
if (cubeMapOnly)
{
glActiveTextureARB( GL_TEXTURE0_ARB );
glEnable(GL_TEXTURE_CUBE_MAP_ARB);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
for (i=0; i<6; i++)
glBindTexture( GL_TEXTURE_2D, texNames[i] );
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_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// Flip from Maya to OGL coordinates +
// rotate the Textures according to the camera orientation
//
glMatrixMode( GL_TEXTURE );
glPushMatrix();
glLoadIdentity();
glRotated( 180.0, 1.0, 0.0, 0.0 );
glRotated( camTheta, camAxis[0], camAxis[1], camAxis[2]);
// Pop the matrix is done during unbind, not here
//glPopMatrix();
glMatrixMode( GL_MODELVIEW );
}
else
{
loadVertexProgramGL();
// Setup texture combiners
//
glEnable(GL_TEXTURE_SHADER_NV);
// stage 0 -- bump normal map (input is u,v and normal map)
glActiveTextureARB( GL_TEXTURE0_ARB );
glEnable(GL_TEXTURE_2D);
//
// We need to be able to pass the bumpScaleValue
// to the texture cache and rebuild the bump or normal map
if( isHeightFieldMap ) {
// convert the HeightField to the NormalMap
if(m_pTextureCache)
m_pTextureCache->bind(bumpConnection.texture(), MTexture::NMAP, true);
}
else {
if(m_pTextureCache)
m_pTextureCache->bind(bumpConnection.texture(), MTexture::RGBA, true);
}
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
// stage 1 -- dot product (input is strq)
glActiveTextureARB( GL_TEXTURE1_ARB );
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV);
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);
// stage 2 -- dot product (input is strq)
glActiveTextureARB( GL_TEXTURE2_ARB );
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_NV);
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);
// stage 3 -- dot product reflect cube map (input is strq, and cube maps)
// ======================================================================
glActiveTextureARB( GL_TEXTURE3_ARB );
glEnable(GL_TEXTURE_CUBE_MAP_ARB);
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV);
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// Bind the 6 textures
//
for (i=0; i<6; i++)
glBindTexture( GL_TEXTURE_2D, texNames[i] );
// Specify the texture parameters
//
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_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Done setting the texture unit 3
//
glActiveTextureARB( GL_TEXTURE0_ARB );
// define a white color
//
float white_color[4] = {1.0, 1.0, 1.0, 1.0};
glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, white_color);
// The register combiner will do the multiplication between
// the illumination and the decal color
//
glEnable(GL_REGISTER_COMBINERS_NV);
glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
// Combiner stage 0 get the input from texture stage3, pass through
//
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE3_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_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
// The final Combiner just pass through
//
glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
verify_shader_config( view );
}
view.endGL();
return MS::kSuccess;
}
/* virtual */
MStatus hwReflectBumpShader_NV20::unbind(const MDrawRequest& request,
M3dView& view)
{
view.beginGL();
if (cubeMapOnly)
{
// Pop the texture matrix pushed during bind
glActiveTextureARB( GL_TEXTURE0_ARB );
glMatrixMode( GL_TEXTURE );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glDisable( GL_TEXTURE_GEN_S );
glDisable( GL_TEXTURE_GEN_T );
glDisable( GL_TEXTURE_GEN_R );
glDisable( GL_TEXTURE_CUBE_MAP_ARB );
}
else
{
glDisable(GL_TEXTURE_SHADER_NV);
glActiveTextureARB( GL_TEXTURE0_ARB );
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glActiveTextureARB( GL_TEXTURE1_ARB );
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glActiveTextureARB( GL_TEXTURE2_ARB );
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glActiveTextureARB( GL_TEXTURE3_ARB );
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glDisable(GL_REGISTER_COMBINERS_NV);
glDisable(GL_VERTEX_PROGRAM_NV);
}
glActiveTextureARB( GL_TEXTURE0_ARB );
glPopClientAttrib();
glPopAttrib();
view.endGL();
return MS::kSuccess;
}
/* virtual */
MStatus hwReflectBumpShader_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::kFailure;
view.beginGL();
glEnable(GL_VERTEX_ARRAY);
if (cubeMapOnly)
{
glVertexPointer(3, GL_FLOAT, 0, vertexArray);
glEnable(GL_VERTEX_ARRAY);
if (normalCount > 0)
{
glNormalPointer(GL_FLOAT, 0, normalArrays[0]);
glEnable(GL_NORMAL_ARRAY);
}
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray);
glDisable(GL_VERTEX_ARRAY);
glDisable(GL_NORMAL_ARRAY);
}
else
{
// Bind and enable the vertex program
glBindProgramNV(GL_VERTEX_PROGRAM_NV, vertex_program_id);
glEnable(GL_VERTEX_PROGRAM_NV);
// VERTEX REGISTERS:
// 0 - coord
// 1 - normal
// 2 - texcoord0
// 3 - texcoord1
// 4 - texcoord2 (binorm)
glVertexAttribPointerNV( 0, 3, GL_FLOAT, 0, vertexArray );
glVertexAttribPointerNV( 1, 3, GL_FLOAT, 0, normalArrays[0] );
glVertexAttribPointerNV( 2, 2, GL_FLOAT, 0, texCoordArrays[0] );
glVertexAttribPointerNV( 3, 2, GL_FLOAT, 0, texCoordArrays[0] );
glVertexAttribPointerNV( 4, 3, GL_FLOAT, 0, normalArrays[2] );
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV );
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY1_NV );
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV );
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY3_NV );
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY4_NV );
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, indexArray);
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY0_NV );
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY1_NV );
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY2_NV );
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY3_NV );
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY4_NV );
glDisable(GL_VERTEX_PROGRAM_NV);
}
glDisable(GL_VERTEX_ARRAY);
view.endGL();
return MS::kSuccess;
}
/* virtual */
int hwReflectBumpShader_NV20::normalsPerVertex()
{
return 3;
}
/* virtual */
int hwReflectBumpShader_NV20::texCoordsPerVertex()
{
return 1;
}