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!
}


Autodesk® Maya® 2009 © 1997-2008 Autodesk, Inc. All rights reserved. Generated with doxygen 1.5.6