# defTwist.py

import sys
import maya.OpenMayaMPx as ommpx
import maya.OpenMaya as om
import maya.cmds as cmds
import math

# Plug-in information:
kPluginNodeName = "twistDeformer"     # The name of the node.
kPluginNodeId = om.MTypeId( 0xBEEF8 ) # A unique ID associated to this node type.

# Some global variables were moved from MPxDeformerNode to MPxGeometryFilter.
# Set som constants to the proper C++ cvars based on the API version
kApiVersion = cmds.about(apiVersion=True)
if kApiVersion < 201600:
	kInput = ommpx.cvar.MPxDeformerNode_input
	kInputGeom = ommpx.cvar.MPxDeformerNode_inputGeom
	kOutputGeom = ommpx.cvar.MPxDeformerNode_outputGeom
	kEnvelope = ommpx.cvar.MPxDeformerNode_envelope
else:
	kInput = ommpx.cvar.MPxGeometryFilter_input
	kInputGeom = ommpx.cvar.MPxGeometryFilter_inputGeom
	kOutputGeom = ommpx.cvar.MPxGeometryFilter_outputGeom
	kEnvelope = ommpx.cvar.MPxGeometryFilter_envelope



#############
# Pug-in
#############
class MyDeformerNode(ommpx.MPxDeformerNode):

	# Static variables which will later be replaced by the node's attributes.
	twistYAttr = om.MObject()

	def __init__(self):
		"""constructor."""
		# Make sure you call the base class's constructor.
		ommpx.MPxDeformerNode.__init__(self)
	
	def deform(self, pDataBlock, pGeometryIterator, pLocalToWorldMatrix, pGeometryIndex):
		"""Deform each vertex using the geometry iterator."""

		# The envelope determines the overall weight of the deformer on the mesh.
		# The envelope is obtained via the OpenMayaMPx.cvar.MPxDeformerNode_envelope (pre Maya 2016) or
		# OpenMayaMPx.cvar.MPxGeometryFilter_envelope (maya 2016)
		# This variable and others like it are generated by SWIG to expose variables or constants declared in C++ header files.
		envelopeAttr = kEnvelope
		envelopeValue = pDataBlock.inputValue(envelopeAttr).asFloat()

		# Get the value of the mesh inflation node attr
		angleData = pDataBlock.inputValue(MyDeformerNode.twistYAttr)
		magnitude = angleData.asDouble()

		# Get the input mesh from the datablock using our getDeformerInputGeometry() helper function.
		inputGeomObj = self.getDeformerInputGeometry(pDataBlock, pGeometryIndex)

		# Compute the list of normals for each vertex inthe mesh.
		normals = om.MFloatVectorArray()
		meshFn = om.MFnMesh(inputGeomObj)
		meshFn.getVertexNormals(True, normals, om.MSpace.kTransform)

		# Iterate over the vertices to move them.
		while not pGeometryIterator.isDone():
			point = pGeometryIterator.position()
			ff = magnitude*point.y*envelopeValue
			if ff != 0.0:
				cct = math.cos(ff)
				cst = math.sin(ff)
				tt = point.x*cct - point.z*cst
				point.z = point.x*cst +point.z*cct
				point.x=tt
			pGeometryIterator.setPosition(point)
			pGeometryIterator.next()


	def getDeformerInputGeometry(self, pDataBlock, pGeometryIndex):
		"""
		Obtain a reference to the input mesh. This mesh will be used to compute our bounding box, and we will also require its normals.

		We use MDataBlock.outputArrayValue() to avoid having to recompute the mesh and propagate this recomputation throughout the
		Dependency Graph.

		ommpx.cvar.MPxDeformerNode_input and ommpx.cvar.MPxDeformerNode_inputGeom(for pre maya 2016) and
		ommpx.cvar.MPxGeometryFilter_input and ommpx.cvar.MPxGeometryFilter_inputGeom (for pre maya 2016) are SWIG-generated
		variables which respectively contain references to the deformer's "input" attr and "inputGeom" attr
		"""
		inputAttr = ommpx.cvar.MPxGeometryFilter_input
		inputGeomAttr = ommpx.cvar.MPxGeometryFilter_inputGeom

		inputAttr = ommpx.cvar.MPxGeometryFilter_input
		inputGeomAttr = ommpx.cvar.MPxGeometryFilter_inputGeom

		inputHandle = pDataBlock.outputArrayValue(inputAttr)
		inputHandle.jumpToElement(pGeometryIndex)
		inputGeomObj = inputHandle.outputValue().child(inputGeomAttr).asMesh()

		return inputGeomObj

def nodeCreator():
	"""Creates an instance of our node class and delivers it to Maya as a pointer."""
	return ommpx.asMPxPtr(MyDeformerNode())

def nodeInitializer():
	"""Defines the input and output attr as statc variables in our plug-in class."""
	# The following MFnNumericAttribute function set will allow us to create our attributes.
	numericAttrFn = om.MFnNumericAttribute()

	#=================
	# INPUT NODE ATTR
	#=================
	# Define the scaling factor attr of the bounding box around the mesh.
	MyDeformerNode.twistYAttr = numericAttrFn.create("twistY", "ty", om.MFnNumericData.kDouble)
	# numericAttrFn.setDefault(0.0)
	numericAttrFn.setKeyable(True)
	MyDeformerNode.addAttribute(MyDeformerNode.twistYAttr)

	print dir(ommpx.cvar)

	MyDeformerNode.attributeAffects(MyDeformerNode.twistYAttr, kOutputGeom)

def initializePlugin( mobject ):
    ''' Initialize the plug-in '''
    mplugin = ommpx.MFnPlugin( mobject )
    try:
        mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator,
                              nodeInitializer, ommpx.MPxNode.kDeformerNode )
    except:
        sys.stderr.write( 'Failed to register node: ' + kPluginNodeName )
        raise
    
def uninitializePlugin( mobject ):
    ''' Uninitializes the plug-in '''
    mplugin = ommpx.MFnPlugin( mobject )
    try:
        mplugin.deregisterNode( kPluginNodeId )
    except:
        sys.stderr.write( 'Failed to deregister node: ' + kPluginNodeName )
        raise








