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

# viewCallbackTest.cpp

    //-
    // ==========================================================================
    // 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 <stdio.h>

    #define OPENMAYA_EXPORT

    #include <maya/MIOStream.h>
    #include <maya/MGlobal.h>
    #include <maya/MString.h>
    #include <maya/MStringArray.h>
    #include <maya/MEventMessage.h>
    #include <maya/MUiMessage.h>
    #include <maya/MFnPlugin.h>

    #include <maya/MPxCommand.h>
    #include <maya/MSyntax.h>
    #include <maya/MArgDatabase.h>
    #include <maya/MArgList.h>

    #include <maya/M3dView.h>
    #include <maya/MPoint.h>
    #include <maya/MImage.h>

    #include <stdio.h>

    // Static pointer to the current refreshCompute per panel so we can delete
    // it if the plug-in is unloaded.
    //
    class refreshCompute;
    static refreshCompute* currentRefreshCompute[4] = { 0, 0, 0, 0 };

    // Possible buffer operations supported
    enum MbufferOperation {
        kInvertColorBuffer,
        kDrawDepthBuffer
    };

    //
    // Command class declaration
    //
    class viewCallbackTest : public MPxCommand
    {
    public:
        viewCallbackTest();
        virtual                 ~viewCallbackTest();

        MStatus                 doIt( const MArgList& args );

        static MSyntax          newSyntax();
        static void*            creator();

    private:
        MStatus                 parseArgs( const MArgList& args );

        // Name of panel to monitor
        MString                 mPanelName;
        MbufferOperation        mBufferOperation;
    };

    //
    // Refresh computation class implementation
    //
    class refreshCompute
    {
    public:
        refreshCompute(const MString &panelName, MbufferOperation bufferOperation);
        ~refreshCompute();

        const MString &panelName() const { return mPanelName; }
        void setPanelName(const MString &panelName) { mPanelName = panelName; }

        MbufferOperation bufferOperation() const { return mBufferOperation; }
        void setBufferOperation(const MbufferOperation operation) { mBufferOperation = operation; }

        void clearCallbacks();

    protected:
        static void             deleteCB(const MString& panelName, void * data);
        static void             preRenderCB(const MString& panelName, void * data);
        static void             postRenderCB(const MString& panelName, void * data);

        MCallbackId             mDeleteId;
        MCallbackId             mPreRenderId;
        MCallbackId             mPostRenderId;

        MString                 mPanelName;
        MbufferOperation        mBufferOperation;
    };

    //
    // Command class implementation
    //

    // Constructor
    //
    viewCallbackTest::viewCallbackTest()
    {
        mPanelName = MString("");
        mBufferOperation = kInvertColorBuffer;
    }

    // Destructor
    //
    viewCallbackTest::~viewCallbackTest()
    {
        // Do nothing
    }

    // creator
    //
    void* viewCallbackTest::creator()
    {
        return (void *) (new viewCallbackTest);
    }

    // newSyntax
    //
    // Buffer operation = -bo/-bufferOperation <string> = (invert | showDepth)
    const char *bufferOperationShortName = "-bo";
    const char *bufferOperationLongName = "-bufferOperation";
    #define _NUMBER_BUFFER_OPERATIONS_ 2
    const MString bufferOperationStrings[_NUMBER_BUFFER_OPERATIONS_ ] =
            { MString("invert"), MString("showDepth") };
    const MbufferOperation bufferOperations[_NUMBER_BUFFER_OPERATIONS_] =
            { kInvertColorBuffer, kDrawDepthBuffer };

    MSyntax viewCallbackTest::newSyntax()
    {
        MSyntax syntax;

        syntax.addFlag(bufferOperationShortName, bufferOperationLongName, MSyntax::kString);

        // Name of model panel to monitor
        //
        syntax.addArg(MSyntax::kString);

        return syntax;
    }

    // parseArgs
    //
    MStatus viewCallbackTest::parseArgs(const MArgList& args)
    {
        MStatus         status;
        MArgDatabase    argData(syntax(), args);

        // Buffer operation argument variables
        mBufferOperation = kInvertColorBuffer;
        MString operationString;

        MString         arg;
        for ( unsigned int i = 0; i < args.length(); i++ )
        {
            arg = args.asString( i, &status );
            if (!status)
                continue;

            if ( arg == MString(bufferOperationShortName) || arg == MString(bufferOperationLongName) )
            {
                if (i == args.length()-1) {
                    arg += ": must specify a buffer operation.";
                    displayError(arg);
                    return MS::kFailure;
                }
                i++;
                args.get(i, operationString );

                bool validOperation = false;
                for (unsigned int k=0; k<_NUMBER_BUFFER_OPERATIONS_; k++)
                {
                    if (bufferOperationStrings[i] == operationString)
                    {
                        mBufferOperation = bufferOperations[k];
                        validOperation = true;
                    }
                }
                if (!validOperation)
                    status.perror("Invalid operation specified. Using invert by default.");
            }
        }

        // Read off the panel name
        status = argData.getCommandArgument(0, mPanelName);
        if (!status)
        {
            status.perror("No panel name specified as command argument");
            return status;
        }
        return MS::kSuccess;
    }

    // doIt
    //
    MStatus viewCallbackTest::doIt(const MArgList& args)
    {
        MStatus status;

        status = parseArgs(args);
        if (!status)
        {
            return status;
        }

        try
        {
            // Only allow one computation per panel at this time.
            refreshCompute *foundComputePtr = 0;
            unsigned int i;
            for (i=0; i<4; i++)
            {
                if (currentRefreshCompute[i] &&
                    (currentRefreshCompute[i])->panelName() == mPanelName)
                {
                    foundComputePtr = currentRefreshCompute[i];
                }
            }

            // If alread exists, just change the operator if it differs.
            if (foundComputePtr)
            {
                foundComputePtr->setBufferOperation( mBufferOperation);
            }
            else
            {
                for (i=0; i<4; i++)
                {
                    if (!currentRefreshCompute[i])
                    {
                        currentRefreshCompute[i] = new refreshCompute(mPanelName, mBufferOperation);
                        break;
                    }
                }
            }
        }
        catch(MStatus status)
        {
            return status;
        }
        catch(...)
        {
            throw;
        }

        return status;
    }

    //
    // refreshCompute implementation
    //
    refreshCompute::refreshCompute(const MString &panelName,
                                   MbufferOperation postBufferOperation)
    {
        MStatus status;

        // Set panel name and operator for post rendering
        mPanelName = panelName;
        mBufferOperation = postBufferOperation;

        // Add the callbacks
        //
        mDeleteId
            = MUiMessage::add3dViewDestroyMsgCallback(panelName,
                                               &refreshCompute::deleteCB,
                                               (void *) this, &status);
        if (mDeleteId == 0)
            status.perror(MString("Could not attach view deletion callback to panel ") +
                          panelName);

        mPreRenderId
            = MUiMessage::add3dViewPreRenderMsgCallback(panelName,
                                              &refreshCompute::preRenderCB,
                                              (void *) this, &status);
        if (mPreRenderId == 0)
            status.perror(MString("Could not attach view prerender callback to panel ") +
                          panelName);

        mPostRenderId
            = MUiMessage::add3dViewPostRenderMsgCallback(panelName,
                                               &refreshCompute::postRenderCB,
                                               (void *) this, &status);
        if (mPostRenderId == 0)
            status.perror(MString("Could not attach view postrender callback to panel ") +
                          panelName);
    }

    // Clear all callbacks for this compute
    void refreshCompute::clearCallbacks()
    {
        if (mDeleteId)
            MMessage::removeCallback(mDeleteId);

        if (mPreRenderId)
            MMessage::removeCallback(mPreRenderId);

        if (mPostRenderId)
            MMessage::removeCallback(mPostRenderId);
    }

    refreshCompute::~refreshCompute()
    {
        clearCallbacks();

        // Reset any global pointer pointing to this compute
        for (unsigned int i=0; i<4; i++)
        {
            if (currentRefreshCompute[i] &&
                (currentRefreshCompute[i])->panelName() == mPanelName)
            {
                currentRefreshCompute[i] = 0;
            }
        }
    }

    // Delete callback
    //
    void refreshCompute::deleteCB(const MString& panelName, void * data)
    {
        refreshCompute *pf = (refreshCompute *) data;

        cout<<"In delete view callback for view "<<panelName.asChar()
            <<". Remove all callbacks."<<endl;

        // Delete callback.
        delete pf;
    }

    // Pre-render callback
    //
    void refreshCompute::preRenderCB(const MString& panelName, void * data)
    {
        refreshCompute *thisCompute = (refreshCompute *) data;
        if (!thisCompute)
            return;

        M3dView view;
        MStatus status = M3dView::getM3dViewFromModelPanel(panelName, view);
        if (status != MS::kSuccess)
            return;

        int width = 0, height = 0;
        width = view.portWidth( &status );
        if (status != MS::kSuccess || (width < 2))
            return;
        height = view.portHeight( &status );
        if (status != MS::kSuccess || (height < 2))
            return;

        unsigned int vx,vy,vwidth,vheight;
        vx = 0;
        vy = 0;
        vwidth = width / 2;
        vheight = height / 2;
        status = view.pushViewport (vx, vy, vwidth, vheight);

        if (thisCompute->mBufferOperation != kDrawDepthBuffer)
        {
            M3dView view;
            MStatus status = M3dView::getM3dViewFromModelPanel(panelName, view);
            MPoint origin;
            status = view.drawText( MString("Pre render callback: ") + panelName, origin );
        }
    }

    // Post-render callback
    //
    void refreshCompute::postRenderCB(const MString& panelName, void * data)
    {
        refreshCompute *thisCompute = (refreshCompute *) data;
        if (!thisCompute)
            return;

        // Get the view if any for the panel
        M3dView view;
        MStatus status = M3dView::getM3dViewFromModelPanel(panelName, view);
        if (status != MS::kSuccess)
            return;

        view.popViewport();

        if (thisCompute->mBufferOperation == kDrawDepthBuffer)
        {
            int width = 0, height = 0;
            width = view.portWidth( &status ) / 2 ;
            if (status != MS::kSuccess || (width < 2))
                return;
            height = view.portHeight( &status ) / 2 ;
            if (status != MS::kSuccess || (height < 2))
                return;

            unsigned int numPixels = width * height;

            float *depthPixels = new float[numPixels];
            if (!depthPixels)
                return;

            unsigned char *colorPixels = new unsigned char[numPixels * 4];
            if (!colorPixels)
            {
                delete [] depthPixels;
                delete [] colorPixels;
                return;
            }

            // Read into a float buffer
            status = view.readDepthMap( 0,0, width, height, (unsigned char *)depthPixels, M3dView::kDepth_Float );

            if (status != MS::kSuccess)
            {
                delete [] depthPixels;
                delete [] colorPixels;
                return;
            }

            // Find depth range and remap normalized depth range (-1 to 1) into 0...255
            // for color.
            float *dPtr = depthPixels;
            unsigned int i = 0;

            float zmin = 100.0f; // *dPtr;
            float zmax = -100.0f; // *dPtr;
            for(i=0; i<numPixels; i++)
            {
                float val = *dPtr; // * 2.0f - 1.0f;
                if(val < zmin) {
                    zmin = *dPtr;
                }
                if(val > zmax) {
                    zmax = *dPtr;
                }
                dPtr++;
            }
            float zrange = zmax - zmin;
            //printf("depth values = (%g, %g). Range = %g\n", zmin, zmax, zrange);

            unsigned char *cPtr = colorPixels;

            dPtr = depthPixels;
            for(i=0; i < numPixels; i++)
            {
                float val = *dPtr; // * 2.0f - 1.0f;
                //unsigned char depth = (unsigned char)(255.0f * (( (*dPtr)-zmin) / zrange) + zmin );
                unsigned char depth = (unsigned char)(255.0f * (( (val)-zmin) / zrange) );
                //unsigned char depth = (unsigned char)(255.0f * val);
                *cPtr = depth; cPtr++;
                *cPtr = depth; cPtr++;
                *cPtr = depth; cPtr++;
                *cPtr = 0xff;
                cPtr++;
                dPtr++;
            }

            MImage image;
            image.setPixels( colorPixels, width, height );

            // Uncomment next line to test writing buffer to file.
            //image.writeToFile( "C:\\temp\\dumpDepth.iff" );

            // Write all pixels back. The origin of the image (lower left)
            // is used
            status = view.writeColorBuffer( image, 5, 5 );

            if (depthPixels)
                delete [] depthPixels;
            if (colorPixels)
                delete [] colorPixels;
        }

        // Do a simple color invert operation on all pixels
        //
        else if (thisCompute->mBufferOperation == kInvertColorBuffer)
        {
            // Optional to read as RGBA. Note that this will be slower
            // since we must swizzle the bytes around from the default
            // BGRA format.
            bool readAsRGBA = true;

            // Read the RGB values from the color buffer
            MImage image;
            status = view.readColorBuffer( image, readAsRGBA );
            if (status != MS::kSuccess)
                return;

            status = view.writeColorBuffer( image, 5, 5 );

            unsigned char *pixelPtr = (unsigned char*)image.pixels();
            if (pixelPtr)
            {
                unsigned int width, height;
                image.getSize( width, height );

                MImage image2;
                image2.create( width, height );
                unsigned char *pixelPtr2 = (unsigned char*)image2.pixels();

                unsigned int numPixels = width * height;
                for (unsigned int i=0; i < numPixels; i++)
                {
                    *pixelPtr2 = (255 - *pixelPtr);
                    pixelPtr2++; pixelPtr++;
                    *pixelPtr2 = (255 - *pixelPtr);
                    pixelPtr2++; pixelPtr++;
                    *pixelPtr2 = (255 - *pixelPtr);
                    pixelPtr2++; pixelPtr++;
                    *pixelPtr2 = 255;
                    pixelPtr2++; pixelPtr++;
                }

                // Write all pixels back. The origin of the image (lower left)
                // is used
                status = view.writeColorBuffer( image2, 5, short(5+height/2) );
            }
        }
    }

    //
    // Plug-in functions
    //

    MStatus initializePlugin( MObject obj )
    {
        MStatus   status;
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "6.5", "Any");

        // Register the command so we can actually do some work
        //
        status = plugin.registerCommand("viewCallbackTest",
                                        viewCallbackTest::creator,
                                        viewCallbackTest::newSyntax);

        if (!status)
        {
            status.perror("registerCommand");
        }

        return status;
    }

    MStatus uninitializePlugin( MObject obj)
    {
        MStatus   status;
        MFnPlugin plugin( obj );

        // Remove all computation class + callbacks
        for (unsigned int i=0; i<4; i++)
        {
            delete currentRefreshCompute[i];
            currentRefreshCompute[i] = 0;
        }

        // Deregister the command
        //
        status = plugin.deregisterCommand("viewCallbackTest");

        if (!status)
        {
            status.perror("deregisterCommand");
        }

        return status;
    }