polyModifierCmd.h

//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

#ifndef _polyModifierCmd
#define _polyModifierCmd
//
// 
// File: polyModifierCmd.h
//
// MEL Command: polyModifierCmd
//
// Author: Lonnie Li
//
// Overview:
//
//              polyModifierCmd is a generic base class designed to aid in modifying
//              polygonal meshes. All polys in Maya possess two features: construction
//              history and tweaks. Both of these have a large impact on the structure
//              of the object as well as how it can be further manipulated. However,
//              they cannot be easily implemented, which is the why we need this abstracted
//              class. polyModifierCmd will automatically handle the DG maintenance of
//              construction history and tweaks on a polygonal mesh.
//
//              To understand what effect both construction history and tweaks have on
//              a mesh, we need to understand the states of a node. There are three things
//              which will affect the state of a node regarding construction history and
//              tweaks. That is:
//
//              (1) Does construction history exist?
//              (2) Do tweaks exist?
//              (3) Is construction history turned on?
//
//              The answer to each of these questions changes how the mesh is interpreted,
//              which in turn affects how the mesh can be accessed/modified. Under each
//              circumstance, new modifications on the mesh go through a standard series
//              of events. To further illustrate how these affect the interpretation of
//              a mesh, we'll delve into each case separately looking at the problems
//              that we face in each case.
//
//              In the case where construction history exists, the existence of construction
//              history informs us that there is a single linear DG chain of nodes upstream
//              from the mesh node. That chain is the history chain. At the top of the chain
//              we have the "original" mesh and at the bottom we have the "final" mesh,
//              where "original" and "final" represent the respective state of the node with
//              regards to mesh's history. Each of these nodes are adjoined via the
//              inMesh/outMesh attributes, where in and out designate the dataflow into
//              and out of the node. Now, with that aside, attempting to modify a node
//              via mutator methods will always write the information onto the inMesh
//              attribute (except in the case of tweaks, where it writes to the cachedInMesh).
//              This presents a problem if history exists since a DG evaluation will overwrite
//              the inMesh of the mesh node, due to the connection from the outMesh of the
//              node directly upstream from the mesh. This will discard any modifications
//              made to the mesh.
//
//              So obviously modifying a mesh directly isn't possible when history exists.
//              To properly modify a mesh with history, we introduce the concept of a modifier
//              node. This polyModifierNode will encapsulate the operations on the mesh
//              and behave similarly to the other nodes in the history chain. The node will
//              then be inserted into the history chain so that on each DG evaluation, it
//              is always accounted for. The diagram below shows the before and after
//              of modifying a mesh with history.
//
//
//              Before modification:
//
//        ____                   ____
//       /    \                 /    \
//      | Hist | O --------> O | mesh | O
//       \____/  |           |  \____/  |
//            outMesh      inMesh    outMesh
//
//
//              After modification:
//
//        ____                   ________                   ____
//       /    \                 /        \                 /    \
//      | Hist | O --------> O | modifier | O --------> O | mesh | O
//       \____/  |           |  \________/  |           |  \____/  |
//            outMesh      inMesh        outMesh      inMesh    outMesh
//
//
//              (Figure 1. Nodes with History)
//
//
//              In the case of tweaks: Tweaks are stored on a hidden attribute on the
//              mesh. Tweaks are manual component modifications on a mesh (eg. repositioning
//              a vertex). During a DG evaluation, the DG takes the inMesh attribute of
//              the node and adds the tweak values onto it to get the final value. From this
//              knowledge we can see that inserting a modifier node ahead of the mesh node
//              reverses the order of operations which can be crucial to the structure of the
//              resulting mesh if the modification is a topological change. To avoid this
//              problem, we retrieve the tweaks off of the mesh, remove it from the mesh and
//              place the tweaks into a polyTweak node. The tweak node is then inserted ahead
//              of the modifier node to preserve the order of operations:
//
//
//              Before modification:
//
//                               Tweak
//        ____                   __O__
//       /    \                 /     \
//      | Hist | O --------> O | mesh  | O
//       \____/  |           |  \_____/  |
//            outMesh      inMesh     outMesh
//
//
//              After modification:
//
//                                                                      Empty Tweak
//        ____                _____                ________                __O__
//       /    \              /     \              /        \              /     \
//      | Hist | O -----> O | Tweak | O -----> O | modifier | O -----> O | mesh  | O
//       \____/  |        |  \_____/  |        |  \________/  |        |  \_____/  |
//            outMesh   inMesh     outMesh   inMesh        outMesh   inMesh     outMesh
//
//
//              (Figure 2. Node with Tweaks)
//              
//
//              The last of the questions deals with whether or not the user has construction
//              history turned on or off. This will change how the node should be modified
//              as well as what the node will look like in the DG following the operation. With
//              history turned on, the user has selected that they would like to keep a
//              history chain. So in that case, the resulting mesh would look like the above
//              diagrams following the operation. On the other hand, with history turned off,
//              the user has selected that they would not like to see a history chain. From here
//              there are two possible choices to modify the mesh:
//
//              (1) Operate on the mesh directly
//              (2) Use the DG, like in the above diagrams, then collapse the nodes down into the mesh.
//
//              The only exception to note out of this case is that if the node already possesses
//              history (as would be answered by the first question), this preference is ignored.
//              If a node has history, we continue to use history. The user is imposed with the
//              task of deleting the history on the object first if they would not like to continue
//              using history.
//
//
//              With History:
//
//        ____                   ____
//       /    \                 /    \
//      | Hist | O --------> O | mesh | O
//       \____/  |           |  \____/  |
//            outMesh      inMesh    outMesh
//
//
//              Without History:
//
//            ____
//           /    \
//        O | mesh | O     (All history compressed onto the inMesh attribute)
//        |  \____/  |
//      inMesh    outMesh
//
//
//              (Figure 3. Node with History preference)
//
//
//              This section has described the "why" part of the question regarding this command.
//              Following sections will provide a more in depth look at "how" this command
//              treats each of these situations and what it really does behind the scenes
//              to handle the above cases.
//
//
// How it works:
//
//              This command approaches the various node state cases similarly to the way
//              Maya works with construction history and tweaks in polygons. It is important
//              to note that history and tweaks are independent states having no effect on
//              each other (in terms of their state). Thus this section will describe each
//              case independently:
//
//              1) History
//
//                      For history, there are 4 cases that need to be considered:
//
//                      (a) History (yes)       -       RecordHistory (yes)
//                      (b) History (yes)       -       RecordHistory (no)
//                      (c) History (no)        -       RecordHistory (yes)
//                      (d) History (no)        -       RecordHistory (no)
//
//                      For (a) and (b), this command treats the node identically. Regardless of
//                      whether recording history is turned on or off, if history already exists
//                      on the node, we treat the node as though recording history is on. As such
//                      the command performs the following steps:
//
//                              (i)             Create a modifier node.
//                              (ii)    Find the node directly upstream to the mesh node.
//                              (iii)   Disconnect the upstream node and the mesh node.
//                              (iv)    Connect the upstream node to the modifier node.
//                              (v)             Connect the modifier node to the mesh node.
//                              (vi)    Done!
//
//                      For (c), polyModifierCmd needs to generate an input mesh to drive the
//                      modifier node. To do this, the mesh node is duplicated and connected
//                      like the upstream node in the previous two cases:
//
//                              (i)             Create a modifier node.
//                              (ii)    Duplicate the mesh node.
//                              (iii)   Connect the duplicate mesh node to the modifier node
//                              (iv)    Connect the modifier node to the mesh node
//                              (v)             Done!
//
//                      For (d), this command is a bit more complicated. There are two approaches
//                      that can be done to respect the fact that no history is desired. The first
//                      involves using the approach in case (c) and simply "baking" or "flattening"
//                      the nodes down into the mesh node. Unfortunately, this presents some
//                      serious problems with undo, as the Maya API in its current state does not
//                      support construction history manipulation. Resorting to the MEL command:
//                      "delete -ch" would be possible, however undoing the operation would not be
//                      trivial as calling an undo from within an undo could destabilize the undo
//                      queue.
//
//                      The second alternative and one that is currently implemented by this class
//                      is to respect the "No Construction History" preference strictly by
//                      not modifying the history chain at all and simply operating directly on the
//                      mesh. In order to do this and maintain generality, a hook is provided for
//                      derived classes to override and place in the code used to directly modify the
//                      mesh. polyModifierCmd will only call this method under the circumstances
//                      of case (d). To prevent code duplication between the operations done in the
//                      modifierNode and the command's directModifier implementation, the concept of
//                      a factory is used. It is recommended that an instance of such a factory is
//                      stored locally on the command much like it will be on the node. See
//                      polyModifierNode.h and polyModifierFty.h for more details.
//
//
//              2) Tweaks
//
//                      Tweaks are handled as noted above in the description section. However, how
//                      they are treated is dependent on the state of history. Using the four cases
//                      above:
//
//                      For (a), (b) and (c), it is as described in the description section:
//
//                              (i)             Create a tweak node.
//                              (ii)    Extract the tweaks from the mesh node.
//                              (iii)   Copy the tweaks onto the tweak node.
//                              (iv)    Clear the tweaks from the mesh node.
//                              (v)             Clear the tweaks from the duplicate mesh node (for case (c) only!)
//
//                      For (d), we have yet another limitation. Tweaks are not handled in this case
//                      because of the same circumstances which give rise to the limitation in the
//                      history section. As such, topological changes may result in some odd behaviour
//                      unless the workaround provided in the limitations section is used.
//
//
// How to use:
//
//              To use this command there are several things that are required based on the needs
//              of the command:
//
//              Step 1: polyModifierFty
//
//              1) Create a factory derived from polyModifierFty
//              2) Find and assign any inputs that your modifier will need onto the factory.
//              3) Override the polyModifierFty::doIt() method of the factory
//              4) Place your modifier code into the doIt() method of the factory
//
//              Step 2: polyModifierNode
//
//              1) Create a node derived from polyModifierNode
//              2) Add any additional input attributes onto the node
//              3) Associate the attributes (ie. inMesh --> affects --> outMesh)
//              4) Add an instance of your polyModifierFty to the node
//              5) Override the MPxNode::compute() method
//              6) Retrieve inputs from attributes, setup the factory and call its doIt() in compute()
//
//              Step 3: polyModifierCmd
//
//              1) Create a command derived from polyModifierCmd
//
//              ---
//
//              2) Override the polyModifierCmd::initModifierNode() method
//              3) Place your node setup code inside the initModifierNode()
//
//              ---
//
//              4) Add an instance of your polyModifierFty to the command
//              5) Cache any input parameters for the factory on the command
//              6) Override the polyModifierCmd::directModifier() method
//              7) Place your factory setup code and call its doIt() in directModifier()
//
//              ---
//
//              8) Override the MPxCommand::doIt() method
//              9) Place your setup code inside the doIt()
//              10) Place the polyModifierCmd setup code inside the doIt()
//                  (ie. setMeshNode(), setModifierNodeType())
//              11) Call polyModifierCmd::doModifyPoly() inside the doIt()
//
//              ---
//
//              12) Override the MPxCommand::redoIt() method
//              13) Call polyModifierCmd::redoModifyPoly() in redoIt()
//
//              ---
//
//              14) Override the MPxCommand::undoIt() method
//              15) Call polyModifierCmd::undoModifyPoly() in undoIt()
//
//              For more details on each of these steps, please visit the associated method/class
//              headers.
//
//
// Limitations:
//
//              There is one limitation in polyModifierCmd:
//
//              (1) Duplicate mesh created under the "No History / History turned on" case not undoable
//
//              Case (1):
//
//                      Under the "No History / History turned on" case, history is allowed so the DG
//                      is used to perform the operation. However, every polyModifierNode requires
//                      an input mesh and without any prior history, a mesh input needs to be created.
//                      polyModifierCmd compensates for this by duplicating the meshNode and marking
//                      it as an intermediate object.
//
//                      The problem with this duplication is that the only duplicate method in the
//                      Maya API resides in MFnDagNode, which does not have an associated undo/redo
//                      mechanism. Attempting to manually delete the node by use of a DGmodifier or
//                      the MEL delete command will break the undo/redo mechanism for the entire
//                      command. As a result, this duplicate mesh is a remnant of each instance of the
//                      command excluding undo/redo.
//
//                      To work past this limitation, a manual delete from the MEL command line is
//                      required.
//

