aboutsummaryrefslogtreecommitdiff
path: root/mayaPlug/shaveCallbacks.cpp
diff options
context:
space:
mode:
authorBen Marsh <[email protected]>2019-10-22 09:07:59 -0400
committerBen Marsh <[email protected]>2019-10-22 09:07:59 -0400
commitbd0027e737c6512397f841c22786274ed74b927f (patch)
treef7ffbdb8f3741bb7f24635616cc189cba5cb865c /mayaPlug/shaveCallbacks.cpp
downloadshave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.tar.xz
shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.zip
Adding Shave-and-a-Haircut 9.6
Diffstat (limited to 'mayaPlug/shaveCallbacks.cpp')
-rw-r--r--mayaPlug/shaveCallbacks.cpp795
1 files changed, 795 insertions, 0 deletions
diff --git a/mayaPlug/shaveCallbacks.cpp b/mayaPlug/shaveCallbacks.cpp
new file mode 100644
index 0000000..9ca292b
--- /dev/null
+++ b/mayaPlug/shaveCallbacks.cpp
@@ -0,0 +1,795 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+#include <maya/MDGMessage.h>
+#include <maya/MDGModifier.h>
+#include <maya/MEventMessage.h>
+#include <maya/MFileIO.h>
+#include <maya/MFnDependencyNode.h>
+#include <maya/MGlobal.h>
+#include <maya/MObjectArray.h>
+#include <maya/MPlug.h>
+#include <maya/MSceneMessage.h>
+#include <maya/MSelectionList.h>
+
+#include "shaveCallbacks.h"
+#include "shaveDebug.h"
+#include "shaveGlobals.h"
+#include "shaveHairShape.h"
+#include "shaveRender.h"
+#include "shaveSDK.h"
+#include "shaveUtil.h"
+
+
+//--------------------------------------------------------------------
+//
+// External Interface
+//
+//--------------------------------------------------------------------
+
+void shaveCallbacks::endLoad(bool isImport, bool isReference)
+{
+ ENTER();
+
+ shaveUtil::setLoadingFile(false);
+
+ //
+ // The output mesh of a shaveHairShape can depend upon other objects in
+ // the scene: hair objects, skull objects, shaveGlobals, etc. So the
+ // shaveHairShape cannot properly initialize its output mesh until
+ // after the scene is fully loaded.
+ //
+ // If we're done loading all files, then run through all the
+ // shaveHairShapes and initialize any new ones.
+ //
+ if (!shaveUtil::isLoadingFile()) initializeHairNodes();
+
+ //if default renderer is changed in Maya prefs
+ //we need to make sure that correct MEL callbacks are set
+ if (!shaveUtil::isLoadingFile())
+ MGlobal::executeCommand("shave_selectedRendererChanged");
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::mayaExiting(void* clientData)
+{
+ if (mCleanUpMELOnExit)
+ {
+ MGlobal::executeCommand("shaveCleanup(true)");
+ }
+//fprintf (stdout,"why are we calling SHAVEcleanup()?\n");fflush(stdout);
+ SHAVEcleanup();
+
+ mCleanUpMELOnExit = false;
+
+ // You might think that we should call removeCallbacks() here, but it's
+ // possible that this method is executing within one of those callbacks
+ // and Maya does not behave well when a callback function removes
+ // itself. So we leave the callback removal to others.
+ //
+}
+
+
+void shaveCallbacks::prepareForNewScene(bool firstTime)
+{
+ ENTER();
+
+ SHAVEclear_stack();
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::registerCallbacks()
+{
+ //
+ // Now that we know the plugin has loaded successfully, register
+ // the callbacks.
+ //
+ beforeExportID = MSceneMessage::addCallback(
+ MSceneMessage::kBeforeExport, beforeExport
+ );
+
+ afterExportID = MSceneMessage::addCallback(
+ MSceneMessage::kAfterExport, afterExport
+ );
+
+ beforeImportID = MSceneMessage::addCallback(
+ MSceneMessage::kBeforeImport, beforeImport
+ );
+
+ afterImportID = MSceneMessage::addCallback(
+ MSceneMessage::kAfterImport, afterImport
+ );
+
+ beforeImportRefID = MSceneMessage::addCallback(
+ MSceneMessage::kBeforeImportReference,
+ beforeImportReference
+ );
+
+ afterImportRefID = MSceneMessage::addCallback(
+ MSceneMessage::kAfterImportReference,
+ afterImportReference
+ );
+
+ beforeOpenID = MSceneMessage::addCallback(
+ MSceneMessage::kBeforeOpen, beforeOpen
+ );
+
+ afterOpenID = MSceneMessage::addCallback(
+ MSceneMessage::kAfterOpen, afterOpen
+ );
+
+ beforeReferenceID = MSceneMessage::addCallback(
+ MSceneMessage::kBeforeReference,
+ beforeReference
+ );
+
+ afterReferenceID = MSceneMessage::addCallback(
+ MSceneMessage::kAfterReference,
+ afterReference
+ );
+
+ beforeSaveID = MSceneMessage::addCallback(
+ MSceneMessage::kBeforeSave, beforeSave
+ );
+
+ afterSaveID = MSceneMessage::addCallback(
+ MSceneMessage::kAfterSave, afterSave
+ );
+
+ afterNewID = MSceneMessage::addCallback(
+ MSceneMessage::kAfterNew, afterNew
+ );
+
+ mayaExitingID = MSceneMessage::addCallback(
+ MSceneMessage::kMayaExiting,
+ mayaExiting
+ );
+
+ nodeCreatedID = MDGMessage::addNodeAddedCallback(nodeCreated);
+
+ shaveNodeDeletedID= MDGMessage::addNodeRemovedCallback(
+ shaveNodeDeleted,
+ shaveHairShape::nodeTypeName
+ );
+
+ selectionChangedID= MEventMessage::addEventCallback(
+ "SelectionChanged", selectionChanged
+ );
+
+ // The node added/removed callbacks above keep a count of the number
+ // of hair nodes in the scene. There may already be some so let's get
+ // the counter properly initialized.
+ if (shaveHairShape::initNumShaveNodes() > 0)
+ {
+ // There is additional initialization required when a scene
+ // contains shaveNodes.
+ shaveCallbacks::firstHairNode();
+ }
+
+ // Several of the callbacks above keep track of whether we're in the
+ // middle of loading a file or not. A file load may already be in
+ // progress, so let's get the state of that set correctly.
+ //
+ // (It would be nice to just use MFileIO::isReadingFile() everywhere,
+ // instead of having to track it, but Maya sets isReadingFile to false
+ // before the newly-loaded nodes have had a chance to run their first
+ // compute cycle, so that's no good.)
+ //
+ if (MFileIO::isReadingFile())
+ shaveCallbacks::startLoad();
+ else
+ {
+ // The callbacks above are responsible for initializing any
+ // existing hair nodes once all file loading has completed. File
+ // loading may have completed before the callbacks were set up,
+ // though, so we must initialize any hair nodes which are already
+ // in the scene.
+ initializeHairNodes();
+ }
+}
+
+
+void shaveCallbacks::removeCallbacks()
+{
+ removeCallback(beforeExportID);
+ removeCallback(afterExportID);
+ removeCallback(beforeImportID);
+ removeCallback(afterImportID);
+ removeCallback(beforeImportRefID);
+ removeCallback(afterImportRefID);
+ removeCallback(beforeOpenID);
+ removeCallback(afterOpenID);
+ removeCallback(beforeReferenceID);
+ removeCallback(afterReferenceID);
+ removeCallback(beforeSaveID);
+ removeCallback(afterSaveID);
+
+ removeCallback(afterNewID);
+ removeCallback(mayaExitingID);
+ removeCallback(nodeCreatedID);
+ removeCallback(shaveNodeDeletedID);
+ removeCallback(selectionChangedID);
+}
+
+
+void shaveCallbacks::startLoad()
+{
+ ENTER();
+ shaveUtil::setLoadingFile(true);
+ LEAVE();
+}
+
+
+
+//--------------------------------------------------------------------
+//
+// Internal Interface
+//
+//--------------------------------------------------------------------
+
+bool shaveCallbacks::mCleanUpMELOnExit = true;
+
+//
+// Callback IDs
+//
+MCallbackId shaveCallbacks::beforeExportID = 0;
+MCallbackId shaveCallbacks::afterExportID = 0;
+MCallbackId shaveCallbacks::beforeImportID = 0;
+MCallbackId shaveCallbacks::afterImportID = 0;
+MCallbackId shaveCallbacks::beforeImportRefID = 0;
+MCallbackId shaveCallbacks::afterImportRefID = 0;
+MCallbackId shaveCallbacks::beforeOpenID = 0;
+MCallbackId shaveCallbacks::afterOpenID = 0;
+MCallbackId shaveCallbacks::beforeReferenceID = 0;
+MCallbackId shaveCallbacks::afterReferenceID = 0;
+MCallbackId shaveCallbacks::beforeSaveID = 0;
+MCallbackId shaveCallbacks::afterSaveID = 0;
+
+MCallbackId shaveCallbacks::afterNewID = 0;
+MCallbackId shaveCallbacks::mayaExitingID = 0;
+MCallbackId shaveCallbacks::nodeCreatedID = 0;
+MCallbackId shaveCallbacks::shaveNodeDeletedID = 0;
+MCallbackId shaveCallbacks::selectionChangedID = 0;
+
+
+void shaveCallbacks::beforeExport(void* clientData)
+{
+ ENTER();
+
+ updateNodeVersions();
+
+ //
+ // The renderer gets called during some types of export, so make sure
+ // that it knows about it.
+ //
+ shaveRender::exportStart();
+
+ //
+ // There's a bug in Maya's ASCII format when saving locked, shared
+ // nodes. shaveGlobals is a shared node so if this is an ASCII save,
+ // be sure to unlock it first.
+ //
+ // We can't use MFileIO::fileType() because that reflects the type of
+ // the main scene, not the export file. Instead, we'll look at the
+ // filename and if ends in anything other than '.mb' we'll unlock
+ // the shaveGlobals node.
+ //
+ MString filename = MFileIO::beforeExportFilename().toLowerCase();
+ int len = filename.length();
+
+ if ((len < 3) || (filename.substring(len-2, len) != ".mb"))
+ unlockShaveGlobals();
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::afterExport(void* clientData)
+{
+ ENTER();
+
+ //
+ // The renderer gets called during some types of export, so make sure
+ // that it knows about it.
+ //
+ shaveRender::exportEnd();
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::beforeImport(void* clientData)
+{
+ ENTER();
+ startLoad();
+ LEAVE();
+}
+
+
+void shaveCallbacks::afterImport(void* clientData)
+{
+ ENTER();
+ endLoad(true, false);
+ LEAVE();
+}
+
+
+void shaveCallbacks::beforeImportReference(void* clientData)
+{
+ ENTER();
+ startLoad();
+ LEAVE();
+}
+
+
+void shaveCallbacks::afterImportReference(void* clientData)
+{
+ ENTER();
+ endLoad(true, false);
+ LEAVE();
+}
+
+
+void shaveCallbacks::afterNew(void* clientData)
+{
+ ENTER();
+
+ prepareForNewScene(false);
+
+ MGlobal::executeCommand("shaveSceneCleared");
+ MGlobal::executeCommand("shave_resetSelectedRenderer");
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::beforeOpen(void* clientData)
+{
+ ENTER();
+
+ prepareForNewScene(false);
+ startLoad();
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::afterOpen(void* clientData)
+{
+ ENTER();
+
+
+ endLoad(false, false);
+
+ //MObjectArray shaveHairShapes;
+ //shaveUtil::getShaveNodes(shaveHairShapes);
+ //for(unsigned int i = 0; i < shaveHairShapes.length(); i++)
+ //{
+ // MFnDependencyNode dFn(shaveHairShapes[i]);
+ // MPlug plug = dFn.findPlug("clumps");
+ // int c; plug.getValue(c); plug.setValue(c);
+
+ // ////does not make any effect
+ // //shaveHairShape* sh = (shaveHairShape*)dFn.userNode();
+ // //sh->mDisplayHairCacheDirty = true;
+ //}
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::beforeReference(void* clientData)
+{
+ ENTER();
+ startLoad();
+ LEAVE();
+}
+
+
+void shaveCallbacks::afterReference(void* clientData)
+{
+ ENTER();
+ endLoad(false, true);
+ if(shaveRender::rendererIsVray())
+ {
+ MGlobal::executeCommand("shave_removeShaveRenderCallbacks;");
+ MGlobal::executeCommand("shaveVraySetRenderCallbacks;");
+ }
+ LEAVE();
+}
+
+
+void shaveCallbacks::beforeSave(void* clientData)
+{
+ ENTER();
+
+ //
+ // If there aren't any shaveHairShapes then get rid of the shaveGlobals
+ // node as well.
+ //
+ bool stillHaveNodes = false;
+ MObjectArray nodes;
+
+ shaveUtil::getShaveNodes(nodes);
+
+ if (nodes.length() == 0)
+ {
+ unsigned int i;
+
+ shaveUtil::getShaveGlobalsNodes(nodes);
+
+ if (nodes.length() > 0)
+ {
+ MDGModifier dgMod;
+
+ for (i = 0; i < nodes.length(); i++)
+ {
+ MFnDependencyNode nodeFn(nodes[i]);
+ shaveGlobals* nodePtr = (shaveGlobals*)nodeFn.userNode();
+
+ if (!nodePtr->deleteMe(dgMod)) stillHaveNodes = true;
+ }
+
+ nodes.clear();
+
+ if (!dgMod.doIt()) stillHaveNodes = true;
+ }
+ }
+ else
+ stillHaveNodes = true;
+
+ if (stillHaveNodes)
+ {
+ updateNodeVersions();
+
+ //
+ // There's a bug in Maya's ASCII format when saving locked, shared
+ // nodes. shaveGlobals is a shared node so if this is an ASCII save,
+ // be sure to unlock it first.
+ //
+ if (MFileIO::fileType() == "mayaAscii") unlockShaveGlobals();
+ }
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::afterSave(void* clientData)
+{
+ ENTER();
+ MGlobal::executeCommand("shaveSceneWritten");
+ LEAVE();
+}
+
+
+void shaveCallbacks::nodeCreated(MObject& node, void* clientData)
+{
+ //
+ // This is a bit too expensive to log ENTER/LEAVE on every node
+ // created, even in debug mode, so we'll leave the macros until we know
+ // if it's a node we care about.
+ //
+ MFnDependencyNode nodeFn(node);
+
+ if (nodeFn.typeId() == shaveHairShape::id)
+ {
+ ENTER();
+
+ //
+ // If this is the first shaveHairShape to be added to the scene,
+ // enable various bits of functionality.
+ //
+ if (shaveHairShape::getNumShaveNodes() == 0)
+ firstHairNode();
+
+ shaveHairShape::nodeAdded();
+
+ //if default renderer is changed in Maya prefs
+ //we need to make sure that correct MEL callbacks are set
+ if (!shaveUtil::isLoadingFile())
+ MGlobal::executeCommand("shave_selectedRendererChanged");
+
+ LEAVE();
+ }
+ else if (node.hasFn(MFn::kField))
+ {
+ // If a new field node has been added, mark the existing list as
+ // dirty.
+ shaveUtil::setFieldsDirty();
+ }
+}
+
+
+void shaveCallbacks::shaveNodeDeleted(MObject& node, void* clientData)
+{
+ ENTER();
+
+ shaveHairShape::nodeRemoved();
+
+ //
+ // If this is the last shaveHairShape, remove the rendering callbacks
+ // so that their overhead is not incurred.
+ //
+ if (shaveHairShape::getNumShaveNodes() == 0)
+ {
+ shaveRender::cleanupCallbacks();
+
+ //
+ // Some menu items should be disabled if there are no shave nodes.
+ //
+ // The evalDeferred is required because the node is not fully
+ // removed yet and we don't want the command to execute until it
+ // is.
+ //
+ MGlobal::executeCommand("evalDeferred shave_enableMenus");
+ }
+ else if (shaveHairShape::getNumShaveNodes() < 0)
+ {
+ MGlobal::displayWarning(
+ "Internal Shave problem: negative shaveHairShape count."
+ );
+ }
+
+ LEAVE();
+}
+
+
+void shaveCallbacks::selectionChanged(void* clientData)
+{
+ //
+ // If we're supposed to ignore this selection change, then ignore it
+ // and reset the flag.
+ //
+ if (shaveUtil::isIgnoringNextSelectionChange())
+ {
+ shaveUtil::setIgnoringNextSelectionChange(false);
+ return;
+ }
+
+ //
+ // Apply the selections to all the hair shapes in the scene.
+ //
+ MObjectArray shaveNodes;
+ shaveUtil::getShaveNodes(shaveNodes);
+
+ if (shaveNodes.length() > 0)
+ {
+ MDagPath currentHair = shaveUtil::getCurrentHairShape();
+ unsigned i;
+ MFnDependencyNode nodeFn;
+ shaveHairShape* nodePtr;
+ MSelectionList selections;
+
+ //
+ // Have the non-current hair nodes clear their selection caches.
+ //
+ for (i = 0; i < shaveNodes.length(); i++)
+ {
+ if (!currentHair.isValid() || (shaveNodes[i] != currentHair.node()))
+ {
+ nodeFn.setObject(shaveNodes[i]);
+ nodePtr = (shaveHairShape*)nodeFn.userNode();
+ nodePtr->clearSelections();
+ }
+ }
+
+ //
+ // Have the current hair node update its selection cache to match
+ // the selection list.
+ //
+ if (currentHair.isValid())
+ {
+ MGlobal::getActiveSelectionList(selections);
+
+ nodeFn.setObject(currentHair.node());
+ nodePtr = (shaveHairShape*)nodeFn.userNode();
+ nodePtr->updateSelections(selections);
+ }
+ }
+
+ //
+ // Let the MEL scripts do their stuff.
+ //
+ MGlobal::executeCommand("shave_selectionChanged");
+}
+
+
+void shaveCallbacks::firstHairNode()
+{
+ shaveRender::setupCallbacks();
+
+ // Some menu items should be enabled if there are now shave nodes.
+ //
+ // The evalDeferred is required because the node is not fully
+ // created yet and we don't want the command to execute until it
+ // is.
+ MGlobal::executeCommand("evalDeferred shave_enableMenus");
+}
+
+
+void shaveCallbacks::initializeHairNodes()
+{
+ bool warningRequired = false;
+
+ // Get the plugin version.
+ const char* pluginVersionStr = SHAVEquery_version();
+
+ // If we have any shaveHairShapes in the scene which are from a
+ // newer version of Shave, delete them and give a warning.
+ MObjectArray shaveNodes;
+ shaveUtil::getShaveNodes(shaveNodes);
+
+ MString badSceneVersionStr;
+ MDGModifier dgMod;
+ unsigned int i;
+ unsigned int numShaveNodes = shaveNodes.length();
+
+ for (i = numShaveNodes; i > 0; --i)
+ {
+ MPlug sceneVersionPlug(
+ shaveNodes[i-1], shaveHairShape::aShaveVersion
+ );
+ MString sceneVersionStr;
+
+ sceneVersionPlug.getValue(sceneVersionStr);
+
+ if (shaveUtil::compareShaveVersions(sceneVersionStr, pluginVersionStr)
+ > 0)
+ {
+ MFnDependencyNode nodeFn(shaveNodes[i-1]);
+ shaveHairShape* sn = (shaveHairShape*)nodeFn.userNode();
+
+ sn->deleteMe(dgMod);
+ shaveNodes.remove(i-1);
+ warningRequired = true;
+ badSceneVersionStr = sceneVersionStr;
+ --numShaveNodes;
+ }
+ }
+
+ dgMod.doIt();
+
+ if (warningRequired)
+ {
+ MGlobal::displayError(
+ MString("Shave: plugin version is ") + pluginVersionStr
+ + " but scene contains incompatible elements from a"
+ + " later version of Shave (" + badSceneVersionStr
+ + "). The incompatible elements have been deleted so"
+ + " DO NOT SAVE OUT THIS SCENE or you may lose data."
+ );
+ }
+
+ //
+ // Parameters override
+ //
+ //int pluginVerParts[3];
+ //splitShaveVersion(pluginVersionStr, pluginVerParts);
+ for (i = 0; i < numShaveNodes; i++)
+ {
+ MString sceneVersionStr;
+ int sceneVerParts[3];
+ MPlug sceneVersionPlug(shaveNodes[i], shaveHairShape::aShaveVersion);
+ sceneVersionPlug.getValue(sceneVersionStr);
+ shaveUtil::splitShaveVersion(sceneVersionStr, sceneVerParts);
+ if(sceneVerParts[0] < 6)
+ {
+ float val;
+ MGlobal::displayInfo("Shave: 'Value Variation' was overriden");
+ MPlug valVarPlug(shaveNodes[i], shaveHairShape::shaveParamValueVariation);
+ valVarPlug.getValue(val);
+ valVarPlug.setValue(val/3.0f);
+ }
+ }
+
+ //
+ // Let MEL do its stuff.
+ //
+ MGlobal::executeCommand("shaveSceneLoaded");
+
+ //
+ // If we have any shaveHairShapes in the scene, then make sure that
+ // we have a shaveGlobals node as well.
+ //
+ if (numShaveNodes > 0) MGlobal::executeCommand("shaveGlobals()");
+
+ //
+ // Tell the shaveHairShapes that they can calculate their output
+ // meshes now.
+ //
+ for (i = 0; i < numShaveNodes; i++)
+ {
+ MFnDependencyNode nodeFn(shaveNodes[i]);
+ shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode();
+
+ if (!nodePtr->nodeIsInitialized())
+ nodePtr->initializeNode();
+ }
+}
+
+
+void shaveCallbacks::removeCallback(MCallbackId& id)
+{
+ if (id != 0)
+ {
+ MMessage::removeCallback(id);
+ id = 0;
+ }
+}
+
+
+void shaveCallbacks::unlockShaveGlobals()
+{
+ MObject node = shaveGlobals::getDefaultNode();
+
+ if (!node.isNull())
+ {
+ MFnDependencyNode nodeFn(node);
+
+ if (nodeFn.isLocked()) nodeFn.setLocked(false);
+ }
+}
+
+
+void shaveCallbacks::updateNodeVersions()
+{
+ ENTER();
+
+ //
+ // Make sure that the version numbers on all of the shaveGlobals nodes
+ // are up to date.
+ //
+ MObjectArray nodes;
+ int nodeVersion;
+
+ shaveUtil::getShaveGlobalsNodes(nodes);
+
+ unsigned int numNodes = nodes.length();
+ unsigned int i;
+
+ for (i = 0; i < numNodes; i++)
+ {
+ MPlug plug(nodes[i], shaveGlobals::aNodeVersion);
+
+ plug.getValue(nodeVersion);
+
+ if (nodeVersion != shaveGlobals::kNodeVersion)
+ plug.setValue(shaveGlobals::kNodeVersion);
+ }
+
+ //
+ // Ditto the shaveHairShapes.
+ //
+ MString pluginVersion = SHAVEquery_version();
+ MString sceneVersion;
+
+ nodes.clear();
+ shaveUtil::getShaveNodes(nodes);
+ numNodes = nodes.length();
+
+ for (i = 0; i < numNodes; i++)
+ {
+ MPlug plug(nodes[i], shaveHairShape::aNodeVersion);
+
+ plug.getValue(nodeVersion);
+
+ if (nodeVersion != shaveHairShape::kNodeVersion)
+ plug.setValue(shaveHairShape::kNodeVersion);
+
+ plug.setAttribute(shaveHairShape::aShaveVersion);
+ plug.getValue(sceneVersion);
+
+ if (sceneVersion != pluginVersion)
+ plug.setValue(pluginVersion);
+ }
+
+ nodes.clear();
+
+ LEAVE();
+}
+