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