// General Includes
//
#include <maya/MIntArray.h>
#include <maya/MFloatVectorArray.h>
#include <maya/MDagPath.h>
#include <maya/MDGModifier.h>
#include <maya/MDagModifier.h>
#include <maya/MPlug.h>

// Proxies
//
#include <maya/MPxCommand.h>


class polyModifierCmd : public MPxCommand
{
public:

                                polyModifierCmd();
        virtual                                 ~polyModifierCmd();

// Restrict access to derived classes only
//
protected:

        // polyModifierCmd Initialization //

        // Target polyMesh to modify
        //
        void                                                    setMeshNode( MDagPath mesh );
        MDagPath                                                getMeshNode() const;

        // Modifier Node Type
        //
        void                                                    setModifierNodeType( MTypeId type );
        void                                                    setModifierNodeName( MString name );
        MTypeId                                                 getModifierNodeType() const;
        MString                                                 getModifierNodeName() const;

        // polyModifierCmd Execution //

        // initModifierNode - Derived classes should override this method if
        //                                        they wish to initialize input attributes on the 
        //                                        modifierNode
        //
        virtual MStatus                                 initModifierNode( MObject modifierNode );

        // directModifier - Derived classes should override this method to provide
        //                                      direct modifications on the meshNode in the case where
        //                                      no history exists and construction history is turned off.
        //                                      (ie. no DG operations desired)
        //
        //                                      This method is called only if history does not exist and
        //                                      history is turned off. At this point, a handle to the
        //                                      meshNode is passed in so a derived class may directly
        //                                      modify the mesh.
        //
        virtual MStatus                                 directModifier( MObject mesh );

