scripted/filmMoveManip.py

scripted/filmMoveManip.py
1 #-
2 # ==========================================================================
3 # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All
4 # rights reserved.
5 #
6 # The coded instructions, statements, computer programs, and/or related
7 # material (collectively the "Data") in these files contain unpublished
8 # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
9 # licensors, which is protected by U.S. and Canadian federal copyright
10 # law and by international treaties.
11 #
12 # The Data is provided for use exclusively by You. You have the right
13 # to use, modify, and incorporate this Data into other products for
14 # purposes authorized by the Autodesk software license agreement,
15 # without fee.
16 #
17 # The copyright notices in the Software and this entire statement,
18 # including the above license grant, this restriction and the
19 # following disclaimer, must be included in all copies of the
20 # Software, in whole or in part, and all derivative works of
21 # the Software, unless such copies or derivative works are solely
22 # in the form of machine-executable object code generated by a
23 # source language processor.
24 #
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
26 # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
27 # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
28 # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
29 # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
30 # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
31 # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
32 # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
33 # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
34 # OR PROBABILITY OF SUCH DAMAGES.
35 #
36 # ==========================================================================
37 #+
38 
39 #
40 # filmMoveManip.py
41 # Scripted plug-in that displays a manipulator that
42 # modifies the film translate horizontal and vertical
43 # values.
44 #
45 # To use this plug-in:
46 # 1. execute the following Python:
47 # import maya
48 # maya.cmds.loadPlugin("filmMoveManip.py")
49 # maya.cmds.spFilmMoveManipCtxCmd( 'spFilmMoveManipContext1' )
50 # maya.cmds.setParent( 'Shelf1' )
51 # maya.cmds.toolButton( 'spFilmMoveManip1', cl='toolCluster', t='spFilmMoveManipContext1', i1="filmMoveManip.xpm" )
52 # 2. Open the outliner and select a camera shape such as perspShape
53 # 3. A manipulator with a horizontal and vertical axis will be displayed.
54 # 4. Move the axis to modify the film translate
55 #
56 # A keyframing example:
57 # This plug-in uses dependentPlugsReset() and addDependentPlugs() to
58 # make the U and V translate values keyable via the manipulator. If you
59 # comment out the calls and try to set a key (e.g. by pressing the "s"
60 # key) you will get an error that no keyable values are associated with
61 # the manipulator. To demonstrate keyframing try the following:
62 #
63 # 5. Follow steps 1-4 above
64 # 6. In the Outliner, select perspShape, topShape and frontShape
65 # 7. In the Channel Control panel, make "Film Translate H" keyable
66 # 8. Drag each of the three manips horizontally in the viewport
67 # 9. Press the "s" key to set a keyframe
68 # 10.Select the three cameraShapes individually. Each should have
69 # its Film Translate H value keyed
70 #
71 
72 import sys
73 import manipulatorMath
74 import maya.OpenMaya as OpenMaya
75 import maya.OpenMayaUI as OpenMayaUI
76 import maya.OpenMayaMPx as OpenMayaMPx
77 import maya.OpenMayaRender as OpenMayaRender
78 
79 filmMoveManipId = OpenMaya.MTypeId(0x8104B)
80 contextCmdName = "spFilmMoveManipCtxCmd"
81 nodeName = "spFilmMoveManip"
82 
83 # class that holds geometry points
84 class manipulatorGeometry:
85  scale = 3
86  def horizontalStart(self):
87  p = OpenMaya.MPoint(0,0,0) * self.scale
88  # print "hstart: %g %g %g" % (p.x,p.y,p.z)
89  return p
90  def horizontalEnd(self):
91  p = OpenMaya.MPoint(1,0,0) * self.scale
92  # print "hend:%g %g %g" % (p.x,p.y,p.z)
93  return p
94  def verticalStart(self):
95  p = OpenMaya.MPoint(0,0,0) * self.scale
96  # print "vstart:%g %g %g" % (p.x,p.y,p.z)
97  return p
98  def verticalEnd(self):
99  p = OpenMaya.MPoint(0,1,0) * self.scale
100  # print "vend: %g %g %g" % (p.x,p.y,p.z)
101  return p
102 
103 # manipulator class
104 class filmMoveManip(OpenMayaMPx.MPxManipulatorNode):
105 
106  # Value indices
107  horizontalIndex = 0
108  verticalIndex = 0
109 
110  # GL names
111  glHorizontalName = 0
112  glVerticalName = 0
113 
114  # Manipulator translation
115  translation = OpenMaya.MPoint()
116 
117  # Find the GL function table for the
118  # draw method
119  glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
120  glFT = glRenderer.glFunctionTable()
121 
122  def __init__(self):
123  OpenMayaMPx.MPxManipulatorNode.__init__(self)
124 
125  def postConstructor(self):
126  try:
127  su = OpenMaya.MScriptUtil(0)
128  iptr = su.asIntPtr()
129  self.addDoubleValue( "horizontalValue", 0, iptr )
130  self.horizontalIndex = su.getInt(iptr)
131  except:
132  sys.stderr.write("Failed to add horizontal double value\n")
133  raise
134 
135  try:
136  su = OpenMaya.MScriptUtil(0)
137  iptr = su.asIntPtr()
138  self.addDoubleValue( "verticalValue", 0, iptr )
139  self.verticalIndex = su.getInt(iptr)
140  except:
141  sys.stderr.write("Failed to add vertical double value\n")
142  raise
143 
144  def draw(self,view,dagpath,displayStyle,displayStatus):
145 
146  # get the 4 points of the manipulator
147  mo = manipulatorGeometry()
148  vs = mo.verticalStart()
149  ve = mo.verticalEnd()
150  hs = mo.horizontalStart()
151  he = mo.horizontalEnd()
152 
153  # pre
154  view.beginGL()
155  self.glFT.glPushMatrix()
156  self.glFT.glTranslated(self.translation.x,self.translation.y,self.translation.z)
157 
158  # get the first handle
159  su = OpenMaya.MScriptUtil(0)
160  iptr = su.asUintPtr()
161  self.glFirstHandle( iptr )
162  startIndex = su.getUint(iptr)
163 
164  # draw the manip
165  self.glHorizontalName = startIndex
166  self.colorAndName( view, self.glHorizontalName, True, self.mainColor() )
167  self.glFT.glBegin( OpenMayaRender.MGL_LINES )
168  self.glFT.glVertex3d( hs.x, hs.y, hs.z )
169  self.glFT.glVertex3d( he.x, he.y, he.z )
170  self.glFT.glEnd()
171 
172  self.glVerticalName = self.glHorizontalName + 1
173  self.colorAndName( view, self.glVerticalName, True, self.mainColor() )
174  self.glFT.glBegin( OpenMayaRender.MGL_LINES )
175  self.glFT.glVertex3d( vs.x, vs.y, vs.z )
176  self.glFT.glVertex3d( ve.x, ve.y, ve.z )
177  self.glFT.glEnd()
178 
179  # post
180  self.glFT.glPopMatrix()
181  view.endGL()
182 
183  def doPress(self,view):
184  self.translation = OpenMaya.MPoint()
185  self.updateDragInformation(view)
186 
187  def doDrag(self,view):
188  self.updateDragInformation(view)
189 
190  def doRelease(self,view):
191  # Only update the attribute on release
192  self.updateDragInformation(view,True)
193 
194  def getPlaneForView(self,view):
195  mo = manipulatorGeometry()
196  vs = mo.verticalStart()
197  ve = mo.verticalEnd()
198  hs = mo.horizontalStart()
199  he = mo.horizontalEnd()
200  v1 = he - hs
201  v2 = vs - ve
202  normal = v1 ^ v2
203  normal.normalize()
204 
205  plane = manipulatorMath.planeMath()
206  plane.setPlane( vs, normal )
207 
208  return plane
209 
210  def updateDragInformation(self,view,updateAttribute = False):
211  # mouse point and direction
212  localMousePoint = OpenMaya.MPoint()
213  localMouseDirection = OpenMaya.MVector()
214  self.mouseRay( localMousePoint, localMouseDirection )
215 
216  # plane that mouse will move in
217  plane = self.getPlaneForView( view )
218 
219  # intersect mouse into plane
220  mouseIntersect = OpenMaya.MPoint()
221  (worked,mouseIntersect) = plane.intersect( localMousePoint, localMouseDirection )
222  if not worked:
223  print "Failed to intersect plane"
224  return
225 
226  # restrict the point movement
227  su = OpenMaya.MScriptUtil(0)
228  uintptr = su.asUintPtr()
229  self.glActiveName( uintptr )
230  active = su.getUint(uintptr)
231 
232  mo = manipulatorGeometry()
233 
234  start = None
235  end = None
236  if active == self.glHorizontalName:
237  start = mo.horizontalStart()
238  end = mo.horizontalEnd()
239  elif active == self.glVerticalName:
240  start = mo.verticalStart()
241  end = mo.verticalEnd()
242  else:
243  raise "Unknown GL active component"
244 
245  vstart_end = start - end
246  line = manipulatorMath.lineMath()
247  line.setLine( start, vstart_end )
248 
249  closestPoint = OpenMaya.MPoint()
250  (worked,closestPoint) = line.closestPoint( mouseIntersect )
251  if not worked:
252  print "Failed to find closest point to line"
253  return
254 
255  self.translation = closestPoint
256  # print "mi: %g %g %g" % (mouseIntersect.x,mouseIntersect.y,mouseIntersect.z)
257 
258  if updateAttribute:
259  m = manipulatorMath.maxOfAbsThree(closestPoint.x,closestPoint.y,closestPoint.z)
260  m = m / 10.0 # slow down operation
261  if active == self.glHorizontalName:
262  self.setDoubleValue(self.horizontalIndex,m)
263  elif active == self.glVerticalName:
264  self.setDoubleValue(self.verticalIndex,m)
265 
266  def connectToDependNode(self, node):
267  nodeFn = OpenMaya.MFnDependencyNode(node)
268 
269  try:
270  hPlug = nodeFn.findPlug("filmTranslateH")
271  vPlug = nodeFn.findPlug("filmTranslateV")
272 
273  su = OpenMaya.MScriptUtil(0)
274  iptr = su.asIntPtr()
275  self.connectPlugToValue( hPlug, self.horizontalIndex, iptr )
276  self.connectPlugToValue( vPlug, self.verticalIndex, iptr )
277 
278  # Mark the plugs keyable.
279  self.addDependentPlug(hPlug)
280  self.addDependentPlug(vPlug)
281  except:
282  sys.stderr.write( "Error finding and connecting plugs\n" )
283  raise
284 
285  try:
286  OpenMayaMPx.MPxManipulatorNode.finishAddingManips(self)
287  OpenMayaMPx.MPxManipulatorNode.connectToDependNode(self,node)
288  except:
289  sys.stderr.write( "Error when finishing node connection\n" )
290  raise
291 
292 
293 def filmMoveManipCreator():
294  return OpenMayaMPx.asMPxPtr( filmMoveManip() )
295 
296 def filmMoveManipInitialize():
297  print "Initializing film move manipulator"
298 
299 class filmMoveManipContext(OpenMayaMPx.MPxSelectionContext):
300  def __init__(self):
301  OpenMayaMPx.MPxSelectionContext.__init__(self)
302 
303  def toolOnSetup(self,event):
304  updateManipulators(self)
305  OpenMaya.MModelMessage.addCallback(OpenMaya.MModelMessage.kActiveListModified, updateManipulators, self)
306 
307 def updateManipulators(clientData):
308  clientData.deleteManipulators()
309  selectionList = OpenMaya.MSelectionList()
310 
311  # Loop through the selection list, linking the film translate
312  # plugs on each selected camera to the manipulator.
314  selectionIter = OpenMaya.MItSelectionList(selectionList, OpenMaya.MFn.kInvalid)
315  while not selectionIter.isDone():
316  dependNode = OpenMaya.MObject()
317  selectionIter.getDependNode(dependNode)
318  if dependNode.isNull() or not dependNode.hasFn(OpenMaya.MFn.kCamera):
319  print "depend node is null or not a camera"
320  selectionIter.next()
321  continue
322 
323  dependNodeFn = OpenMaya.MFnDependencyNode(dependNode)
324  hPlug = dependNodeFn.findPlug("filmTranslateH", False)
325  vPlug = dependNodeFn.findPlug("filmTranslateV", False)
326  if hPlug.isNull() or vPlug.isNull():
327  print "One of filmTranslate H,V plugs are null"
328  selectionIter.next()
329  continue
330 
331  manipObject = OpenMaya.MObject()
332  manipulator = OpenMayaMPx.MPxManipulatorNode.newManipulator(nodeName, manipObject)
333  if manipulator is not None:
334  # Clear the list of keyable plugs.
335  manipulator.dependentPlugsReset()
336 
337  clientData.addManipulator(manipObject)
338  manipulator.connectToDependNode(dependNode)
339  selectionIter.next()
340 
341 
342 class filmMoveManipCtxCmd(OpenMayaMPx.MPxContextCommand):
343  def __init__(self):
344  OpenMayaMPx.MPxContextCommand.__init__(self)
345 
346  def makeObj(self):
347  return OpenMayaMPx.asMPxPtr( filmMoveManipContext() )
348 
349 
350 def contextCmdCreator():
351  return OpenMayaMPx.asMPxPtr( filmMoveManipCtxCmd() )
352 
353 
354 # initialize the script plug-in
355 def initializePlugin(mobject):
356  mplugin = OpenMayaMPx.MFnPlugin(mobject)
357 
358  try:
359  mplugin.registerContextCommand( contextCmdName, contextCmdCreator )
360  except:
361  print "Failed to register context command: %s" % contextCmdName
362  raise
363 
364  try:
365  mplugin.registerNode(nodeName, filmMoveManipId, filmMoveManipCreator, filmMoveManipInitialize, OpenMayaMPx.MPxNode.kManipulatorNode)
366  except:
367  print "Failed to register node: %s" % nodeName
368  raise
369 
370 # uninitialize the script plug-in
371 def uninitializePlugin(mobject):
372  mplugin = OpenMayaMPx.MFnPlugin(mobject)
373  try:
374  mplugin.deregisterContextCommand(contextCmdName)
375  except:
376  print "Failed to deregister context command: %s" % contextCmdName
377  raise
378 
379  try:
380  mplugin.deregisterNode(filmMoveManipId)
381  except:
382  print "Failed to deregister node: %s" % nodeName
383  raise