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

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

    //
    //
    //
    //  The ShapeMonitor is a singleton class that watches shape or texture nodes,
    //  and keep track of which one changed since the last export. It is used to keep
    //  the IFX scenegraph up-to-date in respect to textures.
    //
    //  Client code can:
    //
    //      - Ask for a pointer to the ShapeMonitor. (using the instance() function)
    //        If it doesn't already exist, it is created.
    //      - Ask the ShapeMonitor to watch a specific maya node name (specifying whether it's
    //        a texture, or a shape) and give a unique texture name. Doing so creates some callbacks
    //        on the specified node, so that we know when it changes. When a specific node changes,
    //        it's unique name is appended to the list (actually, set) of dirty textures.
    //      - Ask for the list of dirty textures. Those should be removed from the IFX scenegraph,
    //        and will possibly be regenerated, if they still exist.
    //      - Clear the list of dirty textures.
    //
    //  Additionally, once the ShapeMonitor is no longer necessary (eg: the scene is being closed),
    //  it can be destroyed using the destroy() function. Finally, all callbacks and data structures
    //  can be cleared by calling the initialize() function.

    #include <assert.h>

    #include <maya/MSelectionList.h>
    #include <maya/MConditionMessage.h>
    #include <maya/MFnDagNode.h>
    #include <maya/MDagPath.h>

    #include "ShapeMonitor.h"

    //
    // PUBLIC INTERFACE
    // ----------------
    //

    /* static */
    ShapeMonitor* ShapeMonitor::instance()
    {
        // If there is no texture monitor in use, create one.
        if (privateInstance == NULL)
        {
            privateInstance = new ShapeMonitor();
        }

        return privateInstance;
    }

    /* static */
    ShapeMonitor* ShapeMonitor::initialize()
    {
        destroy();
        return instance();
    }

    /* static */
    void ShapeMonitor::destroy()
    {
        if (privateInstance != NULL)
            delete privateInstance;
    }

    void ShapeMonitor::watch(MString mayaNodeName, MonitoredObject* pMon)
    //
    // Description:
    //
    //    Start watching the specified mayaNodeName for changes. (If the node is part of the DAG, that
    // should be a fully-specified DAG name). This function will store all available information
    // inside a MonitorObject for future reference.
    //
    // Arguments:
    //      mayaNodeName: DG (or fully-qualified DAG) name.
    //      uniqueTextureName: The corresponding texture name. This texture should correspond
    //                         directly to an IFX texture resource.
    {
        // Check if, per chance, this monitored object already exists.
        // If it does, no need to create a new one.
        if (retrieveMonitorObject(mayaNodeName))
            return;

        MStatus stat;

        //
        // Get the node.
        //
        MObject node;
        MSelectionList selList;
        selList.add(mayaNodeName);
        selList.getDependNode(0, node);

        //
        //  Attach the callbacks (dirty or renamed node)
        //
        pMon->mayaNodeName = mayaNodeName;

        pMon->dirtyCallbackId = MNodeMessage::addNodeDirtyCallback(node, watchedObjectDirtyCallback, pMon, &stat );
        assert(stat);

        pMon->renamedCallbackId = MNodeMessage::addNameChangedCallback ( node, watchedObjectRenamedCallback, pMon, &stat);
        assert(stat);

        // Store the pertinent information in the Monitored Objects Array.
        monitoredObjectsPtrArray.append(pMon);
    }

    // Stop watching node(s) that share the given uniqueTextureName.
    // In addition, the uniqueTextureName is automatically added to the list of dirty textures.
    // (If more than one node has the given uniqueTextureName, we stop to watch all of those that match.
    // This is done to minimize callback overhead.)
    void ShapeMonitor::stopWatching(MString mayaNodeName)
    {
        for (int i = monitoredObjectsPtrArray.length()-1; i >= 0; i--)
        {
            // If this record's node name matches...
            if (monitoredObjectsPtrArray[i]->mayaNodeName == mayaNodeName)
            {
                // Remove the callbacks for this node.
                removeCallbacks(monitoredObjectsPtrArray[i]);

                // Remove this element from the monitored objects array.
                delete monitoredObjectsPtrArray[i];
                monitoredObjectsPtrArray.removeElements(i, i, false);
            }
        }
    }

    // Stop watching all nodes. This detaches the callbacks.
    void ShapeMonitor::stopWatchingAll()
    {
        for (int i = monitoredObjectsPtrArray.length()-1; i >= 0; i--)
        {
            // Remove the callbacks for this node.
            removeCallbacks(monitoredObjectsPtrArray[i]);

            // Remove this element from the monitored objects array.
            delete monitoredObjectsPtrArray[i];
            monitoredObjectsPtrArray.removeElements(i, i, false);
        }
    }

    // Remove any DAG object not in the selectedObjects list.
    void ShapeMonitor::stopWatchingUnselectedDagObjects(MSelectionList& selectedObjects)
    {
        // For each monitored object...
        for (int i = monitoredObjectsPtrArray.length()-1; i >= 0; i--)
        {
            MonitoredObject *pMonObject = monitoredObjectsPtrArray[i];

            MStatus stat;

            // Get an MObject for the MonitoredObject->mayaNodeName.
            MDagPath dagpath;
            MSelectionList selList;
            selList.add(pMonObject->mayaNodeName);
            stat = selList.getDagPath(0, dagpath);

            // If the MObject is a DAG node...
            if (stat)
            {
                bool found = false;

                // Check if the dag path is included in the selectedObjects list.
                // For example, say that dagpath = "|group1|group2|pSphere|pSphereShape",
                // selectedObjects contains "|group1|group2".
                // We first check if dagpath is included in selectedObjects. If that's not the
                // case, we pop() one component, so that dagpath = "|group1|group2|pSphere", then
                // check again. We do that until either the dagpath is found to be included in
                // the selectedObjects list, or until there's no component left in dagpath.
                while (!found && dagpath.length() > 0)
                {
                    // Since we store the shape name (as opposed to the parent transform dagpath),
                    // we need to pop() to get the parent transform dagpath.
                    dagpath.pop();

                    MObject component;

                    // Check if the dag path is included in the objects list.
                    if (selectedObjects.hasItemPartly(dagpath, component))
                        found = true;
                }

                // If the object was not in the selectedObjects list, stop watching it.
                if (!found)
                    stopWatching(pMonObject->mayaNodeName);
            }
        }
    }

    //
    // PRIVATE IMPLEMENTATION
    // ----------------------
    //

    // The private instance points to the only texture monitor in memory,
    // or NULL if there is no instance.
    ShapeMonitor* ShapeMonitor::privateInstance = NULL;

    // Both the constructor and destructor assume that "this" is the only instance
    // of ShapeMonitor in memory. We can ensure this condition since both
    // functions are private.
    ShapeMonitor::ShapeMonitor()
    {
    }

    ShapeMonitor::~ShapeMonitor()
    {
        stopWatchingAll();
    }

    MonitoredObject* ShapeMonitor::retrieveMonitorObject(MString mayaNodeName)
    {
        for (int i = 0; i < (int) monitoredObjectsPtrArray.length(); i++)
        {
            // If this element's node name and filename matches...
            if (monitoredObjectsPtrArray[i]->mayaNodeName == mayaNodeName)
                return monitoredObjectsPtrArray[i];
        }

        return NULL;
    }

    // Attempt to detach all of the callbacks for a specific monitoredObject.
    // Assert if any of them fails.
    void ShapeMonitor::removeCallbacks(MonitoredObject *mon)
    {
        MStatus stat;

        stat = MMessage::removeCallback(mon->dirtyCallbackId);
        assert(stat);

        stat = MMessage::removeCallback(mon->renamedCallbackId);
        assert(stat);
    }

    void ShapeMonitor::watchedObjectDirtyCallback( void* clientData )
    {
        MonitoredObject *mon = (MonitoredObject*) clientData;

        privateInstance->stopWatching(mon->mayaNodeName);
        // Note: after this call the monitored object has been deleted,
        // so don't do anything with this pointer!
    }

    void ShapeMonitor::watchedObjectRenamedCallback( MObject & node, void* clientData )
    {
        MonitoredObject *mon = (MonitoredObject*) clientData;

        privateInstance->stopWatching(mon->mayaNodeName);
        // Note: after this call the monitored object has been deleted,
        // so don't do anything with this pointer!
    }