        MStatus                                                 doModifyPoly();
        MStatus                                                 redoModifyPoly();
        MStatus                                                 undoModifyPoly();

private:

        // polyModifierCmd Internal Processing Data //

        // This structure is used to maintain the data vital to the modifyPoly method.
        // It is necessary to simplify parameter passing between the methods used inside
        // modifyPoly (specifically inside connectNodes()). The diagram below dictates
        // the naming style used:
        //
        // NOTE: modifierNode is intentionally left out of this structure since it
        //               is given protected access to derived classes.
        //
        // Before:
        //
        // (upstreamNode) *src -> dest* (meshNode)
        //
        // After:
        //
        // (upstreamNode) *src -> dest* (modifierNode) *src -> dest* (meshNode)
        //
        struct modifyPolyData
        {
                MObject meshNodeTransform;
                MObject meshNodeShape;
                MPlug   meshNodeDestPlug;
                MObject meshNodeDestAttr;

                MObject upstreamNodeTransform;
                MObject upstreamNodeShape;
                MPlug   upstreamNodeSrcPlug;
                MObject upstreamNodeSrcAttr;

                MObject modifierNodeSrcAttr;
                MObject modifierNodeDestAttr;

                MObject tweakNode;
                MObject tweakNodeSrcAttr;
                MObject tweakNodeDestAttr;
        };

