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