def __get_times_and_nodes(self): """Get times and nodes.""" times = [] map_uuid_to_node = dict() for uuid in self._data.keys(): node = maya.cmds.ls(uuid, long=True)[0] map_uuid_to_node[uuid] = dict() for attr_name in self._data[uuid].keys(): plug_name = node + '.' + attr_name plug = node_utils.get_as_plug_apitwo(plug_name) assert plug is not None map_uuid_to_node[uuid][attr_name] = plug data = self._data.get(uuid, dict()) data = data.get(attr_name, dict()) times += data times = list(set(times)) times = list(sorted(times)) return times, map_uuid_to_node
def test_get_double_from_plug_apitwo(self): start_frame = 1001 end_frame = 1101 node = maya.cmds.createNode('transform') attr_names = [ 'translateX', 'rotateY', 'scaleZ', ] for attr_name in attr_names: plug_name = '{}.{}'.format(node, attr_name) plug = node_utils.get_as_plug_apitwo(plug_name) times = [1.0, 2, -1.0, -1, 42, 9999, -9999] times += list(range(start_frame, end_frame)) for time in times: ctx = mod.create_dg_context_apitwo(time) value = mod.get_double_from_plug_apitwo(plug, ctx) assert isinstance(value, float) return
def test_get_matrix_from_plug_apitwo(self): start_frame = 1001 end_frame = 1101 node = maya.cmds.createNode('transform') attr_names = [ 'matrix', 'worldMatrix[0]', 'parentMatrix[0]', ] for attr_name in attr_names: plug_name = '{}.{}'.format(node, attr_name) plug = node_utils.get_as_plug_apitwo(plug_name) times = [1.0, 2, -1.0, -1, 42, 9999, -9999] times += list(range(start_frame, end_frame)) for time in times: ctx = mod.create_dg_context_apitwo(time) matrix = mod.get_matrix_from_plug_apitwo(plug, ctx) assert isinstance(matrix, OpenMaya2.MMatrix) return
def get_world_matrix_apitwo(node, ctx): """ Get the matrix value of attribute 'worldMatrix[0]' at a given DG Context. :param node: Node path to query values on. :type node: str :param ctx: The (time) context to query the attribute at. :type ctx: maya.api.OpenMaya.MDGContext :returns: The 4x4 matrix value at the given DG context. :rtype: maya.api.OpenMaya.MMatrix """ assert isinstance(node, (str, unicode)) assert isinstance(ctx, OpenMaya2.MDGContext) name = node + '.worldMatrix[0]' plug = node_utils.get_as_plug_apitwo(name) return get_matrix_from_plug_apitwo(plug, ctx)
def get_parent_inverse_matrix_apitwo(node, ctx): """ Get the matrix value of attribute 'parentInverseMatrix[0]' at a given DG Context. :param node: Node path to query values on. :type node: str :param ctx: The (time) context to query the attribute at. :type ctx: maya.api.OpenMaya.MDGContext :returns: The 4x4 matrix value at the given DG context. :rtype: maya.api.OpenMaya.MMatrix """ assert isinstance(node, pycompat.TEXT_TYPE) assert isinstance(ctx, OpenMaya2.MDGContext) name = node + '.parentInverseMatrix[0]' plug = node_utils.get_as_plug_apitwo(name) return get_matrix_from_plug_apitwo(plug, ctx)
def process(self): """ Evaluate all the node attributes at times. :rtype: None """ # Get times and nodes. times = [] map_uuid_to_node = dict() for uuid in self._data.keys(): node = maya.cmds.ls(uuid, long=True)[0] map_uuid_to_node[uuid] = dict() for attr_name in self._data[uuid].keys(): plug_name = node + '.' + attr_name plug = node_utils.get_as_plug_apitwo(plug_name) assert plug is not None map_uuid_to_node[uuid][attr_name] = plug data = self._data.get(uuid, dict()) data = data.get(attr_name, dict()) times += data times = list(set(times)) times = list(sorted(times)) # Query the matrices, looping over time sequentially. for t in times: ctx = create_dg_context_apitwo(t) for uuid in self._data.keys(): d = self._data.get(uuid, dict()) for attr_name in d.keys(): d2 = map_uuid_to_node.get(uuid, dict()) plug = d2.get(attr_name, dict()) if 'matrix' in attr_name.lower(): matrix = get_matrix_from_plug_apitwo(plug, ctx) self._data[uuid][attr_name][t] = matrix elif 'rotatepivot' in attr_name.lower(): value = get_double_from_plug_apitwo(plug, ctx) self._data[uuid][attr_name][t] = value else: msg = 'Attribute name is not supported; attr_name=%r' raise ValueError(msg % attr_name) return
def set_transform_values(tfm_matrix_cache, times, src_tfm_node, dst_tfm_node, rotate_order=None, delete_static_anim_curves=False, eval_mode=None): """ Set transform node values on a destination node at times, using previously evaluated cached values. src_tfm_node is used to look-up into the cache for values. dst_tfm_node is the node that will be set values on. It is possible to have src_tfm_node and dst_tfm_node reference the same Maya node, or even the same object. .. note:: The function assumes the given destination node has no locked attributes. :param tfm_matrix_cache: A cache holding queried matrix values. :type tfm_matrix_cache: TransformMatrixCache :param times: The times to set transforms values on. :type times: [int or float, ...] :param src_tfm_node: Source node to get cached transform values from. :type src_tfm_node: TransformNode :param dst_tfm_node: Destination node set transform values on. :type dst_tfm_node: TransformNode :param rotate_order: The rotation order to set on the dst_node, or use the existing rotation on the dst_node if rotate_order is None. :type rotate_order: str :param delete_static_anim_curves: If an animation curve is static, it will be deleted. :type delete_static_anim_curves: bool :param eval_mode: What type of evaluation method to use? :type eval_mode: mmSolver.utils.constant.EVAL_MODE_* :rtype: None """ assert isinstance(tfm_matrix_cache, TransformMatrixCache) assert isinstance(times, (list, tuple)) assert isinstance(src_tfm_node, TransformNode) assert isinstance(dst_tfm_node, TransformNode) assert rotate_order is None or isinstance(rotate_order, (str, unicode)) if eval_mode is None: eval_mode = const.EVAL_MODE_DEFAULT assert eval_mode in const.EVAL_MODE_LIST current_frame = maya.cmds.currentTime(query=True) space = OpenMaya2.MSpace.kWorld attrs = [ 'translateX', 'translateY', 'translateZ', 'rotateX', 'rotateY', 'rotateZ', 'scaleX', 'scaleY', 'scaleZ' ] dst_node = dst_tfm_node.get_node() if rotate_order is None: rotate_order = maya.cmds.xform(dst_node, query=True, rotateOrder=True) else: maya.cmds.xform(dst_node, edit=True, rotateOrder=rotate_order) assert rotate_order in const.ROTATE_ORDER_STR_LIST rotate_order_api = ROTATE_ORDER_STR_TO_APITWO_CONSTANT[rotate_order] # Get destination node plug. dst_node = dst_tfm_node.get_node() dst_name = dst_node + '.parentInverseMatrix[0]' parent_inv_matrix_plug = node_utils.get_as_plug_apitwo(dst_name) # Query the matrix of nodes. world_mat_list = get_transform_matrix_list(tfm_matrix_cache, times, src_tfm_node, rotate_order=rotate_order) assert len(world_mat_list) == len(times) # Set transform dst_node = dst_tfm_node.get_node() prv_rot = None for t, world_mat in zip(times, world_mat_list): assert t is not None assert world_mat is not None parent_inv_mat = None if eval_mode == const.EVAL_MODE_API_DG_CONTEXT: ctx = create_dg_context_apitwo(t) parent_inv_mat = get_matrix_from_plug_apitwo( parent_inv_matrix_plug, ctx) elif eval_mode == const.EVAL_MODE_TIME_SWITCH_GET_ATTR: maya.cmds.currentTime(t, update=True) plug_name = parent_inv_matrix_plug.name() parent_inv_mat = maya.cmds.getAttr(plug_name) parent_inv_mat = OpenMaya2.MMatrix(parent_inv_mat) else: msg = 'eval_mode does not have a valid value' raise ValueError(msg % eval_mode) local_mat = world_mat.asMatrix() * parent_inv_mat local_mat = OpenMaya2.MTransformationMatrix(local_mat) local_mat.reorderRotation(rotate_order_api) # Decompose matrix into components trans = local_mat.translation(space) scl = local_mat.scale(space) rot = local_mat.rotation(asQuaternion=False) # Convert and clean rotation values trans = (trans[0], trans[1], trans[2]) scl = (scl[0], scl[1], scl[2]) rot = (math.degrees(rot[0]), math.degrees(rot[1]), math.degrees(rot[2])) if prv_rot is not None: rot = (animcurve_utils.euler_filter_value(prv_rot[0], rot[0]), animcurve_utils.euler_filter_value(prv_rot[1], rot[1]), animcurve_utils.euler_filter_value(prv_rot[2], rot[2])) prv_rot = rot # Set Keyframes values = trans + rot + scl assert len(attrs) == len(values) for attr, v in zip(attrs, values): maya.cmds.setKeyframe(dst_node, attribute=attr, time=t, value=v) if delete_static_anim_curves is True: maya.cmds.delete(dst_node, staticChannels=True) if eval_mode == const.EVAL_MODE_TIME_SWITCH_GET_ATTR: maya.cmds.currentTime(current_frame, update=True) return
def get_transform_matrix_list(tfm_matrix_cache, times, src_tfm_node, rotate_order=None): """ Get the transform values, as raw MTransformationMatrix. This function does not modify the original node in any way. src_tfm_node is used to look-up into the cache for values. :param tfm_matrix_cache: A cache holding queried matrix values. :type tfm_matrix_cache: TransformMatrixCache :param times: The times to set transforms values on. :type times: [int or float, ...] :param src_tfm_node: Source node to get cached transform values from. :type src_tfm_node: TransformNode :param rotate_order: The rotation order to set on the dst_node, or use the existing rotation on the dst_node if rotate_order is None. :type rotate_order: str :rtype: [maya.api.OpenMaya.MTransformationMatrix, ..] """ assert isinstance(tfm_matrix_cache, TransformMatrixCache) assert isinstance(times, (list, tuple)) assert isinstance(src_tfm_node, TransformNode) assert rotate_order is None or isinstance(rotate_order, (str, unicode)) space = OpenMaya2.MSpace.kWorld attrs = [ 'translateX', 'translateY', 'translateZ', 'rotateX', 'rotateY', 'rotateZ', 'scaleX', 'scaleY', 'scaleZ' ] if rotate_order is None: rotate_order = 'xyz' assert rotate_order in const.ROTATE_ORDER_STR_LIST rotate_order = ROTATE_ORDER_STR_TO_APITWO_CONSTANT[rotate_order] # Query the matrix node. src_node_uuid = src_tfm_node.get_node_uuid() src_node_attrs = tfm_matrix_cache.get_attrs_for_node(src_node_uuid) with_pivot = len(src_node_attrs) > 1 world_mat_list = [] if with_pivot is False: world_mat_list = tfm_matrix_cache.get_node_attr( src_node_uuid, 'worldMatrix[0]', times, ) else: mat_list = tfm_matrix_cache.get_node_attr( src_node_uuid, 'matrix', times, ) assert len(mat_list) == len(times) par_inv_mat_list = tfm_matrix_cache.get_node_attr( src_node_uuid, 'parentInverseMatrix[0]', times, ) assert len(par_inv_mat_list) == len(times) rot_piv_x_list = tfm_matrix_cache.get_node_attr( src_node_uuid, 'rotatePivotX', times, ) assert len(rot_piv_x_list) == len(times) rot_piv_y_list = tfm_matrix_cache.get_node_attr( src_node_uuid, 'rotatePivotY', times, ) assert len(rot_piv_y_list) == len(times) rot_piv_z_list = tfm_matrix_cache.get_node_attr( src_node_uuid, 'rotatePivotZ', times, ) assert len(rot_piv_z_list) == len(times) # Reconstruct World-Matrix, accounting for pivot point. space = OpenMaya2.MSpace.kWorld loop_iter = zip(mat_list, par_inv_mat_list, rot_piv_x_list, rot_piv_y_list, rot_piv_z_list) for mat, par_inv_mat, rot_piv_x, rot_piv_y, rot_piv_z in loop_iter: assert mat is not None assert par_inv_mat is not None assert rot_piv_x is not None assert rot_piv_y is not None assert rot_piv_z is not None mat = OpenMaya2.MTransformationMatrix(mat) pivot = OpenMaya2.MVector(rot_piv_x, rot_piv_y, rot_piv_z) trans = mat.translation(space) mat.setTranslation(trans + pivot, space) world_mat = par_inv_mat * mat.asMatrix() world_mat_list.append(world_mat) assert len(world_mat_list) == len(times) # Get destination node plug. src_node = src_tfm_node.get_node() src_name = src_node + '.parentInverseMatrix[0]' parent_inv_matrix_plug = node_utils.get_as_plug_apitwo(src_name) # Get transform matrix_list = [] for t, world_mat in zip(times, world_mat_list): assert world_mat is not None par_inv_mat = None if True: maya.cmds.currentTime(t, update=True) plug_name = parent_inv_matrix_plug.name() par_inv_mat = maya.cmds.getAttr(plug_name) par_inv_mat = OpenMaya2.MMatrix(par_inv_mat) else: ctx = create_dg_context_apitwo(t) par_inv_mat = get_matrix_from_plug_apitwo(parent_inv_matrix_plug, ctx) local_mat = OpenMaya2.MTransformationMatrix(world_mat) local_mat.reorderRotation(rotate_order) matrix_list.append(local_mat) return matrix_list
def set_transform_values(tfm_matrix_cache, times, src_tfm_node, dst_tfm_node, rotate_order=None, delete_static_anim_curves=True): """ Set transform node values on a destination node at times, using previously evaluated cached values. src_tfm_node is used to look-up into the cache for values. dst_tfm_node is the node that will be set values on. It is possible to have src_tfm_node and dst_tfm_node reference the same Maya node, or even the same object. .. note:: The function assumes the given destination node has no locked attributes. :param tfm_matrix_cache: A cache holding queried matrix values. :type tfm_matrix_cache: TransformMatrixCache :param times: The times to set transforms values on. :type times: [int or float, ...] :param src_tfm_node: Source node to get cached transform values from. :type src_tfm_node: TransformNode :param dst_tfm_node: Destination node set transform values on. :type dst_tfm_node: TransformNode :param rotate_order: The rotation order to set on the dst_node, or use the existing rotation on the dst_node if rotate_order is None. :type rotate_order: str :param delete_static_anim_curves: If an animation curve is static, it will be deleted. :type delete_static_anim_curves: bool :rtype: None """ assert isinstance(tfm_matrix_cache, TransformMatrixCache) assert isinstance(times, (list, tuple)) assert isinstance(src_tfm_node, TransformNode) assert isinstance(dst_tfm_node, TransformNode) assert rotate_order is None or isinstance(rotate_order, (str, unicode)) space = OpenMaya2.MSpace.kWorld attrs = [ 'translateX', 'translateY', 'translateZ', 'rotateX', 'rotateY', 'rotateZ', 'scaleX', 'scaleY', 'scaleZ' ] dst_node = dst_tfm_node.get_node() if rotate_order is None: rotate_order = maya.cmds.xform( dst_node, query=True, rotateOrder=True) else: maya.cmds.xform( dst_node, edit=True, rotateOrder=rotate_order) assert rotate_order in const.ROTATE_ORDER_STR_LIST rotate_order = ROTATE_ORDER_STR_TO_APITWO_CONSTANT[rotate_order] # Get destination node plug. src_node = src_tfm_node.get_node() src_name = src_node + '.parentInverseMatrix[0]' parent_inv_matrix_plug = node_utils.get_as_plug_apitwo(src_name) # Query the matrix node. src_node_uuid = src_tfm_node.get_node_uuid() src_node_attrs = tfm_matrix_cache.get_attrs_for_node(src_node_uuid) with_pivot = len(src_node_attrs) > 1 world_mat_list = [] if with_pivot is False: world_mat_list = tfm_matrix_cache.get_node_attr_matrix( src_node_uuid, 'worldMatrix[0]', times, ) else: mat_list = tfm_matrix_cache.get_node_attr_matrix( src_node_uuid, 'matrix', times, ) assert len(mat_list) == len(times) par_inv_mat_list = tfm_matrix_cache.get_node_attr_matrix( src_node_uuid, 'parentInverseMatrix[0]', times, ) assert len(par_inv_mat_list) == len(times) rot_piv_x_list = tfm_matrix_cache.get_node_attr_matrix( src_node_uuid, 'rotatePivotX', times, ) assert len(rot_piv_x_list) == len(times) rot_piv_y_list = tfm_matrix_cache.get_node_attr_matrix( src_node_uuid, 'rotatePivotY', times, ) assert len(rot_piv_y_list) == len(times) rot_piv_z_list = tfm_matrix_cache.get_node_attr_matrix( src_node_uuid, 'rotatePivotZ', times, ) assert len(rot_piv_z_list) == len(times) # Reconstruct World-Matrix, accounting for pivot point. space = OpenMaya2.MSpace.kWorld loop_iter = zip(mat_list, par_inv_mat_list, rot_piv_x_list, rot_piv_y_list, rot_piv_z_list) for mat, par_inv_mat, rot_piv_x, rot_piv_y, rot_piv_z in loop_iter: assert mat is not None assert par_inv_mat is not None assert rot_piv_x is not None assert rot_piv_y is not None assert rot_piv_z is not None mat = OpenMaya2.MTransformationMatrix(mat) pivot = OpenMaya2.MVector(rot_piv_x, rot_piv_y, rot_piv_z) trans = mat.translation(space) mat.setTranslation(trans + pivot, space) mat = mat.asMatrix() world_mat = par_inv_mat * mat world_mat_list.append(world_mat) assert len(world_mat_list) == len(times) # Set transform dst_node = dst_tfm_node.get_node() prv_rot = None for t, world_mat in zip(times, world_mat_list): assert world_mat is not None ctx = create_dg_context_apitwo(t) parent_mat = get_matrix_from_plug_apitwo( parent_inv_matrix_plug, ctx ) local_mat = parent_mat * world_mat local_mat = OpenMaya2.MTransformationMatrix(local_mat) local_mat.reorderRotation(rotate_order) # Decompose matrix into components trans = local_mat.translation(space) scl = local_mat.scale(space) rot = local_mat.rotation(asQuaternion=False) # Convert and clean rotation values trans = (trans[0], trans[1], trans[2]) scl = (scl[0], scl[1], scl[2]) rot = ( math.degrees(rot[0]), math.degrees(rot[1]), math.degrees(rot[2]) ) if prv_rot is not None: rot = ( animcurve_utils.euler_filter_value(prv_rot[0], rot[0]), animcurve_utils.euler_filter_value(prv_rot[1], rot[1]), animcurve_utils.euler_filter_value(prv_rot[2], rot[2]) ) prv_rot = rot # Set Keyframes values = trans + rot + scl assert len(attrs) == len(values) for attr, v in zip(attrs, values): maya.cmds.setKeyframe(dst_node, attribute=attr, time=t, value=v) if delete_static_anim_curves is True: maya.cmds.delete(dst_node, staticChannels=True) return