        // polyModifierCmd Internal Methods //

        bool                                    isCommandDataValid();
        void                                    collectNodeState();

        // Modifier node methods
        //
        MStatus                                 createModifierNode( MObject& modifierNode );

        // Node processing methods (need to be executed in this order)
        //
        MStatus                                 processMeshNode( modifyPolyData& data );
        MStatus                                 processUpstreamNode( modifyPolyData& data );
        MStatus                                 processModifierNode( MObject modifierNode,
                                                                                                 modifyPolyData& data );
        MStatus                                 processTweaks( modifyPolyData& data );

        // Node connection method
        //
        MStatus                                 connectNodes( MObject modifierNode );

        // Mesh caching methods - Only used in the directModifier case
        //
        MStatus                                 cacheMeshData();
        MStatus                                 cacheMeshTweaks();

        // Undo methods
        //
        MStatus                                 undoCachedMesh();
        MStatus                                 undoTweakProcessing();
        MStatus                                 undoDirectModifier();

        // polyModifierCmd Utility Methods //

        MStatus                                 getFloat3PlugValue( MPlug plug, MFloatVector& value );
        MStatus                                 getFloat3asMObject( MFloatVector value, MObject& object );

        // polyModifierCmd Data //

        // polyMesh
        //
        bool                            fDagPathInitialized;
        MDagPath                        fDagPath;
        MDagPath                        fDuplicateDagPath;

