#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import division
import logging
try:
from maya import cmds
from maya import OpenMaya
from maya import OpenMayaUI
from PySide import QtGui, QtCore, QtTest
import shiboken
except:
pass
log = logging.getLogger('ViewNudger')
[docs]def getRenderer(view):
"""
Gets the current renderer in viewport.
:param view: View to convert point.
:type view: OpenMaya.M3dView
:raises: None
:return: Name of current renderer.
:rtype: str
"""
panelName = OpenMayaUI.MQtUtil.fullName(long(view.widget()))[:-1]
return cmds.modelEditor(panelName, q=True, rnm=True)
[docs]def getSelection():
"""
Gets the current selection.
:raises RuntimeError: If nothing selected.
:return: First index of object selected
:rtype: str
"""
sel = cmds.ls(selection=True, type="transform")
if not sel:
log.error("Nothing selected!")
raise RuntimeError("Nothing selected!")
return sel[0]
[docs]def parseArgs(transformName,
view=None):
"""
Checks input values.
:param transformName: Name of a transform to nudge from.
:type transformName: str
:param view: Optional desired M3dView.
:type view: OpenMaya.M3dView or Str
:raises RuntimeError: If transformName isn't a transform or doesn't exist.
:raises RuntimeError: If view set is not a view.
:return: view
:rtype: OpenMaya.M3dView
"""
if not transformName:
log.error("No transformName supplied.")
raise
if not cmds.objExists(transformName) or \
not cmds.nodeType(transformName) == "transform":
log.error("%s either does not exist or"
" isn't a transform.")
raise
if not view:
log.debug("Getting active view...")
view = OpenMayaUI.M3dView.active3dView()
else:
if not type(view) is OpenMayaUI.M3dView and type(view) is str:
log.debug("Converting %s to OpenMayaUI.M3dView..." % view)
viewStr = view
view = OpenMayaUI.M3dView()
try:
OpenMayaUI.M3dView.getM3dViewFromModelPanel(
viewStr, view)
except:
log.error("%s is not a model panel or view." % view)
raise
else:
log.error("%s is not a view." % view)
raise
return view
[docs]def nudge(transformName=None,
pixelAmount=[1.0, 1.0],
moveObject=False,
rotateView=False,
view=None):
"""
Moves object/camera by pixel amount in x and y.
:param transformName: Name of a transform to nudge from.
:type transformName: str
:param pixelAmount: Pixel amount to nudge in x and y.
:type pixelAmount: list of 2 floats
:param moveObject: Move the object instead of view.
:type moveObject: bool
:param rotateView: Rotate the camera back at point after nudge.
:type rotateView: bool
:param view: View to calculate nudge one.
:type view: OpenMaya.M3dView
:raises: None
:return: None
:rtype: NoneType
"""
view = parseArgs(transformName,
view=view)
renderer = getRenderer(view)
fnCamera, cameraTransform = getCamera(view)
cameraPoint = OpenMaya.MPoint(*cmds.xform(
cameraTransform.fullPathName(),
query=True,
worldSpace=True,
translation=True))
transformPoint = OpenMaya.MPoint(*cmds.xform(
transformName,
query=True,
worldSpace=True,
translation=True))
startDirVec = (cameraPoint - transformPoint)
pointDist = startDirVec.length()
startDirVec.normalize()
log.debug("Object is being moved by %s, %s..." % (
pixelAmount[0], pixelAmount[1]))
x, y = worldToScreen(fnCamera=fnCamera,
cameraPoint=cameraPoint,
transformPoint=transformPoint,
view=view)
log.debug("Object is %s, %s in screen space..." % (x, y))
xyz = screenToWorld(fnCamera=fnCamera,
point2D=[x + pixelAmount[0], y + pixelAmount[1]],
cameraPoint=cameraPoint,
setDistance=pointDist,
view=view)
cmds.undoInfo(openChunk=True)
if moveObject:
cmds.xform(transformName,
translation=[xyz.x, xyz.y, xyz.z],
worldSpace=True)
else:
if rotateView:
loc = cmds.spaceLocator(name="cameraLoc")[0]
cmds.delete(cmds.parentConstraint(
cameraTransform.fullPathName(), loc))
locName = cmds.parent(loc, cameraTransform.fullPathName())[0]
cmds.aimConstraint(transformName, locName,
aimVector=[0.0, 0.0, -1.0],
upVector=[0.0, 1.0, 0.0],
worldUpType="object",
worldUpObject=cameraTransform.fullPathName(),
maintainOffset=True)
offset = (xyz - transformPoint)
cmds.xform(cameraTransform.fullPathName(),
translation=[offset.x, offset.y, offset.z],
relative=True)
if rotateView:
rot = cmds.xform(
locName, query=True, rotation=True)
cmds.xform(cameraTransform.fullPathName(),
rotation=rot,
relative=True,
objectSpace=True)
cmds.delete(locName)
cmds.select(transformName)
if not renderer == "vp2Renderer":
force_update(view)
cmds.undoInfo(closeChunk=True)
[docs]def getCamera(view):
"""
Gets the camera from the current view.
:param view: View to get camera from.
:type view: OpenMaya.M3dView
:raises: None
:return: Camera function set.
:rtype: OpenMaya.MFnCamera
"""
dagCam = OpenMaya.MDagPath()
view.getCamera(dagCam)
fnCamera = OpenMaya.MFnCamera(dagCam)
dagCam.pop()
return fnCamera, dagCam
[docs]def worldToScreen(fnCamera=None,
cameraPoint=None,
transformPoint=None,
view=None):
'''
Converts a world point into a screen point.
:param fnCamera: Camera function set.
:type fnCamera: OpenMaya.MFnCamera
:param cameraPoint: Position to test.
:type cameraPoint: OpenMaya.MPoint
:param transformPoint: Position to test.
:type transformPoint: OpenMaya.MPoint
:param view: View to convert point.
:type view: OpenMaya.M3dView
:raises: None
:return: x and y position of 3d point.
:rtype: list of 2 floats
'''
# Get camera direction.
cameraDir = fnCamera.viewDirection(OpenMaya.MSpace.kWorld)
# Grab project and view matrices.
projectionMatrix = OpenMaya.MMatrix()
view.projectionMatrix(projectionMatrix)
viewMatrix = OpenMaya.MMatrix()
view.modelViewMatrix(viewMatrix)
# Grab viewport width/height.
width = view.portWidth()
height = view.portHeight()
# Check to see that point is in view by checking dot product.
# Positive means it's facing the camera.
pointDir = transformPoint - cameraPoint
z = pointDir * cameraDir
if z < 0.01:
return 0.0, 0.0
# Calculate 2d Screen space.
point3D = transformPoint * (viewMatrix * projectionMatrix)
x = (((point3D.x / point3D.w) + 1.0) / 2.0) * width
y = (((point3D.y / point3D.w) + 1.0) / 2.0) * height
return x, y
[docs]def screenToWorld(fnCamera=None,
point2D=None,
cameraPoint=None,
setDistance=1.0,
view=None):
'''
Converts a screen point to world.
:param fnCamera: Camera function set.
:type fnCamera: OpenMaya.MFnCamera
:param point2D: x and y values to convert to 3d value.
:type point2D: list of 2 floats
:param cameraPoint: Position to test.
:type cameraPoint: OpenMaya.MPoint
:param setDistance: Distance to set returned point from camera.
:type setDistance: float
:param view: View to convert point.
:type view: OpenMaya.M3dView
:raises: None
:return: 2d Point converted to 3d point.
:rtype: OpenMaya.MPoint
'''
cameraDir = fnCamera.viewDirection(OpenMaya.MSpace.kWorld)
# Grab project and view matrices.
projectionMatrix = OpenMaya.MMatrix()
view.projectionMatrix(projectionMatrix)
viewMatrix = OpenMaya.MMatrix()
view.modelViewMatrix(viewMatrix)
# Grab viewport width/height.
width = view.portWidth()
height = view.portHeight()
# Get 2d point in 3d.
point3D = OpenMaya.MPoint()
point3D.x = (2.0 * (point2D[0] / width)) - 1.0
point3D.y = (2.0 * (point2D[1] / height)) - 1.0
viewProjectionMatrix = (viewMatrix * projectionMatrix)
point3D.z = viewProjectionMatrix(3, 2)
point3D.w = viewProjectionMatrix(3, 3)
point3D.x = point3D.x * point3D.w
point3D.y = point3D.y * point3D.w
point3D *= viewProjectionMatrix.inverse()
# Project point into setDistance depth.
directionVec = (point3D - cameraPoint)
directionVec.normalize()
z = directionVec * cameraDir
if z < 0:
directionVec = (cameraPoint - point3D)
directionVec.normalize()
point3D = (directionVec * setDistance) + OpenMaya.MVector(cameraPoint)
return OpenMaya.MPoint(point3D)
[docs]def force_update(view):
'''
Selects the center of the viewport to force it to
refresh properly in VP1. THIS IS AWFUL.
:param view: View to convert point.
:type view: OpenMaya.M3dView
:raises: None
:return: None
:rtype: NoneType
'''
w = shiboken.wrapInstance(long(view.widget()), QtGui.QWidget)
cur_pos = QtGui.QCursor.pos()
p = w.mapToGlobal(w.rect().center())
QtTest.QTest.mouseMove(w)
QtTest.QTest.mousePress(
w, QtCore.Qt.LeftButton, QtCore.Qt.AltModifier, p)
QtTest.QTest.mouseRelease(
w, QtCore.Qt.LeftButton, QtCore.Qt.AltModifier, p)
QtGui.qApp.processEvents()
QtGui.QCursor.setPos(cur_pos)
if __name__ == '__main__':
pixelAmount = [10.0, 10.0]
nudgeView = nudge(transformName="pSphere1",
pixelAmount=pixelAmount,
moveObject=False,
rotateView=True)