This is my script. This is not the final version it is still in dev so it's
normal if some thing looks odd. I had to overwrite all the dependencies so
that it works in one file. I just noticed one thing. The I add a sphere in
script on this code. But the object I add in the scene is normally a
reference. So the refresh works with an object add in code but not in
reference ...
--
You received this message because you are subscribed to the Google Groups
"Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/python_inside_maya/d368b9ec-bf4d-424f-9086-156c9d1ae53d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
# ====================================
# Import Modules
# ====================================
import os
import types
from pymel import core as pmc
from maya import OpenMaya, OpenMayaUI, cmds
from maya.OpenMaya import MScriptUtil
# ====================================
# Picker
# ====================================
def func_test(matrix):
s_sphere = cmds.polySphere()[0]
dp_sphere = get_dag_path(s_sphere)
mfn_transform_sphere = OpenMaya.MFnTransform(dp_sphere)
mfn_transform_sphere.set(OpenMaya.MTransformationMatrix(matrix))
class PickerContext(object):
CONTEXT_NAME = "PickerContext"
def __init__(self, func, **kwargs):
"""
!@Brief
@type func: types.FunctionType / types.ClassType
@param func: Function launch if intersect on mesh found
"""
# Context datas
self.__undo_mode = kwargs.get("undoMode", "step")
self.__projection = kwargs.get("projection", "viewPlane")
self.__space = kwargs.get("space", "screen")
self.__cursor = kwargs.get("cursor", "crossHair")
self.__selected_nodes = list()
# CLS
if not isinstance(func, (types.FunctionType, types.ClassType, types.MethodType)):
raise BaseException("Invalid argument given !!!\n\tGive a function or class with matrix for argument...")
self.func = func
def build(self):
"""
@!Brief Build dragger context.
"""
if cmds.draggerContext(self.CONTEXT_NAME, exists=True):
cmds.setToolTo("selectSuperContext")
cmds.selectMode(object=True)
cmds.deleteUI(self.CONTEXT_NAME)
cmds.draggerContext(
self.CONTEXT_NAME,
undoMode=self.__undo_mode,
projection=self.__projection,
space=self.__space,
initialize=self.on_initialize,
pressCommand=self.on_press,
releaseCommand=self.on_release,
name=self.CONTEXT_NAME,
cursor=self.__cursor,
finalize=self.on_finalize,
)
def launch(self):
"""
!@Brief Launch picker context.
"""
self.build()
cmds.setToolTo(self.CONTEXT_NAME)
@staticmethod
def on_initialize(func=None):
"""
!@Brief This function is called when draggerContext is lanch.
"""
if func is None:
return
func()
def on_press(self):
"""
!@Brief On press action
"""
# Suspend refresh
cmds.refresh(suspend=True)
m3d_view = OpenMayaUI.M3dView().active3dView()
# Get mouse position
pos_x, pos_y, _ = cmds.draggerContext(self.CONTEXT_NAME, query=True, anchorPoint=True)
# Get camera position and ray direction from mouse position
pos = OpenMaya.MPoint()
ray = OpenMaya.MVector()
m3d_view.viewToWorld(int(pos_x), int(pos_y), pos, ray)
# Try to get object under the mouse. If not found nodes get all Mesh.
# This method don't really work. hitTest is not very accurate
# So test all mesh node on this scene. No latence with 1 000 mesh nodes
# panel_view = cmds.getPanel(underPointer=True)
# a_mesh_node = cmds.hitTest(panel_view, pos_x, pos_y)
# if not a_mesh_node:
# a_mesh_node = cmds.ls(type="mesh")
# Get current selection
a_current_objects = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(a_current_objects)
self.__selected_nodes = list()
for i in xrange(a_current_objects.length()):
dag_path = OpenMaya.MDagPath()
a_current_objects.getDagPath(i, dag_path)
self.__selected_nodes.append(dag_path.fullPathName())
# Get selection from picking
msu_x_pos = OpenMaya.MScriptUtil()
msu_x_pos.createFromDouble(pos_x)
ptr_x_pos = msu_x_pos.asShort()
msu_y_pos = OpenMaya.MScriptUtil()
msu_y_pos.createFromDouble(pos_y)
ptr_y_pos = msu_y_pos.asShort()
OpenMaya.MGlobal.selectFromScreen(
ptr_x_pos,
ptr_y_pos,
OpenMaya.MGlobal.kAddToHeadOfList,
OpenMaya.MGlobal.kSurfaceSelectMethod
)
a_objects = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(a_objects)
# Create node from intersection
if a_objects.length() == 0:
self.restore_view(a_nodes=self.__selected_nodes)
return False
dag_path = OpenMaya.MDagPath()
a_objects.getDagPath(0, dag_path)
if dag_path.node().hasFn(OpenMaya.MFn.kTransform):
a_shapes = get_all_shapes(dag_path)
dag_path = a_shapes[0]
if not dag_path.node().hasFn(OpenMaya.MFn.kMesh):
self.restore_view(a_nodes=self.__selected_nodes)
return False
# ToDo: Intersect with nurbsSurface and nurbsCurve
matrix = create_matrix_from_intersect(dag_path.fullPathName(), pos, ray)
if not matrix:
self.restore_view(a_nodes=self.__selected_nodes)
return False
# If matrix found duplicate, create or import node
self.func(matrix)
self.restore_view(a_nodes=self.__selected_nodes)
@staticmethod
def on_release(func=None):
"""
!@Brief This function is called when mouse is released
@type func: void
@param func: Function to launch when mouse is released.
"""
if func is None:
return
func()
def on_finalize(self, func=None):
"""
!@Brief This function is called when mouse event is finish. (after onPress or onRelease ...)
@type func: void
@param func: Function to launch when process is finished.
"""
self.restore_view(a_nodes=self.__selected_nodes)
if func is None:
return
func()
@staticmethod
def restore_view(a_nodes=None):
"""
!@Brief Force refresh of viewport and select saved selection.
@type a_nodes: list(string)
@param a_nodes: List of node to select.
"""
# Restore selection
if a_nodes:
cmds.select(a_nodes)
else:
cmds.select(clear=True)
# Restore refresh
cmds.refresh(suspend=False)
cmds.refresh(force=True)
def get_all_shapes(dag_path, shape_type=None):
"""
!@Brief Get all shape of transform.
@type dag_path: OpenMaya.MObject / OpenMaya.MDagPath
@param dag_path: DagPath of transform.
@type shape_type: OpenMaya.MFn
@param shape_type: Type of shape you want to get. DEfault value is None.
@rtype: list(OpenMaya.MDagPath)
@return: List of shape dagPath.
"""
if isinstance(dag_path, basestring):
dag_path = get_dag_path(dag_path)
elif isinstance(dag_path, OpenMaya.MObject):
dag_path = dagpath_from_mobject(dag_path)
shapes_nodes = list()
# Check count
child_count = dag_path.childCount()
if child_count == 0:
return shapes_nodes
# Get all shapes
for idx in xrange(dag_path.childCount()):
child_object = dag_path.child(idx)
if shape_type and isinstance(shape_type, int) and not child_object.hasFn(shape_type):
continue
if child_object.hasFn(OpenMaya.MFn.kTransform):
continue
dag_node = OpenMaya.MFnDagNode(child_object)
shapes_nodes.append(get_dag_path(dag_node.fullPathName()))
return shapes_nodes
def get_dag_path(node_name):
"""
!@Brief Get MDagPath from node name.
@type node_name: string
@param node_name: Node you want to transform in MDagPath
@rtype: MDagPath
@return: MDagPath of PyNode
"""
if not isinstance(node_name, basestring):
raise RuntimeError("Node name must be a string !!!")
# Get PyNode in MSelectionList
selection_list = OpenMaya.MSelectionList()
selection_list.add(node_name)
# Get MDagPath
dag_path = OpenMaya.MDagPath()
selection_list.getDagPath(0, dag_path)
return dag_path
def dagpath_from_mobject(api_object):
"""
!@Brief Get DagPth from MObject.
@type api_object: OpenMaya.MObject
@param api_object: Api object for get MDagPath.
@rtype: OpenMaya.MDagPath
@return: MDagPath from MObject
"""
if not isinstance(api_object, OpenMaya.MObject):
raise RuntimeError("Invalid object given !!!")
dag_path = OpenMaya.MDagPath()
dag_node = OpenMaya.MFnDagNode(api_object)
dag_node.getPath(dag_path)
return dag_path
def create_matrix_from_intersect(shape_name, start_point, ray_direction):
"""
!@Brief Create transform from intersection. Get point of intersect and normal, tangent, binormal from face vertices.
@type shape_name: string
@param shape_name: Name of shape for get intersect
@type start_point: OpenMaya.MFloatPoint
@param start_point: Origin of ray intersection
@type ray_direction: OpenMaya.MFloatVector
@param ray_direction: Ray direction for intersection
"""
# Mesh
dag_mesh = get_dag_path(shape_name)
mfn_mesh = OpenMaya.MFnMesh(dag_mesh)
# Get intersect
fp_pos = OpenMaya.MFloatPoint(start_point.x, start_point.y, start_point.z)
fvray = OpenMaya.MFloatVector(ray_direction.x, ray_direction.y, ray_direction.z)
intersect_datas = get_mesh_intersect(mfn_mesh, fp_pos, fvray)
if not intersect_datas:
return None
# Get matrix
intersect_position = intersect_datas[0]
hit_face = intersect_datas[1]
matrix = get_matrix_from_face(mfn_mesh, intersect_position, hit_face)
return matrix
def get_mesh_intersect(mfn_mesh, start_point, ray_direction):
"""
!@Brief Get mesh intersection
@type mfn_mesh: OpenMaya.MFnMesh
@param mfn_mesh: MFnMesh for get intersection
@type start_point: OpenMaya.MFloatPoint
@param start_point: Origin of ray intersection
@type ray_direction: OpenMaya.MFloatVector
@param ray_direction: Ray direction for intersection
@rtype: MFloatPoint, int
@return: Point and face id of intersection. None if no intersection found
"""
# Get intersection from all mesh shapes
hitpoint = OpenMaya.MFloatPoint()
face_ids = None
tril_ids = None
ids_sorted = False
max_param = 99999
both_direction = False
accel_param = None
hit_ray_param = None
hit_face_su = MScriptUtil()
hit_face_su.createFromInt(0)
hit_face = hit_face_su.asIntPtr()
hit_triangle = None
hit_bary_1 = None
hit_bary_2 = None
tolerence = 1e-6
intersection = mfn_mesh.closestIntersection(
start_point,
ray_direction,
face_ids,
tril_ids,
ids_sorted,
OpenMaya.MSpace.kWorld,
max_param,
both_direction,
accel_param,
hitpoint,
hit_ray_param,
hit_face,
hit_triangle,
hit_bary_1,
hit_bary_2,
tolerence
)
if not intersection:
return None
return hitpoint, hit_face_su.getInt(hit_face)
def get_matrix_from_face(mfn_mesh, point, face_id):
"""
!@Brief Get matrix from point position and face id.
Take normal, tangent, binormal of all face vertex and average it.
@type mfn_mesh: OpenMaya.MFnMesh
@param mfn_mesh: MFnMesh for get point orientation
@type point: MFloatPoint
@param point: Position on face.
@type face_id: int
@param face_id: Face id
@rtype: pymel.Core.datatypes.Matrix
@return: Matrix found
"""
# Get normal
normals = OpenMaya.MFloatVectorArray()
mfn_mesh.getFaceVertexNormals(face_id, normals)
normal = average_vector_array(normals)
# Get tangent
tangents = OpenMaya.MFloatVectorArray()
mfn_mesh.getFaceVertexTangents(face_id, tangents)
tangent = average_vector_array(tangents)
# Get binormal
binormal = tangent ^ normal
binormal.normalize()
# Force normal perpendicalary
normal = binormal ^ tangent
normal.normalize()
# Create matrix
from pymel import core as pmc
matrix = pmc.datatypes.Matrix(
[tangent.x, tangent.y, tangent.z, 0.0],
[normal.x, normal.y, normal.z, 0.0],
[binormal.x, binormal.y, binormal.z, 0.0],
[point.x, point.y, point.z, 1.0]
)
return matrix
def average_vector_array(vector_array):
"""
!@Brief Average MVector array
@type vector_array: OpenMaya.MVectorArray / OpenMaya.MFloatVectorArray
:param vector_array: Vector array to average
@rtype: OpenMaya.MVector
@return: Vector average
"""
if not isinstance(vector_array, (OpenMaya.MVectorArray, OpenMaya.MFloatVectorArray)):
raise BaseException("Invalid argument !!!\n\tArgument must be a MVectorArray.")
m_vector = OpenMaya.MVector()
for idx in xrange(vector_array.length()):
m_vector += OpenMaya.MVector(vector_array[idx])
m_vector /= vector_array.length()
m_vector.normalize()
return m_vector
picker_ctx = PickerContext(func_test)
picker_ctx.launch()