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

# pluginMain.cpp

    //
    // Copyright (C) 2002-2004 NVIDIA
    //
    // File: pluginMain.cpp
    //
    // Author: Jim Atkinson
    //
    // Changes:
    //  10/2003  Kurt Harriman - www.octopusgraphics.com +1-415-893-1023
    //           - "-pluginPath/pp" flag of cgfxShader command returns the
    //             full path of the "cgfxShader" subdirectory beneath the
    //             directory from which the plug-in binary was loaded.
    //             Supporting files such as MEL scripts can be loaded from
    //             this directory to avoid inadvertently picking up wrong
    //             versions from random directories on the search path.
    //           - The plug-in executes the cgfxShader_initUI.mel script
    //             from this directory at the time the plug-in is loaded.
    //           - The MEL command `pluginInfo -q -version cgfxShader`
    //             returns the plug-in version and cgfxShaderNode.cpp
    //             compile date.
    //  11/2003  Kurt Harriman - www.octopusgraphics.com +1-415-893-1023
    //           - To load or reload a CgFX file, use the cgfxShader command
    //             "-fx/fxFile <filename>" flag.  Setting the cgfxShader
    //             node's "s/shader" attribute no longer loads the file.
    //
    //-
    // ==========================================================================
    // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+
    #include "cgfxShaderCommon.h"

    #include "cgfxShaderNode.h"
    #include "cgfxShaderCmd.h"
    #include "cgfxVector.h"

    #include <maya/MFnPlugin.h>
    #include <maya/MIOStream.h>
    #include <maya/MSceneMessage.h>
    #include <maya/MNodeMessage.h>
    #include <maya/MGlobal.h>
    #include <maya/MFileIO.h>
    #include <maya/MItDependencyNodes.h>
    #include <maya/MDGMessage.h>

    #if defined(_SWATCH_RENDERING_SUPPORTED_)
        #include <maya/MHWShaderSwatchGenerator.h>
        #include <maya/MHardwareRenderer.h>
    #endif

    // viewport 2.0
    #include <maya/MDrawRegistry.h>

    // callbackIds is an array of callback identifiers which need to be
    // cancelled when the plug-in is unloaded.
    //
    static MIntArray callbackIds;

    static void cgfxShaderFileSaveCB(void* clientData );
    static void cgfxShaderFileReadCB(void* clientData );

    MStatus initializePlugin( MObject obj )
    //
    //  Description:
    //      this method is called when the plug-in is loaded into Maya.  It
    //      registers all of the services that this plug-in provides with
    //      Maya.
    //
    //  Arguments:
    //      obj - a handle to the plug-in object (use MFnPlugin to access it)
    //
    {
        MString   sVer = cgfxShaderNode::getPluginVersion();

    #if defined(_SWATCH_RENDERING_SUPPORTED_)
            // Allow an environment variable to override usage of swatch rendering.
        // Set the environment variable to a value other than 0 for it to take effect.
        const char *cgfxEnvVar = getenv("CGFX_SWATCH_RENDERING");

        MString UserClassify = MString( "shader/surface/utility:drawdb/shader/surface/cgfxShader" );

        // Don't initialize swatches in batch mode
        if (MGlobal::mayaState() != MGlobal::kBatch)
        {
            const MString& swatchName = MHWShaderSwatchGenerator::initialize();

    #ifdef _WIN32
            if (!cgfxEnvVar)
                UserClassify = MString( "shader/surface/utility/:drawdb/shader/surface/cgfxShader:swatch/"+swatchName );
            else
            {
                if ( 0 != strcmp(cgfxEnvVar,"0") )
                    UserClassify = MString( "shader/surface/utility/:drawdb/shader/surface/cgfxShader:swatch/"+swatchName );
            }
    #else
            if ( cgfxEnvVar && ( 0 != strcmp(cgfxEnvVar,"0") ) )
            {
                UserClassify = MString( "shader/surface/utility/:drawdb/shader/surface/cgfxShader:swatch/"+swatchName );
            }
    #endif
        }

    #else
        const MString UserClassify( "shader/surface/utility:drawdb/shader/surface/cgfxShader" );
    #endif

        MFnPlugin plugin( obj, "NVIDIA", sVer.asChar(), MApiVersion );

        // Register/initialize localized string resources
        CHECK_MSTATUS( plugin.registerUIStrings(NULL,
                       "cgfxShaderPluginInitStrings" ));

        // Create Cg Context & register the Cg error callback

    #if defined(_SWATCH_RENDERING_SUPPORTED_)
        // The following code is only used on Maya versions 7.0 and
        // later.
        MStatus status = MStatus::kFailure;
    #if !defined(LINUX)
        // Always initialize a context on Linux since it is not predictable as to when
        // we may get a valid context.
        if (MGlobal::mayaState() != MGlobal::kInteractive)
    #endif
        {
            MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();

            if (pRenderer) {
                const MString & backEndStr = pRenderer->backEndString();
                unsigned int width = 64, height = 64;
                status = pRenderer->makeSwatchContextCurrent( backEndStr,
                    width,
                    height );
            }
            if (status != MStatus::kSuccess) {
                MGlobal::displayError(MString("Unqualified video card : Offscreen contexts not supported. CgFx plugin will not be loaded."));
                return MStatus::kFailure;
            }
        }
    #else
    #error "CgFx requires the Maya version 7 or greater"
    #endif

        cgfxShaderNode::sCgContext = cgCreateContext();
        cgSetErrorCallback(cgfxShaderNode::cgErrorCallBack);
        cgSetErrorHandler(cgfxShaderNode::cgErrorHandler, 0);
        cgGLRegisterStates(cgfxShaderNode::sCgContext);
        cgGLSetManageTextureParameters(cgfxShaderNode::sCgContext, CG_TRUE);

        cgSetAutoCompile(cgfxShaderNode::sCgContext, CG_COMPILE_LAZY);
        cgSetLockingPolicy(CG_NO_LOCKS_POLICY);
        cgGLSetDebugMode(CG_FALSE);

        // Set shader profile strings to the latest. Only support Cg, so
        // add only that set for now. Note that we will get different
        // results depending on card and vender (e.g. for G80 nvidia cards
        // we would get the <"gp4vp", "gp4gp", "gp4fp"> triplet).

        // Because cgGLGetLatestProfile() cannot always get the "best"
        // profiles, we check gp4vp/gp4gp/gp4fp, check glslv/glslg/glslf
        // and call cgGLGetLatestProfile() in sequence.

        // Vertex shader profile, which must exist.
        if (cgGLIsProfileSupported(CG_PROFILE_GP4VP))
            cgfxShaderNode::sCgBestVtxProfile = CG_PROFILE_GP4VP;
        else if (cgGLIsProfileSupported(CG_PROFILE_GLSLV))
            cgfxShaderNode::sCgBestVtxProfile = CG_PROFILE_GLSLV;
        else
            cgfxShaderNode::sCgBestVtxProfile = cgGLGetLatestProfile( CG_GL_VERTEX );

        // Geometry shader profile, which may not exist.
        if (cgGLIsProfileSupported(CG_PROFILE_GP4GP))
            cgfxShaderNode::sCgBestGeomProfile = CG_PROFILE_GP4GP;
        else if (cgGLIsProfileSupported(CG_PROFILE_GLSLG))
            cgfxShaderNode::sCgBestGeomProfile = CG_PROFILE_GLSLG;
        else
            cgfxShaderNode::sCgBestGeomProfile = cgGLGetLatestProfile( CG_GL_GEOMETRY );

        // Pixel (fragment) shader profile, which must exist.
        if (cgGLIsProfileSupported(CG_PROFILE_GP4FP))
            cgfxShaderNode::sCgBestFragProfile = CG_PROFILE_GP4FP;
        else if (cgGLIsProfileSupported(CG_PROFILE_GLSLF))
            cgfxShaderNode::sCgBestFragProfile = CG_PROFILE_GLSLF;
        else
            cgfxShaderNode::sCgBestFragProfile = cgGLGetLatestProfile( CG_GL_FRAGMENT );

        {
          // display banner
          MString s;
          s += sVer;
          s += " (using Cg profiles ";
          s += cgGetProfileString(cgfxShaderNode::sCgBestVtxProfile);
          s += ", ";
          s += cgGetProfileString(cgfxShaderNode::sCgBestGeomProfile);
          s += " and ";
          s += cgGetProfileString(cgfxShaderNode::sCgBestFragProfile);
          s += ")";
          MGlobal::displayInfo(s);
        }

        CHECK_MSTATUS( plugin.registerNode("cgfxShader",
                                           cgfxShaderNode::sId,
                                           cgfxShaderNode::creator,
                                           cgfxShaderNode::initialize,
                                           MPxNode::kHwShaderNode,
                                           &UserClassify));

        CHECK_MSTATUS( plugin.registerNode("cgfxVector",
            cgfxVector::sId,
            cgfxVector::creator,
            cgfxVector::initialize));

        CHECK_MSTATUS( plugin.registerCommand("cgfxShader",
            cgfxShaderCmd::creator,
            cgfxShaderCmd::newSyntax));

        // Register a shader override for this node
        CHECK_MSTATUS(MHWRender::MDrawRegistry::registerShaderOverrideCreator(
            cgfxShaderOverride::drawDbClassification,
            cgfxShaderOverride::drawRegistrantId,
            cgfxShaderOverride::Creator));

        // Where are my MEL scripts?
        cgfxShaderCmd::sPluginPath = plugin.loadPath();

        // Run MEL script for user interface initialization.
        if (MGlobal::mayaState() == MGlobal::kInteractive)
        {
            MString sCmd = "evalDeferred \"source \\\"cgfxShader_initUI.mel\\\"\"";
            MGlobal::executeCommand( sCmd );
        }

        // Skip the status checking on the addCallback calls since the only
        // way that they can really fail is if Maya is out of memory and then
        // everything is going to fall apart anyway.
        //
        MCallbackId id;
        id = MSceneMessage::addCallback(MSceneMessage::kAfterNew,
            cgfxShaderFileReadCB, NULL, &status);
        CHECK_MSTATUS(status);
        callbackIds.append((int)id);

        id = MSceneMessage::addCallback(MSceneMessage::kAfterImport,
            cgfxShaderFileReadCB, NULL, &status);
        CHECK_MSTATUS(status);
        callbackIds.append((int)id);

        id = MSceneMessage::addCallback(MSceneMessage::kAfterOpen,
            cgfxShaderFileReadCB, NULL, &status);
        CHECK_MSTATUS(status);
        callbackIds.append((int)id);

        id = MSceneMessage::addCallback(MSceneMessage::kAfterReference,
            cgfxShaderFileReadCB, NULL, &status);
        CHECK_MSTATUS(status);
        callbackIds.append((int)id);

        // call backs for "source directory" removal...
        id = MSceneMessage::addCallback(MSceneMessage::kBeforeSave,
            cgfxShaderFileSaveCB, NULL, &status);
        CHECK_MSTATUS(status);
        callbackIds.append((int)id);

        id = MSceneMessage::addCallback(MSceneMessage::kBeforeExport,
            cgfxShaderFileSaveCB, NULL, &status);
        CHECK_MSTATUS(status);
        callbackIds.append((int)id);

        return MStatus::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj)
    //
    //  Description:
    //      this method is called when the plug-in is unloaded from Maya. It
    //      deregisters all of the services that it was providing.
    //
    //  Arguments:
    //      obj - a handle to the plug-in object (use MFnPlugin to access it)
    //
    {
        MStatus   status;
        MFnPlugin plugin( obj );

        cgDestroyContext(cgfxShaderNode::sCgContext);

        // Remove all the callbacks that we registered.
        //
        MMessage::removeCallbacks(callbackIds);

        // Deregister our node types.
        //
        CHECK_MSTATUS(plugin.deregisterNode( cgfxShaderNode::sId ));
        CHECK_MSTATUS(plugin.deregisterNode( cgfxVector::sId ));

        // Deregister our commands.
        //
        CHECK_MSTATUS(plugin.deregisterCommand( "cgfxShader" ));

        // Deregister the override
        CHECK_MSTATUS(MHWRender::MDrawRegistry::deregisterShaderOverrideCreator(
            cgfxShaderOverride::drawDbClassification, cgfxShaderOverride::drawRegistrantId));

        return MStatus::kSuccess;
    }

    static void cfxfFxFileChanged( MPlug& plug )
    {
        // The easiest thing to do is to simply issue the
        // cgfxShader command to reload the shader.
        //
        MString fxFile;
        plug.getValue(fxFile);

        MString nodeName = plug.name();

        int index = nodeName.index('.');

        if (index >= 0)
        {
            nodeName = nodeName.substring(0, index - 1);

            MString cmd("cgfxShader -e -fx \"");
            cmd += fxFile;
            cmd += "\" ";
            cmd += nodeName;
            cmd += ";";

            MGlobal::executeCommand(cmd);
        }
    }

    static void cgfxShaderFileSaveCB(void* clientData )
    {
        // Look through the scene for cgfxShader nodes whose effect is NULL
        // but whose shader attribute is not empty.
        //
        MStatus status;

        MString workspace;
        status = MGlobal::executeCommand(MString("workspace -q -rd;"),
            workspace);
        if (!status)
        {
            workspace.clear();
        }

        MItDependencyNodes nodeIt;

        for (nodeIt.reset(MFn::kPluginHwShaderNode);
            !nodeIt.isDone();
            nodeIt.next())
        {
            MObject oNode = nodeIt.item();

            MFnDependencyNode fnNode(oNode);
            if (fnNode.typeId() == cgfxShaderNode::sId)
            {
                // We've got a winner.
                //
                cgfxShaderNode* pNode = (cgfxShaderNode*)fnNode.userNode();
                MString ShaderFxFile = pNode->shaderFxFile();
                if (strncmp(ShaderFxFile.asChar(),workspace.asChar(),workspace.length()) == 0)
                {
                    ShaderFxFile = ShaderFxFile.substring(workspace.length(),ShaderFxFile.length() - 1);
                    MPlug plShader = fnNode.findPlug( pNode->sShader );
                    plShader.setValue( ShaderFxFile );
                    OutputDebugString("CGFX shader pathname saved as: ");
                    OutputDebugString(ShaderFxFile.asChar());
                    OutputDebugString("\n");
                }

                if( pNode->getTexturesByName())
                {
                    cgfxAttrDefList::iterator it(pNode->attrDefList());

                    while (it)
                    {
                        cgfxAttrDef* aDef = (*it);

                        MObject oNode = pNode->thisMObject();

                        switch (aDef->fType)
                        {
                        case cgfxAttrDef::kAttrTypeColor1DTexture:
                        case cgfxAttrDef::kAttrTypeColor2DTexture:
                        case cgfxAttrDef::kAttrTypeColor3DTexture:
                        case cgfxAttrDef::kAttrTypeColor2DRectTexture:
                        case cgfxAttrDef::kAttrTypeNormalTexture:
                        case cgfxAttrDef::kAttrTypeBumpTexture:
                        case cgfxAttrDef::kAttrTypeCubeTexture:
                        case cgfxAttrDef::kAttrTypeEnvTexture:
                        case cgfxAttrDef::kAttrTypeNormalizationTexture:
                            {
                                MString pathname;
                                aDef->getValue(oNode, pathname);
                                if (strncmp(pathname.asChar(),workspace.asChar(),workspace.length()) == 0)
                                {
                                    pathname = pathname.substring(workspace.length(),pathname.length());
                                    aDef->setValue(oNode, pathname);
                                    OutputDebugString("CGFX texture pathname saved as: ");
                                    OutputDebugString(pathname.asChar());
                                    OutputDebugString("\n");
                                }
                            }
                            break;
                        default:
                            break;
                        }
                        ++it;
                    }
                }
            }
        }
    }

    static void cgfxShaderFileReadCB(void* clientData )
    {
        // Look through the scene for cgfxShader nodes whose effect is NULL
        // but whose shader attribute is not empty.
        //
        MStatus status;

        MItDependencyNodes nodeIt;

        for (nodeIt.reset(MFn::kPluginHwShaderNode);
            !nodeIt.isDone();
            nodeIt.next())
        {
            MObject oNode = nodeIt.item();

            MFnDependencyNode fnNode(oNode);
            if (fnNode.typeId() == cgfxShaderNode::sId)
            {
                // We've got a winner.
                //
                // cgfxShaderNode* pNode = (cgfxShaderNode*)fnNode.userNode();

                // And invoke the callback to reconnect everything
                //
                MPlug plug(oNode, cgfxShaderNode::sShader);
                cfxfFxFileChanged( plug );
            }
        }
    }