def create_auto(self, name=None, *args, **kwargs): """ Creates an auto buffer group above the control :return: str """ suffix = kwargs.pop('suffix', 'auto') name = name or 'auto' if self.get_auto(suffix=suffix): return self.get_auto(suffix=suffix) new_group = self._create_group(suffix, name=name, *args, **kwargs) node_parent = dcc.node_parent(self.get()) if node_parent: new_group = dcc.set_parent(new_group, node_parent) root_group = self.get_root(suffix=kwargs.pop('auto_suffix', None)) if root_group: if not dcc.node_short_name(dcc.node_parent( new_group)) == dcc.node_short_name(root_group): dcc.set_parent(new_group, root_group) for xform in 'trs': for axis in 'xyz': attr_value = 1.0 if xform == 's' else 0.0 dcc.set_attribute_value(new_group, '{}{}'.format(xform, axis), attr_value) self.set_parent(new_group, parent_top=False) return new_group
def create_root(self, name=None, *args, **kwargs): """ Creates a root buffer group above the control :return: str """ suffix = kwargs.pop('suffix', 'root') name = name or 'root' if self.get_root(suffix=suffix): return self.get_root(suffix=suffix) top_parent = dcc.node_parent(self.get_top()) new_group = self._create_group(suffix, name=name, *args, **kwargs) # MIRROR BEHAVIOUR # TODO: This should be optional mirror_group = None if self.side and dcc.name_is_right(self.side): mirror_group = self._create_group('mirror', name='mirror', *args, **kwargs) # dcc.match_translation_rotation(self.get(), mirror_group) dcc.set_attribute_value(mirror_group, 'scaleX', -1) if top_parent: dcc.set_parent(mirror_group, top_parent) auto_group = self.get_auto() if not auto_group: if mirror_group: dcc.set_parent(new_group, mirror_group) self.set_parent(new_group, parent_top=False) for xform in 'trs': for axis in 'xyz': attr_value = 1.0 if xform == 's' else 0.0 dcc.set_attribute_value(new_group, '{}{}'.format(xform, axis), attr_value) else: node_parent = dcc.node_parent(self.get()) if node_parent: dcc.set_parent(new_group, node_parent) self.set_parent(new_group, parent_top=False) else: if mirror_group: dcc.set_parent(new_group, mirror_group) else: auto_group_parent = dcc.node_parent(auto_group) if auto_group_parent: new_group = dcc.set_parent(new_group, auto_group_parent) dcc.set_parent(auto_group, new_group) return new_group
def get_selected_info(self, data, reply): """ Function that returns selected geometry info (its name and its vertices) :return: tuple(str, list(list(float, float, float)) """ nodes = dcc.selected_nodes(flatten=True) selected_geo = dcc.filter_nodes_by_selected_components(filter_type=12, nodes=nodes, full_path=True) selected_vertices = None if selected_geo: selected_geo = selected_geo[0] if not selected_geo: hilited_geo = dcc.selected_hilited_nodes(full_path=True) if len(hilited_geo) == 1: selected_geo = hilited_geo[0] selected_vertices = dcc.filter_nodes_by_selected_components( filter_type=31, nodes=nodes, full_path=False) elif len(hilited_geo) > 1: logger.warning( 'Only one object can be hilited in component mode!') if selected_geo: if dcc.node_is_a_shape(selected_geo): selected_geo = dcc.node_parent(selected_geo, full_path=True) if not selected_geo or not dcc.node_exists(selected_geo): reply['success'] = False reply['result'] = '', list() return reply['success'] = True reply['result'] = selected_geo, selected_vertices
def duplicate_control(control_name, duplicate_name=None, use_selected_name=True, copy_tracker=True, delete_node_shapes=False): """ Duplicates the given control transform node to a new transform parented to the world :param control_name: str, name of the control to duplicate :param duplicate_name: str, name of the duplicated control :param use_selected_name: bool, Whether or not :param copy_tracker: :param delete_node_shapes: :return: """ if not duplicate_name and use_selected_name: duplicate_name = control_name duplicated_control = xform_utils.duplicate_transform_without_children( control_name, node_name=duplicate_name, delete_shapes=delete_node_shapes) if dcc.node_parent(duplicated_control): duplicated_control = dcc.set_parent_to_world(duplicated_control) if dcc.node_type(duplicated_control) == 'joint': pass return duplicated_control
def _create_twist_joints(self): """ Internal function that creates twist joints """ distance = dcc.distance_between_nodes(self._twist_driver, self._twist_driven) distance_ratio = distance / (self._twist_joints_count - 1) twist_axis = self._get_twist_axis() root_node = dcc.node_parent(self._twist_driver, full_path=False) if root_node == dcc.node_short_name(self._twist_driven): twist_axis = (mathlib.Vector(*twist_axis) * -1.0).list() for i in range(self._twist_joints_count): dcc.clear_selection() twist_joint = dcc.create_joint(self._get_name('roll', id=i, node_type='joint'), size=self._scale) dcc.match_rotation(self._twist_joint or self._twist_driver, twist_joint) dcc.match_translation(self._twist_driver, twist_joint) joint_utils.OrientJointAttributes.zero_orient_joint(twist_joint) if self._reverse_orient: twist_joint = dcc.set_parent(twist_joint, self._twist_driven) else: twist_joint = dcc.set_parent(twist_joint, self._twist_driver) new_distance = mathlib.Vector(*twist_axis) * (distance_ratio * i) dcc.translate_node_in_object_space(twist_joint, new_distance.list(), relative=True) self._twist_joints.append(twist_joint)
def _get_curve_transform(self, curve): parent = curve if dcc.node_exists(curve): if dcc.node_type(curve) == 'nurbsCurve': parent = dcc.node_parent(curve) else: parent = curve return parent
def _setup_bottom_control(self): """ Internal function that setup bottom control after its creation :return: """ joints = self._ik_chain ik_handle_parent = dcc.node_parent(self._ik_handle) dcc.set_parent(ik_handle_parent, self._bottom_control.get()) if self._orient_constraint: dcc.create_orient_constraint(joints[self._end_index], self._bottom_control.get(), maintain_offset=True)
def mirror_mesh(mesh=None): """ Mirror given meshes """ out_dict = {'success': False, 'result': list()} meshes = mesh or dcc.selected_nodes_of_type(node_type='transform') meshes = python.force_list(meshes) if not meshes: out_dict['msg'] = 'No meshes to mirror selected.' return out_dict for mesh in meshes: try: parent_node = dcc.node_parent(mesh) mirror_geo_name = xform_utils.find_transform_right_side( mesh, check_if_exists=False) mirror_geo = maya.cmds.duplicate(mesh, n=mirror_geo_name or None) root = maya.cmds.group(empty=True, world=True) maya.cmds.parent(mirror_geo, root) maya.cmds.setAttr('{}.rx'.format(root), 180) for axis in 'xyz': maya.cmds.setAttr('{}.s{}'.format(root, axis), -1) maya.cmds.parent(mirror_geo, world=True) maya.cmds.delete(root) maya.cmds.makeIdentity(mirror_geo, apply=True, t=False, r=True, s=True, n=False, pn=True) if parent_node: dcc.set_parent(mirror_geo, parent_node) out_dict['result'].append(mirror_geo) except Exception as exc: out_dict[ 'msg'] = 'Was not possible to mirror meshes "{}" : {}'.format( meshes, exc) return out_dict out_dict['success'] = True return out_dict
def manual_orient_joints(self, data, reply): orient_type = data.get('orient_type', 'add') x_axis = data.get('x_axis', 0.0) y_axis = data.get('y_axis', 0.0) z_axis = data.get('z_axis', 0.0) affect_children = data.get('affect_children', False) if orient_type == 'add': tweak = 1.0 else: tweak = -1.0 tweak_rot = [x_axis * tweak, y_axis * tweak, z_axis * tweak] joints = dcc.selected_nodes_of_type(node_type='joint') if not joints: return for jnt in joints: dcc.set_node_rotation_axis_in_object_space(jnt, tweak_rot[0], tweak_rot[1], tweak_rot[2]) dcc.zero_scale_joint(jnt) dcc.freeze_transforms(jnt, preserve_pivot_transforms=True) if affect_children: childs = dcc.list_children( jnt, children_type=['transform', 'joint'], full_path=False, all_hierarchy=True) or list() for child in childs: parent = dcc.node_parent(child) dcc.set_parent_to_world(child) dcc.set_node_rotation_axis_in_object_space( child, tweak_rot[0], tweak_rot[1], tweak_rot[2]) dcc.zero_scale_joint(child) dcc.freeze_transforms(child, preserve_pivot_transforms=True) dcc.set_parent(child, parent) dcc.select_node(joints, replace_selection=True) reply['success'] = True
def reset_joints_orient_to_world(self, data, reply): apply_to_hierarchy = data.get('apply_to_hierarchy', False) if apply_to_hierarchy: dcc.select_hierarchy() joints = dcc.selected_nodes_of_type(node_type='joint', full_path=False) if not joints: reply['msg'] = 'No joints selected' reply['success'] = False return for jnt in reversed(joints): childs = dcc.list_children(jnt, all_hierarchy=False, children_type=['transform', 'joint']) # If the joints has direct childs, unparent that childs and store names if childs: if len(childs) > 0: childs = dcc.set_parent_to_world(childs) # Get parent of this joints for later use parent = dcc.node_parent(jnt, full_path=False) or '' if parent: dcc.set_parent_to_world(jnt) # Clear joint axis dcc.zero_scale_joint(jnt) dcc.freeze_transforms(jnt, preserve_pivot_transforms=True) dcc.zero_orient_joint(jnt) # Reparent if parent: dcc.set_parent(jnt, parent) # Reparent child if childs: if len(childs) > 0: dcc.set_parent(childs, jnt) dcc.select_node(joints, replace_selection=True) reply['success'] = True
def _create_ik_handle(self): root_node = dcc.node_parent(self._twist_driver, full_path=False) if root_node == dcc.node_short_name(self._twist_driven): root_node = self._twist_driver ik_handle = ik_utils.IkHandle( self._get_name('roll', node_type='ikHandle')) ik_handle.set_solver(ik_handle.SOLVER_SC) ik_handle.set_start_joint(self._twist_joints[0]) ik_handle.set_end_joint(self._twist_joints[-1]) ik_handle = ik_handle.create() if root_node: ik_handle = dcc.set_parent(ik_handle, root_node) # we make sure that twist joint follows the driven joint dcc.create_point_constraint(ik_handle, self._twist_driven) self._ik_handle = ik_handle
def combine_meshes(meshes=None, new_mesh_name=None): """ Combines given meshes into one transform """ out_dict = {'success': False, 'result': None} meshes = meshes or dcc.selected_nodes_of_type(node_type='transform') meshes = python.force_list(meshes) if not meshes: out_dict['msg'] = 'No meshes to combine selected.' return out_dict if len(meshes) < 2: out_dict['msg'] = 'You need to select at least two meshes to combine.' return out_dict new_mesh_name = new_mesh_name or dcc.node_short_name(meshes[0]) parent_node = None node_parents = list(set([dcc.node_parent(mesh) for mesh in meshes])) if all(parent == node_parents[0] for parent in node_parents): parent_node = node_parents[0] try: combined_mesh = dcc.combine_meshes(construction_history=False) if not combined_mesh: out_dict[ 'msg'] = 'Combine operation was done but not combined mesh was generated' return out_dict combined_mesh = dcc.rename_node(combined_mesh, new_mesh_name) if parent_node: dcc.set_parent(combined_mesh, parent_node) out_dict['result'] = combined_mesh except Exception as exc: out_dict[ 'msg'] = 'Was not possible to combine meshes "{}" : {}'.format( meshes, exc) return out_dict out_dict['success'] = True return out_dict
def create_control_curve(control_name='new_ctrl', control_type='circle', controls_path=None, control_size=1.0, translate_offset=(0.0, 0.0, 0.0), rotate_offset=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0), axis_order='XYZ', mirror=None, color=None, line_width=-1, create_buffers=False, buffers_depth=0, match_translate=False, match_rotate=False, match_scale=False, parent=None, **kwargs): """ Creates a new curve based control :param control_name: str, name of the new control to create :param control_type: str, curve types used by the new control :param controls_path: str or None, path were control curve types can be located :param control_size: float, global size of the control :param translate_offset: tuple(float, float, float), XYZ translation offset to apply to the control curves :param rotate_offset: tuple(float, float, float), XYZ rotation offset to apply to the control curves :param scale: tuple(float, float, float), scale of the control. :param axis_order: str, axis order of the control. Default is XYZ. :param mirror: str or None, axis mirror to apply to the control curves (None, 'X', 'Y' or 'Z') :param color: list(float, float, float), RGB or index color to apply to the control :param line_width: str, If given, the new shapes will be parented to given node :param create_buffers: bool, Whether or not control buffer groups should be created. :param buffers_depth: int, Number of buffers groups to create. :param parent: str, If given, the new shapes will be parented to given node :param match_translate: bool, Whether or not new control root node should match the translate with the translation of the current DCC selected node :param match_translate: bool, Whether or not new control root node should match the rotate with the rotation of the current DCC selected node :param match_scale: bool, Whether or not new control root node should match the scale with the scale of the current DCC selected node :return: """ current_selection = dcc.selected_nodes() parent_mobj = None if parent: parent_mobj = api_node.as_mobject(parent) runner = command.CommandRunner() control_data = kwargs.pop('control_data', None) if control_data: parent_mobj, shape_mobjs = runner.run( 'tpDcc-libs-curves-dccs-maya-createCurveFromData', curve_data=control_data, curve_size=control_size, translate_offset=translate_offset, scale=scale, axis_order=axis_order, mirror=mirror, parent=parent_mobj) else: parent_mobj, shape_mobjs = runner.run( 'tpDcc-libs-curves-dccs-maya-createCurveFromPath', curve_type=control_type, curves_path=controls_path, curve_size=control_size, translate_offset=translate_offset, scale=scale, axis_order=axis_order, mirror=mirror, parent=parent_mobj) if parent_mobj: for shape in shape_mobjs: api_node.rename_mobject(shape, control_name) curve_long_name_list = api_node.names_from_mobject_handles(shape_mobjs) else: api_node.rename_mobject(shape_mobjs[0], control_name) curve_long_name = api_node.names_from_mobject_handles(shape_mobjs)[0] curve_long_name_list = [curve_long_name] if rotate_offset != (0.0, 0.0, 0.0): shape_utils.rotate_node_shape_cvs(curve_long_name_list, rotate_offset) if line_width != -1: curve.set_curve_line_thickness(curve_long_name_list, line_width=line_width) # TODO: Support index based color if color is not None: if isinstance(color, int): node_utils.set_color(curve_long_name_list, color) else: node_utils.set_rgb_color(curve_long_name_list, color, linear=True, color_shapes=True) transforms = list() for curve_shape in curve_long_name_list: parent = dcc.node_parent(curve_shape) if parent: if parent not in transforms: transforms.append(parent) else: if curve_shape not in transforms: transforms.append(curve_shape) if not parent and transforms: if create_buffers and buffers_depth > 0: transforms = create_buffer_groups(transforms, buffers_depth) if current_selection: match_transform = current_selection[0] if match_transform and dcc.node_exists(match_transform): for transform in transforms: if match_translate: dcc.match_translation(match_transform, transform) if match_rotate: dcc.match_rotation(match_transform, transform) if match_scale: dcc.match_scale(match_transform, transform) return transforms
def replace_control_curves(control_name, control_type='circle', controls_path=None, auto_scale=True, maintain_line_width=True, keep_color=True, **kwargs): """ Replaces the given control with the given control type deleting the existing control curve shape nodes :param control_name: :param control_type: :param controls_path: :param auto_scale: bool :param maintain_line_width: bool :param keep_color: bool :return: """ orig_sel = dcc.selected_nodes() line_width = -1 orig_size = None orig_color = kwargs.pop('color', None) if auto_scale: orig_size = get_control_size(control_name) if maintain_line_width: line_width = curve.get_curve_line_thickness(control_name) if keep_color: orig_color = get_control_color(control_name) new_control = create_control_curve(control_name='new_ctrl', control_type=control_type, controls_path=controls_path, color=orig_color, **kwargs)[0] if auto_scale and orig_size is not None: new_scale = get_control_size(new_control) scale_factor = orig_size / new_scale dcc.scale_shapes(new_control, scale_factor, relative=False) # We need to make sure that transforms are reset in all scenarios # Previous implementation was failing if the target hierarchy had a mirror behaviour (group scaled negative in # one of the axises) # maya.cmds.matchTransform([new_control, target], pos=True, rot=True, scl=True, piv=True) target = dcc.list_nodes(control_name, node_type='transform')[0] dcc.delete_node( dcc.create_parent_constraint(new_control, target, maintain_offset=False)) dcc.delete_node( dcc.create_scale_constraint(new_control, target, maintain_offset=False)) target_parent = dcc.node_parent(target) if target_parent: new_control = dcc.set_parent(new_control, target_parent) for axis in 'XYZ': dcc.set_attribute_value(new_control, 'translate{}'.format(axis), 0.0) dcc.set_attribute_value(new_control, 'rotate{}'.format(axis), 0.0) dcc.set_attribute_value(new_control, 'scale{}'.format(axis), 1.0) new_control = dcc.set_parent_to_world(new_control) if target != new_control: xform_utils.parent_transforms_shapes(target, [new_control], delete_original=True, delete_shape_type='nurbsCurve') if maintain_line_width: curve.set_curve_line_thickness(target, line_width=line_width) dcc.select_node(orig_sel) return target
def _connect_twist_joints(self): """ Internal function that connects twist setup """ # orient locator will be used to retrieve a "clean" orientation value in the twist axis no matter how we # rotate the end joint of the twist chain. self._orient_locator = dcc.create_locator( name=self._get_name('orient', node_type='locator')) self._fixed_locator = dcc.create_locator( name=self._get_name('fixed', node_type='locator')) dcc.match_translation_rotation(self._twist_driver, self._orient_locator) dcc.match_translation_rotation(self._twist_driver, self._fixed_locator) self._fixed_locator = dcc.set_parent( self._fixed_locator, dcc.node_parent(self._twist_driver)) self._orient_locator = dcc.set_parent(self._orient_locator, self._fixed_locator) for axis in 'XYZ': dcc.set_attribute_value(self._orient_locator, 'localScale{}'.format(axis), self._scale) dcc.set_attribute_value(self._fixed_locator, 'localScale{}'.format(axis), self._scale) # as the orient constraint is parented to the local constraint and both locators have the same transforms # the orient locator channels are completely clean. We use orient locator to retrieve a clean rotation of # the twist driver joint orient_constraint = dcc.create_orient_constraint( self._orient_locator, self._twist_driver) # we set the constraint interpolation type to average to avoid joint flipping dcc.set_attribute_value(orient_constraint, 'interpType', 1) axis_letter = self._get_twist_axis(as_letter=True) if len(axis_letter) > 1: axis_letter = axis_letter[-1] twist_value = -1 loop_joints = self.twist_joints if self._reverse_orient: loop_joints = list(reversed(loop_joints)) for i, twist_joint in enumerate(loop_joints): multiply_divide_twist = dcc.create_node( 'multiplyDivide', node_name=self._get_name('twistJoint', id=i, node_type='multiplyDivide')) for axis in 'XYZ': input_attr = 'input2{}'.format(axis) if axis == axis_letter.upper(): if self._reverse_orient: dcc.set_attribute_value( multiply_divide_twist, input_attr, 1.0 / len(self.twist_joints) * i) else: dcc.set_attribute_value(multiply_divide_twist, input_attr, twist_value) else: dcc.set_attribute_value(multiply_divide_twist, input_attr, 0.0) dcc.connect_attribute(self._orient_locator, 'rotate', multiply_divide_twist, 'input1') dcc.connect_attribute(multiply_divide_twist, 'output', twist_joint, 'rotate') twist_value += 1.0 / (self._twist_joints_count - 1)
def orient_joints(self, data, reply): aim_axis_index = data.get('aim_axis_index', 0.0) aim_axis_reverse = data.get('aim_axis_reverse', False) up_axis_index = data.get('up_axis_index', 0.0) up_axis_reverse = data.get('up_axis_reverse', False) up_world_axis_x = data.get('up_world_axis_x', 0.0) up_world_axis_y = data.get('up_world_axis_y', 0.0) up_world_axis_z = data.get('up_world_axis_z', 0.0) apply_to_hierarchy = data.get('apply_to_hierarchy', False) reset_joints = list() # Get up and aim axis aim_axis = [0, 0, 0] up_axis = [0, 0, 0] world_up_axis = [up_world_axis_x, up_world_axis_y, up_world_axis_z] if aim_axis_index == up_axis_index: LOGGER.warning( 'aim and up axis are the same, maybe orientation wont work correctly!' ) aim_axis_reverse_value = 1.0 if not aim_axis_reverse else -1.0 up_axis_reverse_value = 1.0 if not up_axis_reverse else -1.0 aim_axis[aim_axis_index] = aim_axis_reverse_value up_axis[up_axis_index] = up_axis_reverse_value # Get selected joints if apply_to_hierarchy: dcc.select_hierarchy() joints = dcc.selected_nodes_of_type(node_type='joint', full_path=False) if not joints: reply['msg'] = 'No joints selected' reply['success'] = False return for jnt in reversed(joints): childs = dcc.list_children(jnt, all_hierarchy=False, children_type=['transform', 'joint']) # If the joints has direct childs, unparent that childs and store names if childs: if len(childs) > 0: childs = dcc.set_parent_to_world(childs) childs = python.force_list(childs) # Get parent of this joints for later use parent = '' parents = dcc.node_parent(jnt) if parents: parent = parents[0] # Aim to the child aim_target = '' if childs: for child in childs: if dcc.node_type(child) == 'joint': aim_target = child break if aim_target != '': # Apply an aim constraint from the joint to its child (target) dcc.delete_node( dcc.create_aim_constraint(jnt, aim_target, aim_axis=aim_axis, up_axis=up_axis, world_up_axis=world_up_axis, world_up_type='vector', weight=1.0)) # Clear joint axis dcc.zero_scale_joint(jnt) dcc.freeze_transforms(jnt, preserve_pivot_transforms=True) elif parent != '': reset_joints.append(jnt) # Reparent child if childs: if len(childs) > 0: dcc.set_parent(childs, jnt) for jnt in reset_joints: # If there is no target, the joint will take its parent orientation for axis in ['x', 'y', 'z']: dcc.set_attribute_value( jnt, 'jointOrient{}'.format(axis.upper()), dcc.get_attribute_value(jnt, 'r{}'.format(axis))) dcc.set_attribute_value(jnt, 'r{}'.format(axis), 0) dcc.select_node(joints, replace_selection=True) reply['success'] = True