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()

Reply via email to