ShapeMonitor/ShapeMonitor.cpp

ShapeMonitor/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.
//
// ==========================================================================
//+
// DESCRIPTION:
//
// The ShapeMonitor is a singleton class that watches shape or texture nodes,
// and keeps 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,
// its unique name is appended to the list (actually set) of dirty textures.
// - Ask for the list of dirty textures. Those must 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 (for example, 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!
}