        // Modifier Node Type
        //
        bool                            fModifierNodeTypeInitialized;
        bool                            fModifierNodeNameInitialized;
        MTypeId                         fModifierNodeType;
        MString                         fModifierNodeName;

        // Node State Information
        //
        bool                            fHasHistory;
        bool                            fHasTweaks;
        bool                            fHasRecordHistory;

        // Cached Tweak Data (for undo)
        //
        MIntArray                       fTweakIndexArray;
        MFloatVectorArray       fTweakVectorArray;

        // Cached Mesh Data (for undo in the 'No History'/'History turned off' case)
        //
        MObject                         fMeshData;

        // DG and DAG Modifier
        //
        //        - We need both DAG and DG modifiers since the MDagModifier::createNode()
        //              method is overridden and specific to DAG nodes. So to keep
        //              the operations consistent we will only use the fDagModifier
        //              when dealing with the DAG.
        //
        //        - There is a limitation between the reparentNode() and deleteNode()
        //              methods on the MDagModifier. The deleteNode() method does some
        //              preparation work before it enqueues itself in the MDagModifier list
        //              of operations, namely, it looks at it's parents and children and
        //              deletes them as well if they are the only parent/child of the node
        //              scheduled to be deleted.
        //
        //              This conflicts with our call to MDagModifier::reparentNode(),
        //              since we want to reparent the shape of a duplicated node under
        //              another node and then delete the transform of that node. Now you 
        //              can see that since the reparentNode() doesn't execute until after
        //              the MDagModifier::doIt() call, the scheduled deleteNode() call
        //              still sees the child and marks it for delete. The subsequent
        //              doIt() call reparents the shape and then deletes both it and the
        //              transform.
        //
        //              To avoid this conflict, we separate the calls individually and
        //              perform the reparenting (by calling a doIt()) before the deleteNode()
        //              method is enqueued on the modifier.
        //
        MDGModifier                     fDGModifier;
        MDagModifier            fDagModifier;
};

//
// Inlines
//

// polyMesh
//
inline void polyModifierCmd::setMeshNode( MDagPath mesh )
{
        fDagPath = mesh;
        fDagPathInitialized = true;
}

inline MDagPath polyModifierCmd::getMeshNode() const
{
        return fDagPath;
}

// Modifier Node Type
//
inline void polyModifierCmd::setModifierNodeType( MTypeId type )
{
        fModifierNodeType = type;
        fModifierNodeTypeInitialized = true;
}

inline void polyModifierCmd::setModifierNodeName( MString name )
{
        fModifierNodeName = name;
        fModifierNodeNameInitialized = true;
}

inline MTypeId polyModifierCmd::getModifierNodeType() const
{
        return fModifierNodeType;
}

inline MString polyModifierCmd::getModifierNodeName() const
{
        return fModifierNodeName;
}

#endif

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