- Main Page
- Related Pages
- Modules
- Classes
- Examples

# hwUnlitShader.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.
    //
    // ==========================================================================
    //+

    // DESCRIPTION: This is a simple shader, that only
    //              supports decal texturing using standard OpenGL commands.
    //
    // How to use this shader:
    //
    //  1. Compile it and put it in your plug-in path.
    //  2. Window->Setting/Preferences->Plug-in Manager.
    //     Make sure that the hwUnlitShader.mll plug-in is there
    //     and click the "loaded" checkbox next to it to load it.
    //  3. Create a new scene.
    //  4. Create a poly object (say, a polygon cube).
    //  5. Create a new hwUnlitShader. You can use the hypershade, or
    //     right-click on the cube and select Material->Create new Material->hwUnlitShader.
    //  6. Open the attribute editor and select the hwUnlitShader you just created.
    //  7. The color attribute can be mapped on a file texture, just like any
    //     standard maya shader. Try it, press '6' to go in textured mode and see the texture.
    //  8. The transparency attribute can be set to a value other than 0.0 to
    //     display transparent color. If the color attribute is textured and
    //     transparency is set, the average of the transparency is modulated
    //     by the texture.
    //
    // COMPATIBILITY NOTE: Before Maya 4.5, a problem prevented Maya from
    // properly using MPxHwShaderNode::hasTransparency(). The work-around is
    // to create transparency attributes that mimics standard Lambert shaders,
    // and to set the transparency value to a color different than black.
    // This forces Maya to properly order geometry from farthest to closest.
    // This shader illustrates both the workaround (the transparency attributes
    // illustrated in initialize() below) and the recommended way (overloading
    // hasTransparency() to return true). If you do not need to support
    // earlier versions of Maya, or do not need transparency, then there is
    // no need to create the transparency attributes.
    //
    //

    #ifdef WIN32
    #pragma warning( disable : 4786 )       // Disable stupid STL warnings.
    #endif

    #include <maya/MIOStream.h>
    #include <math.h>

    #include <maya/MString.h>
    #include <maya/MPlug.h>
    #include <maya/MDataBlock.h>
    #include <maya/MDataHandle.h>
    #include <maya/MArrayDataHandle.h>
    #include <maya/MFnNumericAttribute.h>
    #include <maya/MFnTypedAttribute.h>
    #include <maya/MFnLightDataAttribute.h>
    #include <maya/MFloatVector.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MFnStringData.h>
    #include <maya/MGlobal.h>

    #include <maya/MPoint.h>
    #include <maya/MMatrix.h>
    #include <maya/MVector.h>
    #include <maya/MEulerRotation.h>
    #include <maya/MDagPath.h>
    #include <maya/MFnDependencyNode.h>
    #include <maya/MSceneMessage.h>

    #if defined(OSMac_MachO_)
    #include <OpenGL/gl.h>
    #include <OpenGL/glu.h>
    #else
    #include <GL/gl.h>
    #include <GL/glu.h>
    #endif

    #include "hwUnlitShader.h"
    #include "ShadingConnection.h"

    // Plug-in ID and Attributes.
    // This ID needs to be unique to prevent clashes.
    //
    MTypeId  hwUnlitShader::id( 0x00105440 );

    MObject  hwUnlitShader::color;
    MObject  hwUnlitShader::colorR;
    MObject  hwUnlitShader::colorG;
    MObject  hwUnlitShader::colorB;

    MObject  hwUnlitShader::transparency;
    MObject  hwUnlitShader::transparencyR;
    MObject  hwUnlitShader::transparencyG;
    MObject  hwUnlitShader::transparencyB;

    void hwUnlitShader::postConstructor( )
    {
        setMPSafe(false);
    }

    void hwUnlitShader::printGlError( const char *call )
    {
        GLenum error;

        while( (error = glGetError()) != GL_NO_ERROR ) {
            assert(0);
            cerr << call << ":" << error << " is " << (const char *)gluErrorString( error ) << "\n";
        }
    }

    hwUnlitShader::hwUnlitShader()
    {
        m_pTextureCache = MTextureCache::instance();

        attachSceneCallbacks();
    }

    hwUnlitShader::~hwUnlitShader()
    {
        detachSceneCallbacks();
    }

    void hwUnlitShader::releaseEverything()
    {
        // Clean the texture cache, through refcounting.
        m_pTextureCache->release();

        if(!MTextureCache::getReferenceCount())
        {
            m_pTextureCache = 0;
        }

    }

    void hwUnlitShader::attachSceneCallbacks()
    {
        fBeforeNewCB  = MSceneMessage::addCallback(MSceneMessage::kBeforeNew,  releaseCallback, this);
        fBeforeOpenCB = MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, releaseCallback, this);
        fBeforeRemoveReferenceCB = MSceneMessage::addCallback(MSceneMessage::kBeforeRemoveReference,
                                                              releaseCallback, this);
        fMayaExitingCB = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, releaseCallback, this);
    }

    /*static*/
    void hwUnlitShader::releaseCallback(void* clientData)
    {
        hwUnlitShader *pThis = (hwUnlitShader*) clientData;
        pThis->releaseEverything();
    }

    void hwUnlitShader::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;
    }

    void * hwUnlitShader::creator()
    {
        return new hwUnlitShader();
    }

    MStatus initializePlugin( MObject obj )
    {
        MStatus   status;

        const MString UserClassify( "shader/surface/utility" );

        MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.0.1", "Any");
        status = plugin.registerNode( "hwUnlitShader", hwUnlitShader::id,
                                      hwUnlitShader::creator, hwUnlitShader::initialize,
                                      MPxNode::kHwShaderNode, &UserClassify );
        if (!status) {
            status.perror("registerNode");
            return status;
        }

        return MS::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj )
    {
        MStatus   status;

        MFnPlugin plugin( obj );

        plugin.deregisterNode( hwUnlitShader::id );
        if (!status) {
            status.perror("deregisterNode");
            return status;
        }

        return MS::kSuccess;
    }

    MStatus hwUnlitShader::initialize()
    {
        MFnNumericAttribute nAttr;
        MStatus status;
        MFnTypedAttribute sAttr; // For string attributes

        // Create COLOR input attributes
        colorR = nAttr.create( "colorR", "cr",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f);

        colorG = nAttr.create( "colorG", "cg",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        colorB = nAttr.create( "colorB", "cb",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        color = nAttr.create( "color", "c", colorR, colorG, colorB);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f, 0.5f, 0.5f); // ugly pink-salmon color. You can't miss it.
        nAttr.setUsedAsColor(true);

        // Create TRANSPARENCY input attributes
        transparencyR = nAttr.create( "transparencyR", "itr",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(1.0f);

        transparencyG = nAttr.create( "transparencyG", "itg",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        transparencyB = nAttr.create( "transparencyB", "itb",MFnNumericData::kFloat);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.5f);

        transparency = nAttr.create( "transparency", "it", transparencyR, transparencyG, transparencyB);
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(0.0001f, 0.0001f, 0.0001f); // very light gray.
        nAttr.setUsedAsColor(true);

        // Add the attributes here
        addAttribute(color);
        addAttribute(transparency);

     // create output attributes here
        // outColor is the only output attribute and it is inherited
        // so we do not need to create or add it.
        //

        return MS::kSuccess;
    }

    // DESCRIPTION:
    // This function gets called by Maya to evaluate the shader.
    //
    // Get color from the input block.
    // Compute the color/alpha of our bump for a given UV coordinate.
    // Put the result into the output plug.

    MStatus hwUnlitShader::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;

        // Always return black for now.
        MFloatVector resultColor(0.0,0.0,0.0);

        // set ouput color attribute
        MDataHandle outColorHandle = block.outputValue( outColor );
        MFloatVector& outColor = outColorHandle.asFloatVector();
        outColor = resultColor;
        outColorHandle.setClean();

        return MS::kSuccess;
    }

    MStatus hwUnlitShader::getFloat3(MObject attr, float value[3])
    {
        // Get the attr to use
        //
        MPlug   plug(thisMObject(), attr);

        MObject object;

        MStatus status = plug.getValue(object);
        if (!status)
        {
            status.perror("hwUnlitShader::bind plug.getValue.");
            return status;
        }

        MFnNumericData data(object, &status);
        if (!status)
        {
            status.perror("hwUnlitShader::bind construct data.");
            return status;
        }

        status = data.getData(value[0], value[1], value[2]);
        if (!status)
        {
            status.perror("hwUnlitShader::bind get values.");
            return status;
        }

        return MS::kSuccess;
    }

    MStatus hwUnlitShader::getString(MObject attr, MString &str)
    {
        MPlug   plug(thisMObject(), attr);
        MStatus status = plug.getValue( str );
        return MS::kSuccess;
    }

    void hwUnlitShader::updateTransparencyFlags(MString objectPath)
    {
        // Update the transparency flags and values.
        // Check if the transparency channel is mapped on a texture, or if
        // it is constant. Textured transparency is not supported in this example,
        // because it would involve multiplying alpha values of two texture maps...
        MString transparencyName = "";
        ShadingConnection transparencyConnection(thisMObject(), objectPath, "transparency");
        if (transparencyConnection.type() == ShadingConnection::CONSTANT_COLOR)
        {
            // transparency = average of r,g,b transparency channels.
            MColor tc = transparencyConnection.constantColor();
            fConstantTransparency = (tc.r + tc.g + tc.b) / 3.0f;
        }
        else
            fConstantTransparency = 0.0f;   // will result in alpha=1.
    }

    /* virtual */
    MStatus hwUnlitShader::bind(const MDrawRequest& request,
                                M3dView& view)
    {
        MStatus status;

        // white, opaque.
        float bgColor[4] = {1,1,1,1};

        // Get path of current object in draw request
        currentObjectPath = request.multiPath();
        MString currentPathName( currentObjectPath.partialPathName() );

        updateTransparencyFlags(currentPathName);

        // Get decal texture name
        MString decalName = "";
        ShadingConnection colorConnection(thisMObject(), currentPathName, "color");

        // If the color attribute is ultimately connected to a file texture, find its filename.
        // otherwise use the default color texture.
        if (colorConnection.type() == ShadingConnection::TEXTURE &&
            colorConnection.texture().hasFn(MFn::kFileTexture))
        {
            // Get the filename of the texture.
            MFnDependencyNode textureNode(colorConnection.texture());
            MPlug filenamePlug( colorConnection.texture(), textureNode.attribute(MString("fileTextureName")) );
            filenamePlug.getValue(decalName);
            if (decalName == "")
                getFloat3(color, bgColor);
        }
        else
        {
            decalName = "";
            getFloat3(color, bgColor);
        }

        assert(glGetError() == GL_NO_ERROR);

        view.beginGL();

        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);

        // Set the standard OpenGL blending mode.
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        // Change the constant alpha value.
        float alpha = 1.0f - fConstantTransparency;

        // Set a color (with alpha). This color will be used directly if
        // the shader is not textured. Otherwise, the texture will get modulated
        // by the alpha.
        glColor4f(bgColor[0], bgColor[1], bgColor[2], alpha);

        // If the shader is textured...
        if (decalName.length() != 0)
        {
            // Enable 2D texturing.
            glEnable(GL_TEXTURE_2D);

            assert(glGetError() == GL_NO_ERROR);

            // Bind the 2D texture through the texture cache. The cache will keep
            // the texture around, so that it will only be loaded in video
            // memory once. In this example, the third parameter (mipmapping) is
            // false, so no mipmaps are generated. Note that mipmaps only work if
            // the texture has even dimensions.

            if(m_pTextureCache)
                m_pTextureCache->bind(colorConnection.texture(), MTexture::RGBA, false);

            // Set minification and magnification filtering to linear interpolation.
            // For better quality, you could enable mipmapping while binding and
            // use GL_MIPMAP_LINEAR_MIPMAP in for minification filtering.
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
        }

        // Disable lighting.
        glDisable(GL_LIGHTING);

        view.endGL();

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwUnlitShader::unbind(const MDrawRequest& request,
                   M3dView& view)
    {
        view.beginGL();
        glPopClientAttrib();
        glPopAttrib();

        view.endGL();

        return MS::kSuccess;
    }

    /* virtual */
    MStatus hwUnlitShader::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)
    {
        view.beginGL();

        glVertexPointer(3, GL_FLOAT, 0, vertexArray);
        glEnableClientState(GL_VERTEX_ARRAY);

        if (normalCount > 0)
        {
            // Technically, we don't need the normals for this example. But
            // most of the 3rd party plug-ins will probably want the normal,
            // which is why the following lines were kept.
            glNormalPointer(GL_FLOAT, 0, normalArrays[0]);
            glEnableClientState(GL_NORMAL_ARRAY);
        }

        if (texCoordCount > 0)
        {
            glTexCoordPointer(2, GL_FLOAT, 0, texCoordArrays[0]);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        }

        glDrawElements(prim, indexCount, GL_UNSIGNED_INT, indexArray);

        view.endGL();

        return MS::kSuccess;
    }

    /* virtual */
    int     hwUnlitShader::normalsPerVertex()
    {
        // Want only normals
        return 1;
    }

    /* virtual */
    int     hwUnlitShader::texCoordsPerVertex()
    {
        return 1;
    }

    /* virtual */
    bool    hwUnlitShader::hasTransparency()
    {
        // Performance note: if we knew that the texture
        // is always opaque, we could return false here
        // to avoid the computation cost associated with
        // ordering objects from farthest to closest.
        return true;
    }