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