//- // ========================================================================== // Copyright (C) 2005 ATI Technologies Inc. All rights reserved. // // 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. // // ========================================================================== //+ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Platform.h" #include "GLSLShaderNode.h" #include "glExtensions.h" #include "ResourceManager.h" #ifdef _HARDWARE_RENDERER_AVAILABLE_ #include #include #include #include #endif // If and when a bug in Maya is fixed so that you // can keyframe internal dynamic attributes, the // code within this ifdef cannot be used. // If you set the attributes to be internal, you // can keyframe, but playback may or may not work // erratically. This is a known API bug. // //#define _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS // // //statics // //////////////////////////////////////////////////////////////////////////////// // This id is supplied by Maya and should never be changed. MTypeId glslShaderNode::sId( 0x81022); //these are the attributes shared by all instances MObject glslShaderNode::sShader; MObject glslShaderNode::sTechnique; MObject glslShaderNode::sTechniqueList; // Convenience attribute MObject glslShaderNode::sShaderPath; // Convenience attribute MObject glslShaderNode::sNakedTexCoords[8]; #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ MObject glslShaderNode::sNakedColors[2]; #endif //this is the default shader, used to render a checkered phong shader *glslShaderNode::sDefShader = NULL; // This is a list of nodes presently created of this type. // It allows us to find all shader nodes and update them after a file load, // and it eases searching for use by the command. std::vector glslShaderNode::sNodeList; //default texture map strings //const char *glslShaderNode::nakedSetNames[8] = { "map1", "", "", "", "", "", "", ""}; const char *glslShaderNode::nakedSetNames[8] = { "map1", "tangent", "binormal", "", "", "", "", ""}; // // // //////////////////////////////////////////////////////////////////////////////// glslShaderNode::glslShaderNode() : m_shader(NULL), m_shaderDirty(false), m_parsedUserAttribList( false) { // Make sure we have our extensions initialised // We do it here rather than at render time so that setting a shader program // in batch mode will populate the shader attributes // InitializeExtensions(); for (int ii=0; ii<8; ii++) { m_nakedTexSets[ii] = nakedSetNames[ii]; m_setNums[ii] = -1; m_mayaType[ii] = kNonMaya; } } // // // //////////////////////////////////////////////////////////////////////////////// glslShaderNode::~glslShaderNode() { #ifdef _HARDWARE_RENDERER_AVAILABLE_ if (MGlobal::mayaState() != MGlobal::kBatch) { MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer(); if (pRenderer) { const MString & backEndStr = pRenderer->backEndString(); MStatus status = pRenderer->makeResourceContextCurrent( backEndStr ); if (status != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to make resource context current: ")); MGlobal::displayError(status.errorString()); } } } #endif //clean up any textures we have created for (std::vector::iterator it=m_samplerAttribList.begin(); ittexName); } //keep the list of allocated shaders correct { //this fixes MSVC and its busted scoping rules std::vector::iterator it = std::find( sNodeList.begin(), sNodeList.end(), this); if (it != sNodeList.end()) { //remove this one from the list sNodeList.erase(it); } else { //this is really bad, somehow one got created without going through create } } delete m_shader; } // // // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::postConstructor() { } // //compute when DAG change occurs (likely nothing for HwShader) // //////////////////////////////////////////////////////////////////////////////// MStatus glslShaderNode::compute( const MPlug& plug, MDataBlock& data) { //need to look to compute a default color if ((plug == outColor) || (plug.parent() == outColor)) { MFloatVector color(.95f, .1f, .07f); MDataHandle outputHandle = data.outputValue( outColor ); outputHandle.asFloatVector() = color; outputHandle.setClean(); return MS::kSuccess; } return MS::kUnknownParameter; } // //factory for node creation // //////////////////////////////////////////////////////////////////////////////// void* glslShaderNode::creator() { glslShaderNode *ret = new glslShaderNode; //add this new node to the list sNodeList.push_back(ret); return ret; } // // initialization (static) // //////////////////////////////////////////////////////////////////////////////// MStatus glslShaderNode::initialize() { //add the necessary nodes MFnTypedAttribute typedAttr; MFnStringData stringData; MStatus stat; MObject string; // The attribute names and short names below are selected to be compatible with // the CgFX plugin for simplicity // // Create the shader attribute and add it to the node type // // The shader attribute holds the file name of an fx file string = stringData.create(&stat); if (stat != MS::kSuccess) return MS::kFailure; sShader = typedAttr.create("shader", "s", MFnData::kString, string, &stat); if (stat != MS::kSuccess) return MS::kFailure; stat = typedAttr.setInternal(true); if (stat != MS::kSuccess) return MS::kFailure; // Add the attribute to the node if ( addAttribute(sShader) != MS::kSuccess) return MS::kFailure; // // Create the technique // // The technique attribute holds the currently selected technique name string = stringData.create(&stat); if (stat != MS::kSuccess) return MS::kFailure; sTechnique = typedAttr.create("technique", "t", MFnData::kString, string, &stat); if (stat != MS::kSuccess) return MS::kFailure; stat = typedAttr.setInternal(true); if (stat != MS::kSuccess) return MS::kFailure; // Add the attribute to the node if ( addAttribute(sTechnique) != MS::kSuccess) return MS::kFailure; // Create a techniqueList to allow access by the AE for UI purposes sTechniqueList = typedAttr.create("techniqueList", "tl", MFnData::kString, string, &stat); if (stat != MS::kSuccess) return MS::kFailure; stat = typedAttr.setInternal(true); if (stat != MS::kSuccess) return MS::kFailure; typedAttr.setWritable( false ); typedAttr.setStorable( false ); typedAttr.setHidden( true ); typedAttr.setConnectable( false ); // Add the attribute to the node if ( addAttribute(sTechniqueList) != MS::kSuccess) return MS::kFailure; // Create a shader path attrib to allow access by the AE for UI purposes sShaderPath = typedAttr.create("shaderPath", "sp", MFnData::kString, string, &stat); if (stat != MS::kSuccess) return MS::kFailure; stat = typedAttr.setInternal(true); if (stat != MS::kSuccess) return MS::kFailure; typedAttr.setWritable( false ); typedAttr.setStorable( false ); typedAttr.setHidden( true ); typedAttr.setConnectable( false ); // Add the attribute to the node if ( addAttribute(sShaderPath) != MS::kSuccess) return MS::kFailure; int ii; // // Add the naked texture coord sets to the node // for (ii=0; ii<8; ii++) { string = stringData.create( MString(nakedSetNames[ii])); sNakedTexCoords[ii] = typedAttr.create( MString("TexCoord") + ii, MString("t")+ ii, MFnData::kString, string); typedAttr.setInternal( true); typedAttr.setKeyable( true ); addAttribute( sNakedTexCoords[ii]); } // Add the naked color sets to the node #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ for (ii=0; ii<2; ii++) { sNakedColors[ii] = typedAttr.create( MString("Color") + ii, MString("c")+ ii, MFnData::kString); typedAttr.setInternal( true); typedAttr.setKeyable( true ); addAttribute( sNakedColors[ii]); } #endif return MS::kSuccess; } // // callback to configure shaders, post file load // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::rejigShaders( void *data) { //iterate over all shaders, and rebuild them, so they can reconnect attributes for (std::vector::iterator it = sNodeList.begin(); it < sNodeList.end(); it++) { (*it)->rebuildShader(); } } // // Update shader and attributes // //////////////////////////////////////////////////////////////////////////////// static bool textureInitPowerOfTwo(unsigned int val, unsigned int & retval) { unsigned int res = 0; // May be we should return 1 when val == 0 if (val) { // Muliply all values by 2, to simplify the testing: // 3*(res/2) < val*2 <= 3*res val <<= 1; unsigned int low = 3; res = 1; while (val > low) { low <<= 1; res <<= 1; } } retval = res; return (res == (val>>1)) ? 1 : 0; } bool glslShaderNode::rebuildShader() { //handle shader initialization bool rebuiltAnything = false; GL_CHECK; // Re (build) current shader if (m_shaderDirty) { MGlobal::displayInfo(MString("Rebuild shader : ") + m_shaderPath.asChar() ); // Clean up old shader (delete NULL is safe, does not need protected) delete m_shader; GL_CHECK; // Create a new shader m_shader = shader::create(m_shaderPath.asChar()); GL_CHECK; if (!m_shader) { MGlobal::displayError("Failed to load shader"); MGlobal::displayError(shader::sError.c_str()); } else { rebuiltAnything = true; // Try to reuse the existing technique name. // This will allow for being able to re-read // files which have saved a technique name which // is not technique 0. bool foundExistingTechnique = false; if (m_techniqueName.length()) { for (int ii=0; iitechniqueCount(); ii++) { if (m_techniqueName == m_shader->techniqueName(ii)) { m_shader->setTechnique(ii); foundExistingTechnique = true; break; } } } // Just use technique 0 as a default, since we don't // know which one to use. if (!foundExistingTechnique) { m_techniqueName = m_shader->techniqueName(0); } m_techniqueList = m_techniqueName ; for (int ii=1; iitechniqueCount(); ii++) m_techniqueList = m_techniqueList + " " + m_shader->techniqueName(ii); // Reconfigure node attributes configureAttribs(); GL_CHECK; } m_shaderDirty = false; } m_techniqueList = ""; if (m_shader && m_shader->techniqueCount()) { for (int ii=0; iitechniqueCount(); ii++) m_techniqueList = m_techniqueList + " " + m_shader->techniqueName(ii); } return rebuiltAnything; } // // search the dependency graph for a file texture node // //////////////////////////////////////////////////////////////////////////////// MObject glslShaderNode::findFileTextureNode( const MPlug &plug) { MPlugArray arr; MStatus stat; plug.connectedTo(arr, true, false, &stat); MObject srcNode; if (stat != MStatus::kSuccess) return MObject::kNullObj; if (arr.length() == 1) srcNode = arr[0].node(); else if (arr.length() == 0 ) { //plug is not connected return MObject::kNullObj; } else { //plug is multiply connected MGlobal::displayWarning("Texture plug is connected to multiple nodes"); return MObject::kNullObj; } if( srcNode != MObject::kNullObj) { MStatus rc; MFnDependencyNode dgFn( srcNode, &rc); MPlug fnPlug = dgFn.findPlug( "fileTextureName", &rc); if (rc == MStatus::kSuccess) { MStatus tStat; MObject fTex = fnPlug.node(&tStat); if ( tStat != MStatus::kSuccess) return MObject::kNullObj; else return fTex; } } return MObject::kNullObj; } // // search the dependency graph for an envCube node // //////////////////////////////////////////////////////////////////////////////// MObject glslShaderNode::findEnvCubeNode( const MPlug &plug) { MPlugArray arr; plug.connectedTo(arr, true, false); MObject srcNode; if (arr.length() == 1) srcNode = arr[0].node(); else if (arr.length() == 0 ) { //plug is not connected return MObject::kNullObj; } else { //plug is multiply connected MGlobal::displayWarning("Texture plug is connected to multiple nodes"); return MObject::kNullObj; } // if we have a real node, look for the envCube if( srcNode != MObject::kNullObj) { MStatus rc; MFnDependencyNode dgFn( srcNode, &rc); // searching for the plug seems a pretty good solution MPlug fnPlug = dgFn.findPlug( "left", &rc); if (rc == MStatus::kSuccess) { MStatus tStat; MObject fCube = fnPlug.node(&tStat); if ( tStat != MStatus::kSuccess) return MObject::kNullObj; else return fCube; } } return MObject::kNullObj; } // // load a cubemap face from a cubeEnv node // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::loadCubeFace( glslShaderNode::CubeFace cf, MObject &cube, MFnDependencyNode &dn){ const char* faceNames[] = { "left", "right", "top", "bottom", "front", "back"}; const GLenum targets[] = {GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; float rgb[3]; MObject face = dn.attribute( faceNames[cf]); MPlug plug( cube, face); MObject fTex = findFileTextureNode( plug); if (fTex != MObject::kNullObj) { MImage img; unsigned int width, height; img.readFromTextureNode(fTex); MStatus status = img.getSize( width, height); // If not power of two and NPOT is not supported, then we need // to resize the original system pixmap before binding. // // Also handle non-square cube map textures bool resizeToBeSquare = false; if (width != height) { width = height; resizeToBeSquare = true; } if (width > 0 && height > 0 && (status != MStatus::kFailure) ) { // If not power of two and NPOT is not supported, then we need // to resize the original system pixmap before binding. // if (!glTextureNonPowerOfTwo || resizeToBeSquare ) { if (width > 2 && height > 2) { unsigned int p2Width, p2Height; bool widthPowerOfTwo = textureInitPowerOfTwo(width, p2Width); bool heightPowerOfTwo = textureInitPowerOfTwo(height, p2Height); if(!widthPowerOfTwo || !heightPowerOfTwo || resizeToBeSquare) { width = p2Width; height = p2Height; img.resize( p2Width, p2Height, false /* preserverAspectRatio */); } } } //MGlobal::displayInfo( MString("Loading ") + faceNames[cf]+ " face (" + width + "," + height + ")"); glTexImage2D( targets[cf], 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels()); GL_CHECK; } } else { MObject data; plug.getValue( data); MFnNumericData val(data); val.getData( rgb[0], rgb[1], rgb[2]); glTexImage2D( targets[cf], 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_FLOAT, rgb); } } // // Bind data such as texture objects for samplers. // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::BindSamplerData() { //iterate over all samplers for ( std::vector::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) { //only update if it has been marked dirty if (it->dirty) { it->dirty = false; //get the plug and associated info MPlug plug(thisMObject(), it->attrib); MObject data; plug.getValue(data); MFnNumericData val(data); float rgb[3]; if (it->texName == 0) { //need to gen a new name, one has not been created glGenTextures( 1, &(it->texName)); m_shader->updateSampler( it->pNum, it->texName); GL_CHECK; } switch (m_shader->samplerType(it->pNum) ) { case shader::st1D: //just load a 1D texture from the plug value until we handle files directly val.getData( rgb[0], rgb[1], rgb[2]); glBindTexture( GL_TEXTURE_1D, it->texName); glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, it->minFilter); glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, it->magFilter); glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, it->wrapMode); glTexParameterf( GL_TEXTURE_1D, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso); if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) { glTexParameteri( GL_TEXTURE_1D, GL_GENERATE_MIPMAP, GL_TRUE); } else { glTexParameteri( GL_TEXTURE_1D, GL_GENERATE_MIPMAP, GL_FALSE); } glTexImage1D( GL_TEXTURE_1D, 0, GL_RGB8, 1, 0, GL_RGB, GL_FLOAT, rgb); GL_CHECK; break; case shader::st1DShadow: //are shadow maps accessible in Maya? break; case shader::st2D: { bool done = false; //set up the texture stuff glBindTexture( GL_TEXTURE_2D, it->texName); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, it->minFilter); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, it->magFilter); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, it->wrapMode); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, it->wrapMode); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso); if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) { glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); } else { glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); } MObject fTex = findFileTextureNode( plug); if (fTex != MObject::kNullObj) { MImage img; unsigned int width, height; img.readFromTextureNode(fTex); MStatus status = img.getSize( width, height); if (width > 0 && height > 0 && (status != MStatus::kFailure) ) { // If not power of two and NPOT is not supported, then we need // to resize the original system pixmap before binding. // if (!glTextureNonPowerOfTwo) { if (width > 2 && height > 2) { unsigned int p2Width, p2Height; bool widthPowerOfTwo = textureInitPowerOfTwo(width, p2Width); bool heightPowerOfTwo = textureInitPowerOfTwo(height, p2Height); if(!widthPowerOfTwo || !heightPowerOfTwo) { width = p2Width; height = p2Height; img.resize( p2Width, p2Height, false /* preserverAspectRatio */); } } } glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels()); GL_CHECK; done = true; } } if (!done) { //just read it from the plug val.getData( rgb[0], rgb[1], rgb[2]); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_FLOAT, rgb); } GL_CHECK; } break; case shader::stCube: { //search for envCube node, then traverse off the 6 faces glBindTexture( GL_TEXTURE_CUBE_MAP, it->texName); glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, it->minFilter); glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, it->magFilter); glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, it->wrapMode); glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, it->wrapMode); glTexParameterf( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso); if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) { glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE); } else { glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_FALSE); } MObject cube = findEnvCubeNode( plug); if ( cube != MObject::kNullObj) { MFnDependencyNode dn(cube); loadCubeFace( cfLeft, cube, dn); loadCubeFace( cfRight, cube, dn); loadCubeFace( cfTop, cube, dn); loadCubeFace( cfBottom, cube, dn); loadCubeFace( cfFront, cube, dn); loadCubeFace( cfBack, cube, dn); } } break; case shader::st2DShadow: case shader::st3D: //need to handle file loading directly for 3D textures break; default: break; }; } } } // // // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::BindUniformData() { for ( std::vector::iterator it = m_uniformAttribList.begin(); it < m_uniformAttribList.end(); it++) { // [ANIM_UNIFORM_FIX2] // To allow animation to work, uniform data has been // left to be dirty all of the time, as internal attributes // do not update properly during playback. // #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS if (it->dirty) { #endif it->dirty = false; MPlug plug(thisMObject(), it->attrib); MObject data; plug.getValue(data); MFnNumericData val(data); float fTemp[4]; int iTemp[4]; bool bTemp[4]; switch (m_shader->uniformType(it->pNum)) { case shader::dtBool: plug.getValue(bTemp[0]); m_shader->updateUniformBool( it->pNum, bTemp[0]); break; case shader::dtBVec2: case shader::dtBVec3: case shader::dtBVec4: //no Maya native type, these are unsupported for now break; case shader::dtInt: plug.getValue(iTemp[0]); m_shader->updateUniformInt( it->pNum, iTemp[0]); break; case shader::dtIVec2: val.getData( iTemp[0], iTemp[1]); m_shader->updateUniformIVec( it->pNum, iTemp); break; case shader::dtIVec3: val.getData( iTemp[0], iTemp[1], iTemp[2]); m_shader->updateUniformIVec( it->pNum, iTemp); break; case shader::dtIVec4: //no Maya native type, these are unsupported for now break; case shader::dtFloat: plug.getValue(fTemp[0]); m_shader->updateUniformFloat( it->pNum, fTemp[0]); break; case shader::dtVec2: val.getData( fTemp[0], fTemp[1]); m_shader->updateUniformVec( it->pNum, fTemp); break; case shader::dtVec3: val.getData( fTemp[0], fTemp[1], fTemp[2]); m_shader->updateUniformVec( it->pNum, fTemp); break; case shader::dtVec4: { MPlug plug2(thisMObject(), it->attrib2); val.getData( fTemp[0], fTemp[1], fTemp[2]); plug2.getValue( fTemp[3]); m_shader->updateUniformVec( it->pNum, fTemp); } break; case shader::dtMat4: { MFnMatrixData mValue(data); MMatrix matrix = mValue.matrix(); m_shader->updateUniformMat( it->pNum, (double*)matrix.matrix); } break; case shader::dtMat2: case shader::dtMat3: //unsupported for now, due to Maya's interface presently lacking support break; default: break; }; } #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS } #endif } // Method to override to let Maya know this shader is batchable. // bool glslShaderNode::supportsBatching() const { return true; } // // Old style drawing commands like the HW renderer needs // //////////////////////////////////////////////////////////////////////////////// MStatus glslShaderNode::glBind(const MDagPath& shapePath) { // Rebuild shader, if dirty // This is only required pre 7.0, and should not be part of // the plugin anymore unless we decide to ship with 6.5. // // Initialize GL extesions if (!InitializeExtensions()) { MGlobal::displayError("Ashli Shader failed to find necessary extensions"); return MStatus::kFailure; } GL_CHECK; //attempt to clean up any delayed deletions ResourceManager::recover(); // Build default shader if (!sDefShader) sDefShader = new defaultShader; GL_CHECK; // //bind any dirty data //should add an extra global dirty flag if (m_shader) { if (!m_shader->build()) MGlobal::displayError(MString(m_shader->errorString())); BindUniformData(); //now the samplers BindSamplerData(); } // Disable these to avoid possible problems with shader programs // going into software glDisable(GL_POINT_SMOOTH); glDisable( GL_LINE_SMOOTH); //check for active shader, if none, fall back to the default shader if ((m_shader != NULL) && (m_shader->valid())) { m_passCount = m_shader->passCount(); if (!m_shader->build()) { m_passCount = 0; } if( m_passCount == 1) { m_shader->setPass( 0); m_shader->bind(); } } else { m_passCount = -1; m_maxSetNum = 1; // We need the default UV sDefShader->bind(); } glEnableClientState(GL_VERTEX_ARRAY); glEnable(GL_VERTEX_PROGRAM_TWO_SIDE); GL_CHECK; return MS::kSuccess; } // // // //////////////////////////////////////////////////////////////////////////////// MStatus glslShaderNode::glUnbind(const MDagPath& shapePath) { for (std::vector::iterator it=m_attributeAttribList.begin(); ithandle); for( int ii = 0; ii < m_maxSetNum; ii++) m_glState.disableTexCoord( ii); m_glState.activeTexture( 0); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_VERTEX_PROGRAM_TWO_SIDE); // If we're not using multi-pass, disable the effect here if( m_passCount < 0) sDefShader->unbind(); else if( m_passCount == 1) m_shader->unbind(); // Dirty our attribute list so it gets re-built next time // this shader is used m_parsedUserAttribList = false; return MS::kSuccess; } // // // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::updateBoundAttributes( const MDagPath& shapePath) { // // This code needs to get the current matrices // the world matrix is easy (dagPath) // The view matrix seems like it can come from the current M3DView // I can't see any good way to get the projection matrix other than reading it // MMatrix world; MMatrix temp; MMatrix proj; MMatrix view; MMatrix worldview; //get the matrices if (shapePath.isValid()) world = shapePath.inclusiveMatrix(); else world.setToIdentity(); glGetDoublev( GL_PROJECTION_MATRIX, (double*)proj.matrix); glGetDoublev( GL_MODELVIEW_MATRIX, (double*)worldview.matrix); view = world.inverse() * worldview; for (std::vector::iterator it = m_boundUniformList.begin(); it < m_boundUniformList.end(); it++) { switch ( it->usage) { case shader::smWorld: m_shader->updateUniformMat( it->pNum, (double*)world.matrix); break; case shader::smView: m_shader->updateUniformMat( it->pNum, (double*)view.matrix); break; case shader::smProjection: m_shader->updateUniformMat( it->pNum, (double*)proj.matrix); break; case shader::smWorldView: m_shader->updateUniformMat( it->pNum, (double*)worldview.matrix); break; case shader::smViewProjection: temp = view * proj; m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldViewProjection: temp = worldview * proj; m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldI: temp = world.inverse(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smViewI: temp = view.inverse(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smProjectionI: temp = proj.inverse(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldViewI: temp = worldview.inverse(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smViewProjectionI: temp = view * proj; temp = temp.inverse(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldViewProjectionI: temp = worldview * proj; temp = temp.inverse(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldT: temp = world.transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smViewT: temp = view.transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smProjectionT: temp = proj.transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldViewT: temp = worldview.transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smViewProjectionT: temp = view * proj; temp = temp.transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldViewProjectionT: temp = worldview * proj; temp = temp.transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldIT: temp = world.inverse().transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smViewIT: temp = view.inverse().transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smProjectionIT: temp = proj.inverse().transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldViewIT: temp = worldview.inverse().transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smViewProjectionIT: temp = view * proj; temp = temp.inverse().transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smWorldViewProjectionIT: temp = worldview * proj; temp = temp.inverse().transpose(); m_shader->updateUniformMat( it->pNum, (double*)temp.matrix); break; case shader::smViewPosition: { float vec[4]; temp = view.inverse(); vec[0] = (float)temp[3][0]; vec[1] = (float)temp[3][1]; vec[2] = (float)temp[3][2]; vec[3] = (float)temp[3][3]; m_shader->updateUniformVec( it->pNum, vec); } break; case shader::smTime: // need to find a good way to get the current animation time // the cgfx plugin uses: // (float)timeGetTime() * 1000.0f // This has two problems // 1. one it does not correspond to Maya time // 2. it has portability problems m_shader->updateUniformFloat( it->pNum, 0.0f); break; case shader::smViewportSize: { float vp[4]; glGetFloatv( GL_VIEWPORT, vp); m_shader->updateUniformVec( it->pNum, &vp[2]); } break; default: break; }; } } // // Configure the texture coordinate sets and color sets for rendering. This // function sets up all the vertex arry pointers or current values, if a set // is not available. //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::configureTexCoords( int normalCount, const float ** normalArrays, int texCoordCount, const float ** texCoordArrays, int colorCount, const float ** colorArrays) { //printf("*********** configureTexCoords [normal count = %d]\n", normalCount); int ii; //bind in the naked texture sets for (ii=0; ii 0) && normalArrays[0]) { //there is a tangent m_glState.enableAndActivateTexCoord( ii); glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[0]); } else { //there is no tangent available glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 0.0f, 1.0f); m_glState.disableTexCoord( ii); } continue; } else if (m_mayaType[ii] == kTangent) { //this set has requested a tangent if ( m_numNormals > 1 && normalCount > 1) { int offset = 0; if (m_numNormals > 2) offset = ( m_setNums[ii] * 3 ) + 1; else offset = ( m_setNums[ii] * 2 ) + 1; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 2) offset = 1; else offset = -1; } //printf("TANGENT%d : Access tangents via collection id(%d) = offset %d\n", // ii, m_setNums[ii], offset ); if ((offset > 0) && normalArrays[offset]) { //there is a tangent m_glState.enableAndActivateTexCoord( ii); glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[offset]); } else { //there is no tangent available glMultiTexCoord3f( GL_TEXTURE0 + ii, 1.0f, 0.0f, 0.0f); m_glState.disableTexCoord( ii); } } else { //there is no tangent available glMultiTexCoord3f( GL_TEXTURE0 + ii, 1.0f, 0.0f, 0.0f); m_glState.disableTexCoord( ii); } continue; } else if (m_mayaType[ii] == kBinormal) { //this set has requested a binormal if ( m_numNormals > 2 && normalCount > 1) { int offset = ( m_setNums[ii] * 3 ) + 2; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 3) offset = 2; else offset = -1; } //printf("BINORMAL%d : Access binormals via collection id(%d) = offset %d\n", // ii, m_setNums[ii], offset ); if ((offset > 0) && normalArrays[offset] ) { //there is a binormal m_glState.enableAndActivateTexCoord( ii); glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[offset]); } else { //there is no binormal available glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f); m_glState.disableTexCoord( ii); } } else { //there is no binormal available glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f); m_glState.disableTexCoord( ii); } continue; } else if (m_mayaType[ii] == kColorSet) { //this set has requested a color if ( (m_setNums[ii] < colorCount) && colorArrays[m_setNums[ii]]) { //there is a color m_glState.enableAndActivateTexCoord( ii); glTexCoordPointer( 4, GL_FLOAT, 0, colorArrays[m_setNums[ii]]); } else { //there is no binormal available glMultiTexCoord4f( GL_TEXTURE0 + ii, 0.0f, 0.0f, 0.0f, 1.0f); m_glState.disableTexCoord( ii); } continue; } //now deal with regular sets if ( (m_setNums[ii] < texCoordCount) && texCoordArrays[m_setNums[ii]]) { //this set exists m_glState.enableAndActivateTexCoord( ii); glTexCoordPointer( 2, GL_FLOAT, 0, texCoordArrays[m_setNums[ii]]); } else { //the set does not exist glMultiTexCoord2f( GL_TEXTURE0 + ii, 0.0f, 0.0f); m_glState.disableTexCoord( ii); } } #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ //bind in the naked color sets //primary color first glColor4f( 1.0f, 1.0f, 1.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); //handle the special numbers if (m_colorSetNums[0] == -1) { // no name is specified, send a default color of (1,1,1,1) glColor4f( 1.0f, 1.0f, 1.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } else if (m_colorMayaType[0] == kNormal) { //this set has requested a normal if ( (normalCount > 0) && normalArrays[0]) { //there is a tangent glColorPointer( 3, GL_FLOAT, 0, normalArrays[0]); glEnableClientState( GL_COLOR_ARRAY); } else { //there is no normal available glColor4f( 0.0f, 0.0f, 1.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } } else if (m_colorMayaType[0] == kTangent) { //this set has requested a tangent if ( m_numNormals > 1 && normalCount > 1) { int offset = 0; if (m_numNormals > 2) offset = ( m_colorSetNums[0] * 3 ) + 1; else offset = ( m_colorSetNums[0] * 2 ) + 1; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 2) offset = 1; else offset = -1; } //printf("COLOR0: Access tangents via collection id(%d) = offset %d\n", // m_setNums[ii], offset ); if ((offset > 0) && normalArrays[offset]) { //there is a tangent glColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]); glEnableClientState( GL_COLOR_ARRAY); } else { //there is no tangent available glColor4f( 1.0f, 0.0f, 0.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } } else { //there is no tangent available glColor4f( 1.0f, 0.0f, 0.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } } else if (m_colorMayaType[0] == kBinormal) { //this set has requested a binormal if ( m_numNormals > 2 && normalCount > 1) { int offset = ( m_colorSetNums[0] * 3 ) + 2; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 3) offset = 2; else offset = -1; } //printf("COLOR0: Access binormals via collection id(%d) = offset %d\n", // m_setNums[ii], offset ); if ((offset > 0) && normalArrays[offset] ) { //there is a binormal glColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]); glEnableClientState( GL_COLOR_ARRAY); } else { //there is no binormal available glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f); m_glState.disableTexCoord( ii); } } else { //there is no binormal available glColor4f( 0.0f, 1.0f, 0.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } } // Route color set through color 0 else if (m_colorMayaType[0] == kColorSet) { //this set has requested a color if ( (m_colorSetNums[0] < colorCount) && colorArrays[m_colorSetNums[0]]) { //there is a color glColorPointer( 4, GL_FLOAT, 0, colorArrays[m_colorSetNums[0]]); glEnableClientState( GL_COLOR_ARRAY); } else { //there is no color available glColor4f( 0.0f, 0.0f, 0.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } } // Route a uv set through color 0 else if (m_colorMayaType[0] == kUvSet) { //this set has requested a uvset if ( (m_colorSetNums[0] < texCoordCount) && texCoordArrays[m_colorSetNums[0]]) { // For now Maya only supports 2 compont tex coords. glColorPointer( 2, GL_FLOAT, 0, texCoordArrays[m_colorSetNums[0]]); glEnableClientState( GL_COLOR_ARRAY); } else { //there is no color available glColor4f( 1.0f, 0.0f, 0.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } } //secondary color if (glSecondaryColorSupport) { glSecondaryColor3f( 1.0f, 1.0f, 1.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); //handle the special numbers if (m_colorSetNums[1] == -1) { // no name is specified, send a default color of (1,1,1) glSecondaryColor3f( 1.0f, 1.0f, 1.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); } else if (m_colorMayaType[1] == kNormal) { //this set has requested a normal if ( (normalCount > 0) && normalArrays[0]) { //there is a tangent glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[0]); glEnableClientState( GL_SECONDARY_COLOR_ARRAY); } else { //there is no normal available glSecondaryColor3f( 0.0f, 0.0f, 1.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); } } else if (m_colorMayaType[1] == kTangent) { //this set has requested a tangent if ( m_numNormals > 1 && normalCount > 1) { int offset = 0; if (m_numNormals > 2) offset = ( m_colorSetNums[1] * 3 ) + 1; else offset = ( m_colorSetNums[1] * 2 ) + 1; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 2) offset = 1; else offset = -1; } //printf("COLOR1: Access tangents via collection id(%d) = offset %d\n", // m_setNums[ii], offset ); if ((offset > 0) && normalArrays[offset]) { //there is a tangent glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]); glEnableClientState( GL_SECONDARY_COLOR_ARRAY); } else { //there is no tangent available glSecondaryColor3f( 1.0f, 0.0f, 0.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); } } else { //there is no tangent available glSecondaryColor3f( 1.0f, 0.0f, 0.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); } } else if (m_colorMayaType[1] == kBinormal) { //this set has requested a binormal if ( m_numNormals > 2 && normalCount > 1 ) { int offset = ( m_colorSetNums[1] * 3 ) + 2; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 3) offset = 2; else offset = -1; } //printf("COLOR1 : Access binormals via collection id(%d) = offset %d\n", // m_setNums[ii], offset ); if ((offset > 0) && normalArrays[offset] ) { //there is a binormal glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]); glEnableClientState( GL_SECONDARY_COLOR_ARRAY); } else { //there is no binormal available glSecondaryColor3f( 0.0f, 1.0f, 0.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); } } else { //there is no binormal available glSecondaryColor3f( 0.0f, 1.0f, 0.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); } } // Route a color set through color 1 else if (m_colorMayaType[1] == kColorSet) { //this set has requested a color if ( (m_colorSetNums[1] < colorCount) && colorArrays[m_colorSetNums[1]]) { //there is a color glSecondaryColorPointer( 4, GL_FLOAT, 0, colorArrays[m_colorSetNums[1]]); glEnableClientState( GL_SECONDARY_COLOR_ARRAY); } else { //there is no color available glSecondaryColor3f( 1.0f, 1.0f, 1.0f); glDisableClientState( GL_SECONDARY_COLOR_ARRAY); } } #if 0 // Route a uv set through color 1 else if (m_colorMayaType[1] == kUvSet) { //this set has requested a uvset if ( (m_colorSetNums[1] < texCoordCount) && texCoordArrays[m_colorSetNums[1]]) { // For now Maya only supports 2 compont tex coords. glSecondaryColorPointer( 2, GL_FLOAT, 0, texCoordArrays[m_colorSetNums[1]]); glEnableClientState( GL_COLOR_ARRAY); } else { //there is no color available glColor4f( 0.0f, 0.0f, 0.0f, 1.0f); glDisableClientState( GL_COLOR_ARRAY); } } #endif } #endif // _MULTIPLE_COLOR_SET_SUPPORT_ //handle the generic attribute sets for (std::vector::iterator it=m_attributeAttribList.begin(); ithandle = m_shader->attributeHandle(it->pNum); if (it->set == -1) { glDisableVertexAttribArray( it->handle); glVertexAttrib2f( it->handle, 0.0f, 0.0f); continue; } else if (it->mtype == kNormal) { //this set has requested a tangent if (( normalCount > 0) && normalArrays[0]) { //there is a tangent glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[0]); glEnableVertexAttribArray( it->handle); } else { //there is no tangent available glVertexAttrib2f( it->handle, 0.0f, 0.0f); glDisableVertexAttribArray( it->handle); } continue; } else if (it->mtype == kTangent) { //this set has requested a tangent if ( m_numNormals > 1 && normalCount > 1) { int offset = 0; if (m_numNormals > 2) offset = ( it->set * 3 ) + 1; else offset = ( it->set * 2 ) + 1; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 2) offset = 1; else offset = -1; } //printf("VTX_ATTRIB: Access tangents via collection id(%d) = offset %d\n", // it->set, offset ); if ((offset > 0) && normalArrays[offset]) { //there is a tangent glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[offset]); glEnableVertexAttribArray( it->handle); } else { //there is no tangent available glVertexAttrib2f( it->handle, 0.0f, 0.0f); glDisableVertexAttribArray( it->handle); } } else { //there is no tangent available glVertexAttrib2f( it->handle, 0.0f, 0.0f); glDisableVertexAttribArray( it->handle); } continue; } else if (it->mtype == kBinormal) { //this set has requested a binormal if ( m_numNormals > 2 && normalCount > 1) { int offset = ( it->set * 3 ) + 2; // Can occur with swatch rendering. if (offset > normalCount) { if (normalCount >= 3) offset = 2; else offset = -1; } //printf("VTX_ATTRIB : Access binormals via collection id(%d) = offset %d\n", // it->set, offset ); if ((offset > 0) && normalArrays[offset] ) { //there is a binormal glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[offset]); glEnableVertexAttribArray( it->handle); } else { //there is no binormal available glVertexAttrib2f( it->handle, 0.0f, 0.0f); glDisableVertexAttribArray( it->handle); } } else { //there is no binormal available glVertexAttrib2f( it->handle, 0.0f, 0.0f); glDisableVertexAttribArray( it->handle); } continue; } else if (it->mtype == kColorSet) { //this set has requested a color if (( it->set < colorCount ) && colorArrays[it->set]) { //there is a color glVertexAttribPointer( it->handle, 4, GL_FLOAT, GL_FALSE, 0, colorArrays[it->set]); glEnableClientState( it->handle ); } else { //there is no color available glVertexAttrib4f( it->handle, 0.0f, 0.0f, 0.0f, 1.0f); glDisableVertexAttribArray( it->handle); } continue; } //now deal with regular sets if (( it->set < texCoordCount ) && texCoordArrays[it->set]) { //this set exists glVertexAttribPointer( it->handle, 2, GL_FLOAT, GL_FALSE, 0, texCoordArrays[it->set]); glEnableVertexAttribArray( it->handle); } else { //the set does not exist glVertexAttrib2f( it->handle, 0.0f, 0.0f); glDisableVertexAttribArray( it->handle); } } m_glState.activeTexture( 0); } // // // //////////////////////////////////////////////////////////////////////////////// MStatus glslShaderNode::glGeometry( const MDagPath& shapePath, 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) { if (!(glShadingLanguageSupport && glMultiTextureSupport && glTexture3DSupport && glAsmProgramSupport)) return MStatus::kFailure; // If we're not using the default shader, setup any shape specific // shader parameters if( m_passCount > 0) updateBoundAttributes(shapePath); // this is inefficient for the moment int passCount = m_passCount >= 0 ? m_passCount : 1; for( int jj=0; jj < passCount; jj++) { // If this effect has more than 1 pass, bind the // shader per-pass. If it's a single pass effect, // the pass will have been bound in glBind() if( passCount > 1) { m_shader->setPass( jj); m_shader->bind(); } // Setup any shape dependent uniforms (e.g. matrices) if( m_passCount > 0) { m_shader->setShapeDependentState(); } else { sDefShader->setShapeDependentState(); } //all of the following should come from shader attribute querying // might want to add VBO support for multiple pass shaders glVertexPointer( 3, GL_FLOAT, 0, vertexArray); if (normalCount && normalArrays[0] ) { glNormalPointer( GL_FLOAT, 0, &normalArrays[0][0]); glEnableClientState(GL_NORMAL_ARRAY); } else { glDisableClientState(GL_NORMAL_ARRAY); } #if !defined(_MULTIPLE_COLOR_SET_SUPPORT_) if (colorCount && colorArrays[0] ) { glColorPointer( 4, GL_FLOAT, 0, colorArrays[0]); glEnableClientState(GL_COLOR_ARRAY); } else { glColor4f( 1.0f, 1.0f, 1.0f, 1.0f); glDisableClientState(GL_COLOR_ARRAY); } configureTexCoords( normalCount, normalArrays, texCoordCount, texCoordArrays, 0, NULL ); #else configureTexCoords( normalCount, normalArrays, texCoordCount, texCoordArrays, colorCount, colorArrays ); #endif GL_CHECK; //should probably switch to draw range elements glDrawElements( prim, indexCount, GL_UNSIGNED_INT, indexArray); // If this effect has more than 1 pass, unbind the // shader per-pass (if it's a single pass effect, // the effect will be bound once in glBind() if( m_passCount > 1) { m_shader->unbind(); } GL_CHECK; }// for num passes if( m_passCount > 1) m_shader->setPass(0); return MS::kSuccess; } // // If the the attribute is a shader attribute // mark it dirty, and pass it to the parent //////////////////////////////////////////////////////////////////////////////// MStatus glslShaderNode::connectionMade( const MPlug& plug, const MPlug& otherPlug, bool asSrc ) { //need to grab the root in case we are dealing with a vector const MPlug &root = plug.isChild() ? plug.parent() : plug; //MGlobal::displayInfo(MString("Connection made: ") + root.name()); // // Is this time critical enough to use something like a map, instead or linearly searching all // attribute? Present expectation is for shaders to have significantly fewer than 100 attributes. { for ( std::vector::iterator it = m_uniformAttribList.begin(); it < m_uniformAttribList.end(); it++) { if ( root == it->attrib) { it->dirty = true; //allow the set to proceed using the default method break; } } } { for ( std::vector::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) { if ( root == it->attrib) { it->dirty = true; //allow the set to proceed using the default method break; } } } // Pass to parent class. Must do this to ensure keyframe // animation works ! return MS::kUnknownParameter; } // // If the the attribute is a shader attribute // mark it dirty, and pass it to the parent //////////////////////////////////////////////////////////////////////////////// MStatus glslShaderNode::connectionBroken( const MPlug& plug, const MPlug& otherPlug, bool asSrc ) { //need to grab the root in case we are dealing with a vector const MPlug &root = plug.isChild() ? plug.parent() : plug; //MGlobal::displayInfo(MString("Connection broken: ") + root.name()); // // Is this time critical enough to use something like a map, instead or linearly searching all // attribute? Present expectation is for shaders to have significantly fewer than 100 attributes. { for ( std::vector::iterator it = m_uniformAttribList.begin(); it < m_uniformAttribList.end(); it++) { if ( root == it->attrib) { it->dirty = true; //allow the set to proceed using the default method break; } } } { for ( std::vector::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) { if ( root == it->attrib) { it->dirty = true; //allow the set to proceed using the default method break; } } } // Pass to parent class. Must do this to ensure keyframe // animation works ! return MS::kUnknownParameter; } // // // //////////////////////////////////////////////////////////////////////////////// /* virtual */ bool glslShaderNode::getInternalValueInContext( const MPlug &plug, MDataHandle &handle, MDGContext& ) { bool retVal = false; if ( plug == sShader) { handle.set(m_shaderName); retVal = true; } else if ( plug == sTechnique) { handle.set(m_techniqueName); retVal = true; } else if ( plug == sTechniqueList ) { handle.set(m_techniqueList); retVal = true; } else if ( plug == sShaderPath ) { handle.set(m_shaderPath); retVal = true; } else { int ii; for (ii=0; ii<8; ii++) { if ( plug == sNakedTexCoords[ii]) { handle.set(m_nakedTexSets[ii]); retVal = true; break; } } #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ for (ii=0; ii<2; ii++) { if (plug == sNakedColors[ii]) { handle.set(m_nakedColorSets[ii]); retVal = true; break; } } #endif //handle the generic attribute sets for (std::vector::iterator it=m_attributeAttribList.begin(); itattrib) { handle.set(it->setName); retVal = true; break; } } } return retVal; } // // // //////////////////////////////////////////////////////////////////////////////// /* virtual */ bool glslShaderNode::setInternalValueInContext( const MPlug &plug, const MDataHandle &handle, MDGContext & ) { bool retVal = false; //if reading file, short circuit things, we'll clean up post load bool reading = MFileIO::isReadingFile(); if (reading) m_shader = NULL; //MGlobal::displayInfo(MString("Plug update: ") + plug.name()); if ( plug == sShader) { m_shaderName = handle.asString(); m_shaderPath = m_shaderName; if (locateFile( m_shaderName, m_shaderPath)) { // Set shader name to be full path so that we // can access a fully qualified name from the UI (e.g. // for reloading, viewing and editing the fx file. // m_shaderDirty = true; if (!reading) { #ifdef _HARDWARE_RENDERER_AVAILABLE_ if (MGlobal::mayaState() == MGlobal::kBatch) rebuildShader(); else { MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer(); if (pRenderer) { const MString & backEndStr = pRenderer->backEndString(); MStatus status = pRenderer->makeResourceContextCurrent( backEndStr ); if (status != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to make resource context current: ")); MGlobal::displayError(status.errorString()); } else rebuildShader(); } } #else rebuildShader(); #endif //_HARDWARE_RENDERER_AVAILABLE_ } } else { MGlobal::displayError(MString("Couldn't find shader file: '") + m_shaderPath + "'"); } retVal = true; } else if ( plug == sTechnique) { MString temp = handle.asString(); // We are in file read and m_shader has not been built yet, // so don't print out an error message here. // if (reading) { m_techniqueName = temp; retVal = true; } else { if (m_shader) { for (int ii=0; iitechniqueCount(); ii++) { if (temp == m_shader->techniqueName(ii)) { m_shader->setTechnique(ii); retVal = true; break; } } } if (retVal) m_techniqueName = temp; else { MGlobal::displayWarning(MString("Unknown technique: ") + temp + MString(" for shader: ") + m_shaderPath.asChar()); } } retVal = true; } else if ( plug == sTechniqueList ) { // Do nothing } else { int ii; //handle the naked texture coord sets for (ii=0; ii<8; ii++) { if (plug == sNakedTexCoords[ii]) { m_nakedTexSets[ii] = handle.asString(); //MGlobal::displayInfo( MString("Setting Tex ") + ii + " to " + m_nakedTexSets[ii]); return true; } } #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ for (ii=0; ii<2; ii++) { if (plug == sNakedColors[ii]) { m_nakedColorSets[ii] = handle.asString(); //MGlobal::displayInfo( MString("Setting Color ") + ii + " to " + m_nakedColorSets[ii]); return true; } } #endif //handle the generic attribute sets for (std::vector::iterator it=m_attributeAttribList.begin(); itattrib) { it->setName = handle.asString(); return true; } } if (!reading) { //process dynamic attributes //need to grab the root in case we are dealing with a vector const MPlug &root = plug.isChild() ? plug.parent() : plug; //MGlobal::displayInfo(MString("Root name: ") + root.name()); // // Is this time critical enough to use something like a map, instead or linearly searching all // attribute? Present expectation is for shaders to have significantly fewer than 100 attributes. for ( std::vector::iterator it2 = m_uniformAttribList.begin(); it2 < m_uniformAttribList.end(); it2++) { if ( ( root == it2->attrib) || ( root == it2->attrib2) ){ it2->dirty = true; //allow the set to proceed using the default method break; } } for ( std::vector::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) { if (root == it->attrib) { it->dirty = true; //allow the set to proceed using the default method break; } if (root == it->anisoAttrib) { it->dirty = true; it->maxAniso = handle.asFloat(); //allow the set to proceed using the default method break; } if (root == it->magFilterAttrib) { it->dirty = true; switch (handle.asShort()) { case 0: it->magFilter = GL_NEAREST; break; case 1: it->magFilter = GL_LINEAR; break; default: //this should be impossible it->magFilter = GL_LINEAR; break; }; //allow the set to proceed using the default method break; } if (root == it->minFilterAttrib) { it->dirty = true; switch (handle.asShort()) { case 0: it->minFilter = GL_NEAREST; break; case 1: it->minFilter = GL_LINEAR; break; case 2: it->minFilter = GL_LINEAR_MIPMAP_NEAREST; break; case 3: it->minFilter = GL_LINEAR_MIPMAP_LINEAR; break; default: //this should be impossible it->minFilter = GL_LINEAR; break; }; //allow the set to proceed using the default method break; } if (root == it->wrapAttrib) { it->dirty = true; switch (handle.asShort()) { case 0: it->wrapMode = GL_REPEAT; break; case 1: it->wrapMode = GL_CLAMP_TO_EDGE; break; case 2: it->wrapMode = GL_MIRRORED_REPEAT; break; default: //this should be impossible it->wrapMode = GL_REPEAT; break; }; //allow the set to proceed using the default method break; } } } } GL_CHECK; return retVal; } void glslShaderNode::copyInternalData( MPxNode* pMPx ) { glslShaderNode* pNode = dynamic_cast( pMPx ); m_shader = NULL; m_shaderDirty = false; // These get rebuilt m_uniformAttribList.clear(); m_samplerAttribList.clear(); m_attributeAttribList.clear(); m_boundUniformList.clear(); // Reset temporaries m_parsedUserAttribList = false; m_numUVsets = 0; m_uvSetNames.clear(); m_numColorSets = 0; m_colorSetNames.clear(); m_numNormals = 3; m_maxSetNum = pNode->m_maxSetNum; if( pNode ) { for(int ii=0; ii<8; ii++) { m_nakedTexSets[ii] = pNode->m_nakedTexSets[ii]; m_setNums[ii] = pNode->m_setNums[ii]; m_mayaType[ii] = pNode->m_mayaType[ii]; } #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ m_nakedColorSets[0] = pNode->m_nakedColorSets[0]; m_nakedColorSets[1] = pNode->m_nakedColorSets[1]; m_colorSetNums[0] = pNode->m_colorSetNums[0]; m_colorSetNums[1] = pNode->m_colorSetNums[1]; m_colorMayaType[0] = pNode->m_colorMayaType[0]; m_colorMayaType[1] = pNode->m_colorMayaType[1]; #endif m_shaderName = pNode->m_shaderName; m_shaderPath = m_shaderName; m_techniqueList = ""; m_techniqueName = ""; if (locateFile( m_shaderName, m_shaderPath)) { // Must be done before rebuild to set the correct technique m_techniqueName = pNode->m_techniqueName; m_shaderDirty = true; #ifdef _HARDWARE_RENDERER_AVAILABLE_ MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer(); if (pRenderer) { const MString & backEndStr = pRenderer->backEndString(); MStatus status = pRenderer->makeResourceContextCurrent( backEndStr ); if (status != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to make resource context current: ")); MGlobal::displayError(status.errorString()); } else rebuildShader(); } else rebuildShader(); #else rebuildShader(); #endif //_HARDWARE_RENDERER_AVAILABLE_ } } } // //attribute request interface // //////////////////////////////////////////////////////////////////////////////// int glslShaderNode::colorsPerVertex() { return 1; } // // // //////////////////////////////////////////////////////////////////////////////// int glslShaderNode::texCoordsPerVertex() { return 0; } #if MAYA_API_VERSION >= 700 // There are two versions of parseUserAttributeList, so the plug-in // can work on multiple revisions of Maya. The version for 700 and // above relies on the presence of the named color sets, and a fix // to a bug in the Maya API. // // // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::parseUserAttributeList() { if (m_parsedUserAttribList) return; // Reset shared data which is used by getColorSetNames() and // getTexCoordSetNames // m_numUVsets = 0; m_numColorSets = 0; m_uvSetNames.clear(); m_colorSetNames.clear(); m_numNormals = 3; MStringArray uvSetNames; MStringArray colorSetNames; bool haveNurbs = false; // "Magic" name for nurbs uvs MString nurbsDefaultUV("Nurbs Tri UVs"); const MDagPath & path = currentPath(); if (path.hasFn( MFn::kMesh ) ) { // Check the # of uvsets MFnMesh fnMesh( path.node() ); if (fnMesh.numUVSets()) fnMesh.getUVSetNames(uvSetNames); // Check the # of color sets #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ if (fnMesh.numColorSets()) fnMesh.getColorSetNames(colorSetNames); #endif } else // is a NURBS { haveNurbs = true; uvSetNames.append(nurbsDefaultUV); // There is no such thing as color sets no nurbs // for now, so don't add any "default" } // // The code here needs to work as follows: // 1. Add nurbs uv set if it is a nurbs. // 2. Iterate the list of requested maps // 3. Check for special maps (tangent/binormal) // 4. If the map is not yet added, add it // 5. Record the index int ii = 0; for (; ii<8; ii++) { m_setNums[ii] = -1; m_mayaType[ii] = kNonMaya; } // Fixed splitting character to find qualifiers for inputs. // For now only used to scope the tangent, and binormal to // a given tangent or binormal set. // const char _splitChar = ':'; ii = 0; for (; ii<8; ii++) { MStringArray splitStrings; MString comparisonString = m_nakedTexSets[ii]; if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) && splitStrings.length() > 1) { comparisonString = splitStrings[0]; } // Special case nurbs. If "map1" is specified then // append the "special" uv set name for nurbs instead if (haveNurbs && comparisonString == "map1") { m_setNums[ii] = m_uvSetNames.length(); m_uvSetNames.append(nurbsDefaultUV); m_mayaType[ii] = kUvSet; continue; } else if (comparisonString == "normal") { m_setNums[ii] = 0; // Normal is currently always for set 0 m_mayaType[ii] = kNormal; continue; } else if (comparisonString == "tangent") { m_setNums[ii] = 0; // Tangent is currently always for set 0 m_mayaType[ii] = kTangent; continue; } // Note that for NURBS there is currently an API bug // in that you cannot get the binormals even when you // ask for them. else if (comparisonString == "binormal") { m_setNums[ii] = 0; // Binormal is currently always for set 0 m_mayaType[ii] = kBinormal; continue; } else if (comparisonString == "") { m_setNums[ii] = -1; m_mayaType[ii] = kNonMaya; continue; } else { // Test out uv set name matches first bool matchUVSet = false; unsigned int numNames = uvSetNames.length(); if (numNames) { unsigned int jj = 0; for (jj=0; jj 1) { comparisonString = splitStrings[0]; } if (comparisonString == "normal") { m_colorSetNums[ii] = 0; // Normal is currently always for set 0 m_colorMayaType[ii] = kNormal; continue; } else if (comparisonString == "tangent") { m_colorSetNums[ii] = 0; // Tangent is currently always for set 0 m_colorMayaType[ii] = kTangent; continue; } // Note that for NURBS there is currently an API bug // in that you cannot get the binormals even when you // ask for them. else if (comparisonString == "binormal") { m_colorSetNums[ii] = 0; // Binormal is currently always for set 0 m_colorMayaType[ii] = kBinormal; continue; } else if (comparisonString == "") { m_colorSetNums[ii] = -1; m_colorMayaType[ii] = kNonMaya; continue; } else { // Test out color set name matches first. // Basically the opposite of uv set lookup. // bool matchUVSet = false; // Test out color set name matches unsigned int numNames = colorSetNames.length(); if (numNames) { unsigned int jj = 0; for (jj=0; jj::iterator it = m_attributeAttribList.begin(); itsetName ); if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) && splitStrings.length() > 1) { comparisonString = splitStrings[0]; } if (comparisonString == "normal") { it->mtype = kNormal; it->set = 0; continue; } else if (comparisonString == "tangent") { it->mtype = kTangent; it->set = 0; continue; } else if (comparisonString == "binormal") { it->mtype = kBinormal; it->set = 0; continue; } else if (comparisonString == "") { it->set = -1; continue; } else { // Test out uv set name matches first bool matchUVSet = false; unsigned int numNames = uvSetNames.length(); if (numNames) { unsigned int jj = 0; for (jj=0; jjset = m_uvSetNames.length(); m_uvSetNames.append( uvSetNames[jj] ); it->mtype = kUvSet; matchUVSet = true; break; } } } // Test out color set name matches second if (!matchUVSet) { numNames = colorSetNames.length(); if (numNames) { unsigned int jj = 0; for (jj=0; jjset = m_colorSetNames.length(); it->mtype = kColorSet; break; } } } } } } ///////////////////////////////////////////////////////////////////////////////////////// // Do a post-scan to see if a specific uvset is specified for tangent // and binormal in the uv set entries. // //printf("----------------------- POST SCAN TEXCOORDS --------------------\n"); unsigned int numUVSets = uvSetNames.length(); if (numUVSets) { for (ii=0; ii<8; ii++) { if (m_mayaType[ii] == kTangent || m_mayaType[ii] == kBinormal) { MStringArray splitStrings; MString origString( m_nakedTexSets[ii] ); //printf("Attempt to split the TEXCOORD string[%d] = %s.\n", // ii, origString.asChar()); if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) && splitStrings.length() > 1) { // Look for a match of existing uvset names //printf("--- Managed to split the string[%d]. Look for uvset %s\n", // ii, splitStrings[1].asChar()); unsigned int jj = 0; for (jj=0; jj 0) { // Set number is index into uvset name array. m_setNums[ii] = foundSet; } else { // Append a new uvset name, and point to end of // uvset name array. m_setNums[ii] = m_uvSetNames.length(); m_uvSetNames.append( splitStrings[1] ); } //printf("---- Add uvset Index=%d. uvset name %s\n", m_setNums[ii], // splitStrings[1].asChar()); break; } } } } } } // Do a post-scan to see if a specific uvset is specified for tangent // and binormal in the color set entries. // // printf("----------------------- POST SCAN COLORS --------------------\n"); if (numUVSets) { for (ii=0; ii<2; ii++) { if (m_colorMayaType[ii] == kTangent || m_colorMayaType[ii] == kBinormal) { MStringArray splitStrings; MString origString( m_nakedColorSets[ii] ); //printf("Attempt to split the COLOR string[%d] = %s.\n", // ii, origString.asChar()); if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) && splitStrings.length() > 1) { // Look for a match of existing uvset names unsigned int jj = 0; for (jj=0; jj 0) { // Set number is index into uvset name array. m_colorSetNums[ii] = foundSet; } else { // Append a new uvset name, and point to end of // uvset name array. m_colorSetNums[ii] = m_uvSetNames.length(); m_uvSetNames.append( splitStrings[1] ); } //printf("---- Add uvset Index=%d. uvset name %s\n", m_colorSetNums[ii], // splitStrings[1].asChar()); break; } } } } } } // Do a post-scan to see if a specific uvset is specified for tangent // and binormal in the generic entries. // if (numUVSets) { for (std::vector::iterator it2 = m_attributeAttribList.begin(); it2mtype == kTangent || it2->mtype == kBinormal) { MStringArray splitStrings; MString origString( it2->setName ); if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) && splitStrings.length() > 1) { // Look for a match of existing uvset names unsigned int jj = 0; for (jj=0; jj 0) { // Set number is index into uvset name array. it2->set = foundSet; } else { // Append a new uvset name, and point to end of // uvset name array. it2->set = m_uvSetNames.length(); m_uvSetNames.append( splitStrings[1] ); } //printf("---- uvset Index=%d. uvset name %s\n", it->set, // splitStrings[1].asChar()); break; } } } } } } m_numUVsets = m_uvSetNames.length(); m_numColorSets = m_colorSetNames.length(); m_maxSetNum = 0; for ( ii = 0; ii<8; ii++) if( m_mayaType[ii] != kNonMaya) m_maxSetNum = ii + 1; m_parsedUserAttribList = true; } #else // if MAYA_7 // // // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::parseUserAttributeList() { if (m_parsedUserAttribList) return; // Reset shared data which is used by getColorSetNames() and // getTexCoordSetNames // m_numUVsets = 0; m_numColorSets = 0; m_uvSetNames.clear(); m_colorSetNames.clear(); // // Always requesting a default set of map1 first, works around // an annyoing bug seen in Maya 6.5. // m_uvSetNames.append("map1"); int ii = 0; for (; ii<8; ii++) { m_setNums[ii] = -1; m_mayaType[ii] = kNonMaya; } ii = 0; for (; ii<8; ii++) { // Special case, grab the special set from set 0 if ( m_nakedTexSets[ii] == "map1") { m_setNums[ii] = 0; m_mayaType[ii] = kUvSet; continue; } else if (m_nakedTexSets[ii] == "normal") { m_setNums[ii] = 0; // Normal is currently always for set 0 m_mayaType[ii] = kNormal; continue; } else if (m_nakedTexSets[ii] == "tangent") { m_setNums[ii] = 0; // Tangent is currently always for set 0 m_mayaType[ii] = kTangent; continue; } // Note that for NURBS there is currently an API bug // in that you cannot get the binormals even when you // ask for them. else if (m_nakedTexSets[ii] == "binormal") { m_setNums[ii] = 0; // Binormal is currently always for set 0 m_mayaType[ii] = kBinormal; continue; } else if (m_nakedTexSets[ii] == "") { m_setNums[ii] = -1; m_mayaType[ii] = kNonMaya; continue; } else { //just append the set m_setNums[ii] = m_uvSetNames.length(); m_uvSetNames.append( m_nakedTexSets[ii] ); m_mayaType[ii] = kUvSet; } } //now handle the generic attributes unsigned int setNum = 0; for (std::vector::iterator it = m_attributeAttribList.begin(); itsetName == "normal") { it->mtype = kNormal; it->set = 0; continue; } else if (it->setName == "tangent") { it->mtype = kTangent; it->set = 0; continue; } else if (it->setName == "binormal") { it->mtype = kBinormal; it->set = 0; continue; } else if (it->setName == "") { it->set = -1; continue; } else { //just append the set it->set = m_uvSetNames.length(); m_uvSetNames.append( it->setName ); it->mtype = kUvSet; } } m_numUVsets = m_uvSetNames.length(); m_numColorSets = m_colorSetNames.length(); m_maxSetNum = 0; for ( ii = 0; ii<8; ii++) if( m_mayaType[ii] != kNonMaya) m_maxSetNum = ii + 1; m_parsedUserAttribList = true; } #endif // if MAYA_7 #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ // // // //////////////////////////////////////////////////////////////////////////////// int glslShaderNode::getColorSetNames(MStringArray& names) { if (!m_parsedUserAttribList) parseUserAttributeList(); names = m_colorSetNames; return m_numColorSets; } #endif // // // //////////////////////////////////////////////////////////////////////////////// int glslShaderNode::getTexCoordSetNames( MStringArray &names) { if (!m_parsedUserAttribList) parseUserAttributeList(); names = m_uvSetNames; return m_numUVsets; } // // // //////////////////////////////////////////////////////////////////////////////// int glslShaderNode::normalsPerVertex() { // this can be revisited, now that we have dynamic attributes m_numNormals = 3; return m_numNormals; } // // // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::configureUniformAttrib( MString &name, int pNum, shader::DataType type, shader::Semantic sm, float *defVal, MFnDependencyNode &dn, MDGModifier &mod) { uniformAttrib ua; MFnNumericAttribute nAttrib; MFnTypedAttribute tAttrib; MStatus stat; //set the generic attribute values ua.name = name; ua.pNum = pNum; ua.dirty = true; ua.type = type; ua.attrib = MObject::kNullObj; ua.attrib2 = MObject::kNullObj; // [ANIM_UNIFORM_FIX1] : // Note that all uniforms are not set as being internal as this // makes animation playback unreliable. [See related code for binding // unforms [ANIM_UNIFORM_FIX2]] //obtain relavent information float min=0.0f, max=0.0f; bool color = shader::isColor(sm); bool clamped = shader::isClamped(sm); shader::getLimits( sm, min, max); switch (ua.type) { case shader::dtFloat: if ( !dn.hasAttribute( name )) { ua.attrib = nAttrib.create( name, name, MFnNumericData::kFloat); if (clamped) { nAttrib.setMin(min); nAttrib.setMax(max); } #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[0]); else nAttrib.setDefault( -1.0 ); stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtVec2: if ( !dn.hasAttribute( name )) { ua.attrib = nAttrib.create( name, name, MFnNumericData::k2Float); if (clamped) { nAttrib.setMin( min, min); nAttrib.setMax( max, max); } #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[0], defVal[1] ); else nAttrib.setDefault( -1.0, -1.0 ); stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtVec3: if ( !dn.hasAttribute( name )) { if (color) { ua.attrib = nAttrib.createColor( name, name); #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[0], defVal[1], defVal[2] ); } else { ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Float); if (clamped) { nAttrib.setMin( min, min, min); nAttrib.setMax( max, max, max); } #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[0], defVal[1], defVal[2] ); else nAttrib.setDefault( -1.0, -1.0, -1.0 ); } stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtVec4: if ( !dn.hasAttribute( name )) { if (color) { ua.attrib = nAttrib.createColor( name, name); #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[0], defVal[1], defVal[2] ); stat = mod.addAttribute( thisMObject(), ua.attrib ); ua.attrib2 = nAttrib.create( name + "Alpha", name + "Alpha", MFnNumericData::kFloat); if (clamped) { nAttrib.setMin( 0.0f); nAttrib.setMax( 1.0f); } #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[3]); else nAttrib.setDefault( 1.0 ); stat = mod.addAttribute( thisMObject(), ua.attrib2 ); } else { ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Float); if (clamped) { nAttrib.setMin( min, min, min); nAttrib.setMax( max, max, max); } #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[0], defVal[1], defVal[2] ); else nAttrib.setDefault( -1.0, -1.0, -1.0 ); stat = mod.addAttribute( thisMObject(), ua.attrib ); ua.attrib2 = nAttrib.create( name + "W", name + "W", MFnNumericData::kFloat); if (clamped) { nAttrib.setMin( min); nAttrib.setMax( max); } #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( defVal[3]); else nAttrib.setDefault( -1.0 ); stat = mod.addAttribute( thisMObject(), ua.attrib2 ); } } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); ua.attrib2 = dn.attribute( name + "W", &stat); } break; case shader::dtBool: if ( !dn.hasAttribute( name )) { ua.attrib = nAttrib.create( name, name, MFnNumericData::kBoolean); #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); nAttrib.setDefault( true ); //Will this work? stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtInt: if ( !dn.hasAttribute( name )) { ua.attrib = nAttrib.create( name, name, MFnNumericData::kInt); #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); if (defVal) nAttrib.setDefault( (int)defVal[0]); else nAttrib.setDefault( -1 ); stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtIVec2: if ( !dn.hasAttribute( name )) { ua.attrib = nAttrib.create( name, name, MFnNumericData::k2Int); #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); nAttrib.setDefault( -1, -1 ); stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtIVec3: if ( !dn.hasAttribute( name )) { ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Int); #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS nAttrib.setInternal(true); #endif nAttrib.setKeyable( true ); nAttrib.setDefault( -1, -1, -1 ); stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtMat4: if ( !dn.hasAttribute( name )) { MFnMatrixAttribute mAttrib; ua.attrib = mAttrib.create( name, name, MFnMatrixAttribute::kFloat); #ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS mAttrib.setInternal(true); #endif mAttrib.setKeyable( true ); //How do I set default???? stat = mod.addAttribute( thisMObject(), ua.attrib ); } else { //just grab it, need to make sure it is correct ua.attrib = dn.attribute(name, &stat); } break; case shader::dtBVec2: case shader::dtBVec3: case shader::dtIVec4: case shader::dtBVec4: case shader::dtMat2: case shader::dtMat3: //not yet supported, primarly due to no native Maya type return; default: MGlobal::displayWarning(MString("Unknown uniform parameter type for: ") + name); }; if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to add attribute: ") + name); MGlobal::displayError(stat.errorString()); } m_uniformAttribList.push_back(ua); //will this cause reference counting problems? } // // // //////////////////////////////////////////////////////////////////////////////// void glslShaderNode::configureAttribs() { MDGModifier mod; { for (std::vector::iterator it=m_uniformAttribList.begin(); itpNum = -1; //mark it as unused } } { for (std::vector::iterator it=m_samplerAttribList.begin(); itpNum = -1; //mark it as unused } } { for (std::vector::iterator it=m_attributeAttribList.begin(); itpNum = -1; //mark it as unused } } //no need to worry about reattachment with these, they don't create attributes m_boundUniformList.clear(); MFnDependencyNode dn(thisMObject()); int ii; if (m_shader && m_shader->valid()) { //find the uniforms that the new shader uses for (ii=0; iiuniformCount(); ii++) { MString name(m_shader->uniformName(ii)); //determine if it is bound or not if ( shader::isBound(m_shader->uniformSemantic(ii))) { //handle bound uniforms //add a new one to the list boundUniform bu; bu.name = name; bu.usage = m_shader->uniformSemantic(ii); bu.pNum = ii; m_boundUniformList.push_back(bu); } else { //handle uniform attributes (tweakables) //iterate the list to find it std::vector::iterator it=m_uniformAttribList.begin(); //might want to store this stuff in a map instead while (it < m_uniformAttribList.end()) { if (it->name == name) break; //found it it++; // look at the next } //see if we found the correct attribute if (it !=m_uniformAttribList.end()) { // make sure we have the same type if ( it->type == m_shader->uniformType(ii)) { //record the handle, and mark it as dirty, so we update it it->pNum = ii; it->dirty = true; //process the next attribute continue; } else { //need to delete the old one here, otherwise we can't create the new one mod.removeAttribute( thisMObject(), it->attrib); it->attrib = MObject::kNullObj; //check to see if it was a vec4, and kill the last component if (it->attrib2 != MObject::kNullObj) { mod.removeAttribute( thisMObject(), it->attrib2); it->attrib2 = MObject::kNullObj; } } } //if there was no appropriate attribute create a new one and add it configureUniformAttrib( name, ii, m_shader->uniformType(ii), m_shader->uniformSemantic(ii), m_shader->uniformDefault(ii), dn, mod); GL_CHECK; } } //samplers for (ii=0; iisamplerCount(); ii++) { MString name(m_shader->samplerName(ii)); //iterate the list to find it std::vector::iterator it=m_samplerAttribList.begin(); //might want to store this stuff in a map instead while (it < m_samplerAttribList.end()) { if (it->name == name) break; //found it it++; // look at the next } if (it !=m_samplerAttribList.end()) { //if found, set the pNum to point to this one it->pNum = ii; it->dirty = false; m_shader->updateSampler( ii, it->texName); } else { //if not found, create a new one and add it samplerAttrib sa; MFnNumericAttribute nAttrib; MFnEnumAttribute eAttrib; MStatus stat; sa.name = name; sa.pNum = ii; sa.dirty = true; sa.texName = 0; sa.minFilter = GL_LINEAR; sa.magFilter = GL_LINEAR; sa.wrapMode = GL_REPEAT; sa.maxAniso = 1.0f; if ( !dn.hasAttribute( name )) { sa.attrib = nAttrib.createColor( name, name); nAttrib.setInternal(true); stat = mod.addAttribute( thisMObject(), sa.attrib); } else { sa.attrib = dn.attribute(name, &stat); } if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to add attribute: ") + name); MGlobal::displayError(stat.errorString()); } if ( !dn.hasAttribute( name +"MinFilter")) { sa.minFilterAttrib = eAttrib.create( name+"MinFilter", name+"MinFilter"); eAttrib.addField( "Nearest", 0); eAttrib.addField( "Linear", 1); eAttrib.addField( "Linear, Mipmaped", 2); eAttrib.addField( "Trilinear", 3); eAttrib.setDefault( 1); eAttrib.setInternal(true); stat = mod.addAttribute( thisMObject(), sa.minFilterAttrib); } else { sa.minFilterAttrib = dn.attribute(name+"MinFilter", &stat); //need to query the filter attribute MPlug plug(thisMObject(), sa.minFilterAttrib); short val; plug.getValue(val); switch (val) { case 0: sa.minFilter = GL_NEAREST; break; case 1: sa.minFilter = GL_LINEAR; break; case 2: sa.minFilter = GL_LINEAR_MIPMAP_NEAREST; break; case 3: sa.minFilter = GL_LINEAR_MIPMAP_LINEAR; break; default: //this should be impossible sa.minFilter = GL_LINEAR; break; }; } if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to add attribute: ") + name+"MinFilter"); MGlobal::displayError(stat.errorString()); } if ( !dn.hasAttribute( name +"MagFilter")) { sa.magFilterAttrib = eAttrib.create( name+"MagFilter", name+"MagFilter"); eAttrib.addField( "Nearest", 0); eAttrib.addField( "Linear", 1); eAttrib.setDefault( 1); eAttrib.setInternal(true); stat = mod.addAttribute( thisMObject(), sa.magFilterAttrib); } else { sa.magFilterAttrib = dn.attribute(name+"MagFilter", &stat); //need to query the filter attribute MPlug plug(thisMObject(), sa.magFilterAttrib); short val; plug.getValue(val); switch (val) { case 0: sa.magFilter = GL_NEAREST; break; case 1: sa.magFilter = GL_LINEAR; break; default: //this should be impossible sa.magFilter = GL_LINEAR; break; }; } if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to add attribute: ") + name+"MagFilter"); MGlobal::displayError(stat.errorString()); } if ( !dn.hasAttribute( name+"Wrap" )) { sa.wrapAttrib = eAttrib.create( name+"Wrap", name+"Wrap"); eAttrib.addField( "Repeat", 0); eAttrib.addField( "Clamp", 1); eAttrib.addField( "Mirror", 2); eAttrib.setDefault( 0); eAttrib.setInternal(true); stat = mod.addAttribute( thisMObject(), sa.wrapAttrib); } else { sa.wrapAttrib = dn.attribute(name+"Wrap", &stat); //need to query the filter attribute MPlug plug(thisMObject(), sa.wrapAttrib); short val; plug.getValue(val); switch (val) { case 0: sa.wrapMode = GL_REPEAT; break; case 1: sa.wrapMode = GL_CLAMP_TO_EDGE; break; case 2: sa.wrapMode = GL_MIRRORED_REPEAT; break; default: //this should be impossible sa.wrapMode = GL_REPEAT; break; }; } if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to add attribute: ") + name+"Wrap"); MGlobal::displayError(stat.errorString()); } if ( !dn.hasAttribute( name+"MaxAniso" )) { sa.anisoAttrib = nAttrib.create( name+"MaxAniso", name+"MaxAniso", MFnNumericData::kFloat, 1.0); nAttrib.setInternal(true); nAttrib.setMin( 1.0); nAttrib.setMax( 16.0); stat = mod.addAttribute( thisMObject(), sa.anisoAttrib); } else { sa.anisoAttrib = dn.attribute(name+"MaxAniso", &stat); //need to query the filter attribute MPlug plug(thisMObject(), sa.anisoAttrib); float val; plug.getValue(val); sa.maxAniso = val; } if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to add attribute: ") + name+"MaxAniso"); MGlobal::displayError(stat.errorString()); } m_samplerAttribList.push_back(sa); GL_CHECK; } } //attributes for (ii=0; iiattributeCount(); ii++) { MString name(m_shader->attributeName(ii)); //iterate the list to find it std::vector::iterator it=m_attributeAttribList.begin(); //might want to store this stuff in a map instead while (it < m_attributeAttribList.end()) { if (it->name == name) break; //found it it++; // look at the next } if (it !=m_attributeAttribList.end()) { //if found, set the pNum to point to this one it->pNum = ii; it->handle = m_shader->attributeHandle( ii); it->set = -1; } else { MFnTypedAttribute tAttrib; attributeAttrib aa; MStatus stat; aa.name = name; aa.setName = name; aa.pNum = ii; aa.handle = m_shader->attributeHandle(ii); aa.set = -1; //no set aa.mtype = kNonMaya; if ( !dn.hasAttribute( name )) { aa.attrib = tAttrib.create( name, name, MFnData::kString); tAttrib.setInternal(true); tAttrib.setKeyable( true ); stat = mod.addAttribute( thisMObject(), aa.attrib); } else { aa.attrib = dn.attribute(name, &stat); } m_attributeAttribList.push_back(aa); } } } for (ii=0; ii< (int)m_uniformAttribList.size(); ) { if (m_uniformAttribList[ii].pNum == -1) { //attrib is unused, remove it if (m_uniformAttribList[ii].attrib != MObject::kNullObj ) { mod.removeAttribute( thisMObject(), m_uniformAttribList[ii].attrib); m_uniformAttribList[ii].attrib = MObject::kNullObj; } if ( m_uniformAttribList[ii].attrib2 != MObject::kNullObj ) { mod.removeAttribute( thisMObject(), m_uniformAttribList[ii].attrib2); m_uniformAttribList[ii].attrib2 = MObject::kNullObj; } m_uniformAttribList.erase(m_uniformAttribList.begin()+ii); } else { //move to the next item ii++; } } for (ii=0; ii<(int)m_samplerAttribList.size(); ) { MStatus stat, stat2, stat3, stat4, stat5; if (m_samplerAttribList[ii].pNum == -1) { //attrib is unused, disconnect it and remove it if ( m_samplerAttribList[ii].attrib != MObject::kNullObj ) stat = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].attrib); if ( m_samplerAttribList[ii].magFilterAttrib != MObject::kNullObj ) stat2 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].magFilterAttrib); if ( m_samplerAttribList[ii].minFilterAttrib != MObject::kNullObj ) stat3 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].minFilterAttrib); if ( m_samplerAttribList[ii].wrapAttrib != MObject::kNullObj ) stat4 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].wrapAttrib); if ( m_samplerAttribList[ii].anisoAttrib != MObject::kNullObj ) stat5 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].anisoAttrib); if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to destroy attribute ") + m_samplerAttribList[ii].name); MGlobal::displayError(MString(" ") + stat.errorString()); } //I don't think we have a context for certain here ResourceManager::destroyTexture( m_samplerAttribList[ii].texName); m_samplerAttribList[ii].attrib = MObject::kNullObj; m_samplerAttribList.erase(m_samplerAttribList.begin()+ii); } else { //move to the next item ii++; } } for (ii=0; ii<(int)m_attributeAttribList.size(); ) { MStatus stat; if (m_attributeAttribList[ii].pNum == -1) { //attrib is unused, disconnect it and remove it stat = mod.removeAttribute( thisMObject(), m_attributeAttribList[ii].attrib); if (stat != MStatus::kSuccess) { MGlobal::displayError(MString("Failed to destroy attribute ") + m_attributeAttribList[ii].name); MGlobal::displayError(MString(" ") + stat.errorString()); } m_attributeAttribList[ii].attrib = MObject::kNullObj; m_attributeAttribList.erase(m_attributeAttribList.begin()+ii); } else { //move to the next item ii++; } } GL_CHECK; mod.doIt(); } // // // //////////////////////////////////////////////////////////////////////////////// bool glslShaderNode::locateFile( const MString &name, MString &path) { MString proj; struct stat fstats; MString temp; // // I swear, there must be a better way MGlobal::executeCommand( MString("workspace -q -rd;"), proj); //search order: // current directory // project/shaders directory // project directory // Try absolute path first if ( stat(name.asChar(), &fstats) == 0){ //found it in the current path path = name; return true; } // Get the current working directory and // try relative path char buffer[MAX_PATH_LENGTH]; if ( getcwd( buffer, MAX_PATH_LENGTH ) != NULL) { temp = MString(buffer) + "\\" + name; if ( stat(temp.asChar(), &fstats) == 0){ //found it in the current path path = temp; return true; } } temp = proj + "shaders/" + name; if ( stat(temp.asChar(), &fstats) == 0){ //found it in the project/shaders path path = temp; return true; } temp = proj + name; if ( stat(temp.asChar(), &fstats) == 0){ //found it in the project path path = temp; return true; } return false; } //////////////////////////////////////////////////////////////////////// // Swatch rendering //////////////////////////////////////////////////////////////////////// #ifdef _HARDWARE_RENDERER_AVAILABLE_ /* virtual */ MStatus glslShaderNode::renderSwatchImage( MImage & outImage ) { MStatus status = MStatus::kFailure; // Get the hardware renderer utility class MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer(); if (pRenderer) { const MString& backEndStr = pRenderer->backEndString(); // Get geometry // ============ unsigned int* pIndexing = 0; unsigned int numberOfData = 0; unsigned int indexCount = 0; MHardwareRenderer::GeometricShape gshape = MHardwareRenderer::kDefaultSphere; MGeometryData* pGeomData = pRenderer->referenceDefaultGeometry( gshape, numberOfData, pIndexing, indexCount ); if( !pGeomData ) { return MStatus::kFailure; } // Make the swatch context current // =============================== // unsigned int width, height; outImage.getSize( width, height ); unsigned int origWidth = width; unsigned int origHeight = height; MStatus status2 = pRenderer->makeSwatchContextCurrent( backEndStr, width, height ); if( status2 != MS::kSuccess ) { pRenderer->dereferenceGeometry( pGeomData, numberOfData ); return status2; } // Get the light direction from the API, and use it // ============================================= { float light_pos[4]; pRenderer->getSwatchLightDirection( light_pos[0], light_pos[1], light_pos[2], light_pos[3] ); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glLightfv(GL_LIGHT0, GL_POSITION, light_pos ); glPopMatrix(); float light_ambt[4] = {1.0f, 1.0f, 1.0f, 1.0}; float light_diff[4] = {1.0f, 1.0f, 1.0f, 1.0}; float light_spec[4] = {1.0f, 1.0f, 1.0f, 1.0}; glLightfv( GL_LIGHT0, GL_AMBIENT, light_ambt ); glLightfv( GL_LIGHT0, GL_DIFFUSE, light_diff ); glLightfv( GL_LIGHT0, GL_SPECULAR, light_spec ); glEnable( GL_LIGHTING ); glEnable( GL_LIGHT0 ); GL_CHECK; } // Get camera // ========== { // Get the camera frustum from the API glMatrixMode(GL_PROJECTION); glLoadIdentity(); double l, r, b, t, n, f; pRenderer->getSwatchPerspectiveCameraSetting( l, r, b, t, n, f ); glFrustum( l, r, b, t, n, f ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float x, y, z, w; pRenderer->getSwatchPerspectiveCameraTranslation( x, y, z, w ); glTranslatef( x, y, z ); GL_CHECK; } // Get the default background color and clear the background // float r, g, b, a; MHWShaderSwatchGenerator::getSwatchBackgroundColor( r, g, b, a ); glClearColor( r, g, b, a ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); GL_CHECK; // Draw The Swatch // =============== //drawTheSwatch( pGeomData, pIndexing, numberOfData, indexCount ); MDagPath dummyPath; glBind( dummyPath ); float *vertexData = (float *)( pGeomData[0].data() ); float *normalData = (float *)( pGeomData[1].data() ); float *uvData = (float *)( pGeomData[2].data() ); float *tangentData = (float *)( pGeomData[3].data() ); float *binormalData = (float *)( pGeomData[4].data() ); unsigned int normalCount = 0; // Stick normal, tangent, binormals into ptr array float ** normalArrays = new float * [3]; if (normalData) { normalArrays[0] = normalData; normalCount++; } else normalArrays[0] = NULL; if (tangentData) { normalArrays[1] = tangentData; normalCount++; } else normalArrays[1] = NULL; if (binormalData) { normalArrays[2] = binormalData; normalCount++; } else normalArrays[2] = NULL; // Stick uv data into ptr array unsigned int uvCount = 0; float ** texCoordArrays = new float * [1]; if (uvData) { texCoordArrays[0] = uvData; uvCount = 1; } else texCoordArrays[0] = NULL; m_setNums[0] = 0; m_setNums[1] = 0; m_setNums[2] = 0; m_setNums[3] = 0; m_setNums[4] = 0; m_setNums[5] = 0; m_setNums[6] = 0; m_setNums[7] = 0; m_mayaType[0] = kUvSet; m_mayaType[1] = kTangent; m_mayaType[2] = kBinormal; m_mayaType[3] = kUvSet; m_mayaType[4] = kUvSet; m_mayaType[5] = kUvSet; m_mayaType[6] = kUvSet; m_mayaType[7] = kUvSet; #ifdef _MULTIPLE_COLOR_SET_SUPPORT_ // No colours m_colorSetNums[0] = -1; m_colorSetNums[1] = -1; m_colorMayaType[0] = kColorSet; m_colorMayaType[1] = kColorSet; #endif glGeometry( dummyPath, GL_TRIANGLES, false, indexCount, pIndexing, 0, NULL, /* no vertex ids */ vertexData, normalCount, (const float **) normalArrays, 0, NULL, /* no colours */ uvCount, (const float **) texCoordArrays); glUnbind( dummyPath ); glFinish(); normalArrays[0] = NULL; normalArrays[1] = NULL; delete[] normalArrays; texCoordArrays[0] = NULL; delete[] texCoordArrays; // Read pixels back from swatch context to MImage // ============================================== pRenderer->readSwatchContextPixels( backEndStr, outImage ); // Double check the outing going image size as image resizing // was required to properly read from the swatch context outImage.getSize( width, height ); if (width != origWidth || height != origHeight) { status = MStatus::kFailure; } else { status = MStatus::kSuccess; } } GL_CHECK; return status; } #endif // // //////////////////////////////////////////////////////////////////////////////// bool glslShaderNode::printVertexShader( int pass) { if ( m_shader) { const char *shader = m_shader->getVertexShader( pass); if (shader) { MGlobal::displayInfo(shader); return true; } } return false; } // // //////////////////////////////////////////////////////////////////////////////// bool glslShaderNode::printPixelShader( int pass) { if ( m_shader) { const char *shader = m_shader->getPixelShader( pass); if (shader) { MGlobal::displayInfo(shader); return true; } } return false; } // // //////////////////////////////////////////////////////////////////////////////// glslShaderNode* glslShaderNode::findNodeByName(const MString &name) { for (std::vector::iterator it = sNodeList.begin(); it < sNodeList.end(); it++) { if ( (*it)->name() == name) return *it; } return NULL; }