コード例 #1
0
    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
コード例 #2
0
    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)
コード例 #3
0
def separate_meshes(meshes=None, new_mesh_name=None):
    """
    Separates 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 separate selected.'
        return out_dict
    new_mesh_name = new_mesh_name or dcc.node_short_name(meshes[0])

    try:
        result_meshes = list()
        separated_meshes = dcc.separate_meshes(construction_history=False)
        if not separated_meshes:
            out_dict[
                'msg'] = 'Separate operation was done but not separated mesh was generated'
            return out_dict
        for separated_mesh in separated_meshes:
            separated_mesh = dcc.rename_node(separated_mesh, new_mesh_name)
            result_meshes.append(separated_mesh)

        out_dict['result'] = result_meshes
    except Exception as exc:
        out_dict[
            'msg'] = 'Was not possible to separate meshes "{}" : {}'.format(
                meshes, exc)
        return out_dict

    out_dict['success'] = True

    return out_dict
コード例 #4
0
    def create(self):
        self._start_joint = dcc.node_short_name(self._start_joint
                                                or self.joints[0])
        self._end_joint = dcc.node_short_name(self._end_joint
                                              or self.joints[-1])
        self._start_index = self.joints.index(
            dcc.node_short_name(self._start_joint))
        self._end_index = self.joints.index(
            dcc.node_short_name(self._end_joint))

        super(IkRig, self).create()

        self._create_pole_vector_control()
        self._create_bottom_control()
        self._setup_bottom_control()

        if self._build_pole_vector_control:
            self._create_pole_vector()
コード例 #5
0
    def _get_joint_name(self, name, index=None, side=None):
        if index is not None:
            name = '{}_{}'.format(name, index)
        if side is not None:
            name = '{}_{}'.format(name, side)

        for joint in self._joints:
            if dcc.node_short_name(joint) == name:
                return joint

        return None
コード例 #6
0
def set_shape(crv,
              crv_shape_list,
              size=None,
              select_new_shape=False,
              keep_color=False):
    """
    Creates a new shape on the given curve
    :param crv:
    :param crv_shape_list:
    :param size:
    :param select_new_shape: bool
    :param keep_color: bool
    """

    crv_shapes = controlutils.validate_curve(crv)

    orig_size = None
    orig_color = None
    if crv_shapes:
        orig_size = dcc.node_bounding_box_size(crv)

        # If there are multiple shapes, we only take into account the color of the first shape
        orig_color = dcc.node_color(crv_shapes[0])

    if crv_shapes:
        dcc.delete_node(crv_shapes)

    for i, c in enumerate(crv_shape_list):
        new_shape = dcc.list_shapes(c)[0]
        new_shape = dcc.rename_node(
            new_shape,
            dcc.node_short_name(crv) + 'Shape' + str(i + 1).zfill(2))
        dcc.enable_overrides(new_shape)
        if orig_color is not None and keep_color:
            dcc.set_node_color(new_shape, orig_color)
        dcc.combine_shapes(crv, new_shape, delete_after_combine=True)

    new_size = dcc.node_bounding_box_size(crv)

    if orig_size and new_size:
        scale_size = orig_size / new_size
        dcc.scale_shapes(crv, scale_size, relative=False)

    if size:
        dcc.scale_shapes(crv, size, relative=True)

    if select_new_shape:
        dcc.select_node(crv)

    return crv
コード例 #7
0
    def namespaces(self):
        """
        Returns the namespaces contained in the transfer object
        :return: list(str)
        """

        if self._namespaces is None:
            group_namespaces = dict()
            for name in self.objects():
                node_namespace = namespace.get_namespace(name)
                if not node_namespace:
                    continue
                group_namespaces.setdefault(dcc.node_short_name(name), list())
                group_namespaces[node_namespace].append(name)
            self._namespaces = list(group_namespaces.keys())

        return self._namespaces
コード例 #8
0
    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
コード例 #9
0
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
コード例 #10
0
    def import_data(self, **kwargs):

        filepath = self.format_identifier()
        if not filepath.endswith(ControlCVsData.EXTENSION):
            filepath = '{}{}'.format(filepath, ControlCVsData.EXTENSION)

        if not filepath:
            logger.warning(
                'Impossible to load Control CVs from file: "{}"!'.format(
                    filepath))
            return False

        library = self._initialize_library(filepath)

        objects = kwargs.get('objects', None)
        if not objects:
            objects = dcc.client().selected_nodes(full_path=True) or list()

        # We make sure that we store the short name of the controls
        objects = [dcc.node_short_name(obj) for obj in objects]

        updated_controls = list()
        controls = objects or controllib.get_controls()
        for control in controls:
            shapes = dcc.client().get_curve_shapes(control)
            if not shapes:
                continue
            library.set_curve(control, check_curve=True)
            updated_controls.append(control)

        # We force the update of all the curves that are stored in the library and are in the scene
        for curve_data in list(library._library_curves.values()):
            for curve_name in list(curve_data.keys()):
                if curve_name and dcc.node_exists(
                        curve_name) and curve_name not in updated_controls:
                    library.set_curve(curve_name, check_curve=True)
                    updated_controls.append(curve_name)

        logger.info('Imported {} data'.format(self.name()))

        return True
コード例 #11
0
    def remove_last(self, data, reply):
        num_to_remove = data.get('count', 0)
        rename_shape = data.get('rename_shape', True)
        search_hierarchy = data.get('hierarchy_check', False)
        selection_only = data.get('only_selection', True)
        filter_type = data.get('filter_type', None)

        if not num_to_remove > 0:
            msg = 'Specify a number of characters to remove greater than zero ({})'.format(
                num_to_remove)
            LOGGER.warning(msg)
            reply['success'] = False
            reply['msg'] = msg
            return

        if not search_hierarchy and not selection_only:
            msg = 'Remove last must be used with "Selected" options not with "All"'
            LOGGER.warning(msg)
            reply['success'] = False
            reply['msg'] = msg
            return

        filtered_obj_list = dcc.filter_nodes_by_type(
            filter_type=filter_type,
            search_hierarchy=search_hierarchy,
            selection_only=selection_only)

        for obj in filtered_obj_list:
            original_name = dcc.node_short_name(obj)
            new_name = obj[:-num_to_remove]
            if not new_name:
                LOGGER.warning(
                    'Impossible to rename {}. Total characters to remove is greater or equal than '
                    'the original name length: {} >= {}'.format(
                        original_name, num_to_remove, len(original_name)))
                continue
            dcc.rename_node(obj, new_name, rename_shape=rename_shape)

        reply['success'] = True
コード例 #12
0
    def _update_names_list(self, nodes):
        """
        Internal function that updates names list with given node names
        :param nodes: list(str)
        """

        nodes_to_discard = self._get_nodes_to_discard() or list()

        nodes = list(set(nodes))

        for obj in nodes:
            if obj in nodes_to_discard:
                continue
            node_type = dcc.node_type(obj)
            if node_type not in self._get_node_types(
            ) and not self._others_btn.isChecked():
                is_valid = False
                for node_type in self._get_node_types():
                    is_valid = dcc.check_object_type(obj,
                                                     node_type,
                                                     check_sub_types=True)
                    if is_valid:
                        break
                if not is_valid:
                    continue

            node_name = dcc.node_short_name(obj)
            item = QTreeWidgetItem(self._names_list, [node_name])
            item.obj = node_name
            item.preview_name = ''
            item.full_name = obj
            if dcc.is_maya():
                sel = api.SelectionList()
                sel.add(obj)
                item.handle = maya.OpenMaya.MObjectHandle(
                    sel.get_depend_node(0))

            self._names_list.addTopLevelItem(item)
コード例 #13
0
def mirror_control(source_control,
                   target_control=None,
                   mirror_axis='X',
                   mirror_mode=0,
                   mirror_color=None,
                   mirror_replace=False,
                   keep_color=True,
                   from_name=None,
                   to_name=None):
    """
    Find the right side control of a left side control and mirrors the control following next rules:
        - Mirror only will be applied if corresponding right side name exists
        - Replace left prefix and suffixes checking for validity
    :param mirror_axis: str
    :param mirror_mode: int or None
    :param mirror_color: int or list(float, float, float)
    :param mirror_replace: bool
    :param keep_color: bool
    :return: str, mirrored control
    """

    if keep_color:
        target_control_color = get_control_color(source_control)
    else:
        target_control_color = get_control_color(
            source_control
        ) if not mirror_color and keep_color else mirror_color

    source_shapes = dcc.list_shapes_of_type(source_control, 'nurbsCurve')
    if not source_shapes:
        return None

    duplicated_control = duplicate_control(source_control)
    mirror_pivot_grp = dcc.create_empty_group(name='temp_mirrorPivot')
    duplicated_control = dcc.set_parent(duplicated_control, mirror_pivot_grp)

    dcc.set_attribute_value(mirror_pivot_grp,
                            'scale{}'.format(mirror_axis.upper()), -1)

    target_control = target_control or source_control.replace(
        from_name, to_name)

    # We force this conversion. This is something that we should remove in the future
    if target_control and not dcc.node_exists(target_control):
        target_control = target_control.replace('Left', 'Right')

    if target_control and not dcc.node_exists(target_control):
        target_control = dcc.node_short_name(target_control)

    if target_control and dcc.node_exists(target_control) and mirror_replace:
        if keep_color:
            target_control_color = get_control_color(target_control)
        mirrored_control = xform_utils.parent_transforms_shapes(
            target_control, duplicated_control, delete_original=True)
    else:
        mirrored_control = dcc.set_parent_to_world(duplicated_control)

    maya.cmds.delete(mirror_pivot_grp)

    if mirror_mode == 0:
        dcc.move_node(mirrored_control, 0, 0, 0, world_space=True)
    elif mirror_mode == 1:
        orig_pos = dcc.node_world_space_pivot(source_control)
        dcc.move_node(mirrored_control,
                      orig_pos[0],
                      orig_pos[1],
                      orig_pos[2],
                      world_space=True)

    if target_control_color:
        target_shapes = dcc.list_shapes_of_type(mirrored_control,
                                                shape_type='nurbsCurve')
        for target_shape in target_shapes:
            dcc.set_node_color(target_shape, target_control_color)

    if from_name and to_name and from_name != to_name:
        if from_name in mirrored_control:
            mirrored_control = dcc.rename_node(
                mirrored_control, source_control.replace(from_name, to_name))

    return mirrored_control
コード例 #14
0
    def auto_rename(self, tokens_dict, unique_id=True, last_joint_end=True):

        import maya.cmds

        active_rule = self._model.active_rule
        if not active_rule:
            LOGGER.warning('Impossible to auto rename because no active rule defined.')
            return False

        rule_name = active_rule.name

        hierarchy_check = self._model.hierarchy_check
        selection_type = self._model.selection_type
        rename_shape = self._model.rename_shape

        objs_to_rename = utils.get_objects_to_rename(
            hierarchy_check=hierarchy_check, selection_type=selection_type, uuid=False) or list()
        if not objs_to_rename:
            LOGGER.warning('No objects to rename. Please select at least one object!')
            return False
        # generated_names = self.generate_names(items=objs_to_rename, **kwargs)

        if not self._naming_lib.has_rule(rule_name):
            return False

        current_rule = self._naming_lib.active_rule()

        self._naming_lib.set_active_rule(rule_name)

        # TODO: Naming config should be define the name of the rule to use when using auto renaming
        solved_names = dict()
        if rule_name == 'node' and self._model.config:
            auto_suffix = self._model.naming_config.get('auto_suffixes', default=dict())
            if auto_suffix:
                solved_names = dict()
                for i, obj_name in enumerate(reversed(objs_to_rename)):
                    obj_uuid = maya.cmds.ls(obj_name, uuid=True)[0]
                    if obj_uuid in solved_names:
                        LOGGER.warning(
                            'Node with name: "{} and UUID "{}" already renamed to "{}"! Skipping ...'.format(
                                obj_name, obj_uuid, solved_names[obj_name]))
                        continue

                    # TODO: This code is a duplicated version of the one in
                    #  tpDcc.dccs.maya.core.name.auto_suffix_object function. Move this code to a DCC specific function
                    obj_type = maya.cmds.objectType(obj_name)
                    if obj_type == 'transform':
                        shape_nodes = maya.cmds.listRelatives(obj_name, shapes=True, fullPath=True)
                        if not shape_nodes:
                            obj_type = 'group'
                        else:
                            obj_type = maya.cmds.objectType(shape_nodes[0])
                    elif obj_type == 'joint':
                        shape_nodes = maya.cmds.listRelatives(obj_name, shapes=True, fullPath=True)
                        if shape_nodes and maya.cmds.objectType(shape_nodes[0]) == 'nurbsCurve':
                            obj_type = 'controller'
                        else:
                            children = dcc.list_children(obj_name)
                            if not children and last_joint_end:
                                obj_type = 'jointEnd'
                    if obj_type == 'nurbsCurve':
                        connections = maya.cmds.listConnections('{}.message'.format(obj_name))
                        if connections:
                            for node in connections:
                                if maya.cmds.nodeType(node) == 'controller':
                                    obj_type = 'controller'
                                    break
                    if obj_type not in auto_suffix:
                        rule_name = 'node'
                        node_type = obj_type
                    else:
                        rule_name = auto_suffix[obj_type]
                        node_type = auto_suffix[obj_type]

                    if 'node_type' in tokens_dict and tokens_dict['node_type']:
                        node_type = tokens_dict.pop('node_type')
                    node_name = dcc.node_short_name(obj_name)
                    if 'description' in tokens_dict and tokens_dict['description']:
                        description = tokens_dict['description']
                    else:
                        description = node_name
                    side = tokens_dict.get('side', None)
                    if unique_id:
                        solved_name = self._naming_lib.solve(
                            description, side=side, node_type=node_type, id=i)
                    else:
                        solved_name = self._naming_lib.solve(
                            description, side=side, node_type=node_type)
                    if not solved_name:
                        continue
                    solved_name = dcc.find_unique_name(solved_name)
                    solved_names[obj_uuid] = solved_name

        if solved_names:
            for obj_id, solved_name in solved_names.items():
                obj_name = maya.cmds.ls(obj_id, long=True)[0]
                dcc.rename_node(obj_name, solved_name, uuid=obj_id, rename_shape=rename_shape)
        else:
            for obj_name in objs_to_rename:
                solve_name = self._naming_lib.solve(**tokens_dict)
                if not solve_name:
                    LOGGER.warning(
                        'Impossible to rename "{}" with rule "{}" | "{}"'.format(obj_name, rule_name, tokens_dict))
                    continue
                try:
                    dcc.rename_node(obj_name, solve_name, rename_shape=rename_shape)
                except Exception as exc:
                    LOGGER.error('Impossible to rename "{}" to "{}" | {}'.format(obj_name, solve_name, exc))
                    continue

            if current_rule:
                self._naming_lib.set_active_rule(current_rule.name)
コード例 #15
0
    def generate_names(self, items, **kwargs):
        text = kwargs.get('name', '')
        prefix = kwargs.get('prefix', '')
        suffix = kwargs.get('suffix', '')
        padding = kwargs.get('padding', 0)
        naming_method = kwargs.get('naming_method', 0)
        upper = kwargs.get('upper', False)
        side = kwargs.get('side', '')
        remove_first = kwargs.get('remove_first', 0)
        remove_last = kwargs.get('remove_last', 0)
        joint_end = kwargs.get('joint_end', False)
        search_str = kwargs.get('search', '')
        replace_str = kwargs.get('replace', '')

        duplicated_names = dict()
        generated_names = list()

        if dcc.is_maya():
            import maya.api.OpenMaya

        for item in items:
            compare_item = item
            if not text:
                base_name = None
                if dcc.is_maya():
                    if hasattr(item, 'object'):
                        mobj = item.object()
                        try:
                            dag_path = maya.api.OpenMaya.MDagPath.getAPathTo(mobj)
                            base_name = dag_path.partialPathName()
                            compare_item = base_name
                        except Exception as exc:
                            LOGGER.warning('Error while retrieving node path from MObject: {}'.format(exc))
                            continue
                if base_name is None:
                    if hasattr(item, 'obj'):
                        base_name = item.obj
                    else:
                        base_name = dcc.node_short_name(item)
            else:
                base_name = text
                if dcc.is_maya():
                    if hasattr(item, 'object'):
                        mobj = item.object()
                        try:
                            dag_path = maya.api.OpenMaya.MDagPath.getAPathTo(mobj)
                            compare_item = dag_path.partialPathName()
                        except Exception as exc:
                            LOGGER.warning('Error while retrieving node path from MObject: {}'.format(exc))
                            continue

            if base_name == compare_item and not prefix and not suffix and not side:
                generate_preview_name = False
            else:
                generate_preview_name = True
            if base_name in duplicated_names:
                duplicated_names[base_name] += 1
            else:
                duplicated_names[base_name] = 0

            if generate_preview_name:
                if base_name == compare_item and (prefix or suffix or side):
                    index = None
                else:
                    index = duplicated_names[base_name]
                preview_name = self._find_manual_available_name(
                    items, base_name, prefix=prefix, side=side, suffix=suffix, index=index, padding=padding,
                    letters=naming_method, capital=upper, joint_end=joint_end, remove_first=remove_first,
                    remove_last=remove_last, search_str=search_str, replace_str=replace_str)
                while preview_name in generated_names:
                    duplicated_names[base_name] += 1
                    preview_name = self._find_manual_available_name(
                        items, base_name, prefix=prefix, side=side, suffix=suffix, index=duplicated_names[base_name],
                        padding=padding, letters=naming_method, capital=upper, joint_end=joint_end,
                        remove_first=remove_first, remove_last=remove_last, search_str=search_str,
                        replace_str=replace_str)
            else:
                preview_name = base_name

            if not isinstance(item, (str, unicode)) and hasattr(item, 'preview_name'):
                item.preview_name = preview_name

            generated_names.append(preview_name)

        return generated_names
コード例 #16
0
    def _import_skin_weights(self, data_path, mesh):
        if not dcc.node_exists(mesh) or not os.path.isdir(data_path):
            return False

        try:
            if not dcc.is_plugin_loaded('ngSkinTools2'):
                dcc.load_plugin('ngSkinTools2')
            import ngSkinTools2
            from ngSkinTools2 import api as ngst_api
        except ImportError:
            logger.warning(
                'NgSkinTools 2.0 is not installed. Impossible to import ngSkin data'
            )
            return False

        ng_skin_data_path = path_utils.join_path(data_path, 'ngdata.json')
        if not path_utils.is_file(ng_skin_data_path):
            logger.warning(
                'No Ng Skin Data file found: "{}", aborting import skin weights operation ...'
                .format(ng_skin_data_path))
            return False

        is_valid_mesh = False
        shape_types = ['mesh', 'nurbsSurface', 'nurbsCurve', 'lattice']
        for shape_type in shape_types:
            if shape_utils.has_shape_of_type(mesh, shape_type):
                is_valid_mesh = True
                break
        if not is_valid_mesh:
            logger.warning(
                'Node "{}" is not a valid mesh node! Currently supported nodes include: {}'
                .format(mesh, shape_types))
            return False

        logger.info('Importing skin clusters {} --> "{}"'.format(
            mesh, data_path))

        influence_dict = self._get_influences(data_path)
        if not influence_dict:
            logger.warning('No influences data found for: {}'.format(mesh))
            return False

        influences = influence_dict.keys()
        if not influences:
            logger.warning('No influences found for: "{}"'.format(mesh))
            return False
        influences.sort()
        logger.debug('Influences found for {}: {}'.format(mesh, influences))

        short_name = dcc.node_short_name(mesh)
        transfer_mesh = None

        if shape_utils.has_shape_of_type(mesh, 'mesh'):
            orig_mesh = self._import_mesh_obj(data_path)
            if orig_mesh:
                mesh_match = geo_utils.is_mesh_compatible(orig_mesh, mesh)
                if not mesh_match:
                    transfer_mesh = mesh
                    mesh = orig_mesh
                else:
                    dcc.delete_node(orig_mesh)

        # Check if there are duplicated influences and also for the creation of influences that does not currently
        # in the scene
        add_joints = list()
        remove_entries = list()
        for influence in influences:
            joints = dcc.list_nodes(influence, full_path=True)
            if type(joints) == list and len(joints) > 1:
                add_joints.append(joints[0])
                conflicting_count = len(joints)
                logger.warning(
                    'Found {} joints with name {}. Using only the first one: {}'
                    .format(conflicting_count, influence, joints[0]))
                remove_entries.append(influence)
                influence = joints[0]
            if not dcc.node_exists(influence):
                dcc.clear_selection()
                dcc.create_joint(
                    name=influence,
                    position=influence_dict[influence]['position'])
        for entry in remove_entries:
            influences.remove(entry)
        influences += add_joints

        settings_data = dict()
        settings_path = path_utils.join_path(data_path, 'settings.info')
        if path_utils.is_file(settings_path):
            lines = fileio.get_file_lines(settings_path)
            for line in lines:
                test_line = line.strip()
                if not test_line:
                    continue
                line_list = eval(line)
                attr_name = line_list[0]
                value = line_list[1]
                settings_data[attr_name] = value

        # Create skin cluster and removes if it already exists
        skin_cluster = deform_utils.find_deformer_by_type(mesh, 'skinCluster')
        if skin_cluster:
            dcc.delete_node(skin_cluster)

        skin_node_name = settings_data.pop('skinNodeName',
                                           'skin_{}'.format(short_name))
        skin_cluster = maya.cmds.skinCluster(
            influences, mesh, tsb=True,
            n=dcc.find_unique_name(skin_node_name))[0]
        dcc.set_attribute_value(skin_cluster, 'normalizeWeights', 0)
        skin_utils.set_skin_weights_to_zero(skin_cluster)

        # TODO: This Influence mapping configuration should be generated during export and imported here as JSON file
        # Import ng skin data
        config = ngst_api.InfluenceMappingConfig()
        config.use_distance_matching = True
        config.use_label_matching = True
        config.use_name_matching = True

        ngst_api.import_json(mesh,
                             file=ng_skin_data_path,
                             influences_mapping_config=config)

        maya.cmds.skinCluster(skin_cluster, edit=True, normalizeWeights=1)
        maya.cmds.skinCluster(skin_cluster,
                              edit=True,
                              forceNormalizeWeights=True)

        for attr_name, value in settings_data.items():
            if attr_name == 'blendWeights':
                skin_utils.set_skin_blend_weights(skin_cluster, value)
            else:
                if dcc.attribute_exists(skin_cluster, attr_name):
                    dcc.set_attribute_value(skin_cluster, attr_name, value)

        if transfer_mesh:
            logger.info(
                'Import sking weights: mesh topology does not match. Trying to transfer topology ...'
            )
            skin_utils.skin_mesh_from_mesh(mesh, transfer_mesh)
            dcc.delete_node(mesh)

        logger.info('Import skinCluster weights: {} from {}'.format(
            short_name, data_path))

        return True
コード例 #17
0
    def save(self, *args, **kwargs):
        """
        Saves NG Skin weights file
        """

        dependencies = dict()

        filepath = self.format_identifier()
        if not filepath:
            logger.warning(
                'Impossible to save NGSkin Cluster Weights file because save file path not defined!'
            )
            return

        objects = kwargs.get('objects', None)
        if not objects:
            objects = dcc.client().selected_nodes(full_path=True)
        if not objects:
            logger.warning(
                'Nothing selected to export skin weights of. Please, select a mesh,'
                ' curve, NURBS surface or lattice with skin weights to export')
            return False

        logger.debug('Saving {} | {}'.format(filepath, kwargs))

        try:
            if not dcc.is_plugin_loaded('ngSkinTools2'):
                dcc.load_plugin('ngSkinTools2')
            import ngSkinTools2
            from ngSkinTools2 import api as ngst_api
        except ImportError:
            logger.warning(
                'NgSkinTools 2.0 is not installed. Impossible to export ngSkin data'
            )
            return False

        valid_nodes = list()

        # Check that all objects that we are going to export have at least one skin cluster node associated
        # Make sure also that all objects skin output folder have been created
        obj_dirs = OrderedDict()
        skin_nodes = OrderedDict()
        geo_paths = OrderedDict()
        skin_weights = OrderedDict()
        for obj in objects:
            if dcc.client().node_is_a_shape(obj):
                obj = dcc.client().node_parent(obj, full_path=True)
            obj_filename = obj
            if obj.find('|') > -1:
                obj_filename = obj_filename.replace('|', '.')
                if obj_filename.startswith('.'):
                    obj_filename = obj_filename[1:]
            if obj_filename.find(':') > -1:
                obj_filename = obj_filename.replace(':', '-')

            skin = dcc.client().find_deformer_by_type(obj, 'skinCluster')
            if not skin:
                logger.warning(
                    'Skip skin weights export for object because no skinCluster found!'
                    .format(obj))
                continue
            valid_nodes.append((obj, obj_filename, skin))

        if not valid_nodes:
            logger.warning(
                'Skin exported failed! No objects found with skinClusters applied!'
            )
            return False

        # Create skin folder only is valid nodes are available
        file_name = '.{}'.format(os.path.basename(filepath))
        file_folder = path_utils.join_path(os.path.dirname(filepath),
                                           file_name)
        if not os.path.isdir(file_folder):
            skin_folder = folder.create_folder(file_folder, make_unique=True)
            dependencies[skin_folder] = 'skin_folder'

        for node_data in valid_nodes:
            obj, obj_filename, skin = node_data

            geo_path = path_utils.join_path(file_folder, obj_filename)
            if path_utils.is_dir(geo_path):
                folder.delete_folder(obj_filename, file_folder)
            geo_path = folder.create_folder(obj_filename, file_folder)
            if not geo_path:
                logger.error(
                    'Unable to create skin weights directory: "{}" in "{}"'.
                    format(obj_filename, file_folder))
                return False
            dependencies[geo_path] = 'geo_path'

            weights = dcc.client().get_skin_weights(skin)

            obj_dirs[obj] = obj_filename
            skin_nodes[obj] = skin
            geo_paths[obj] = geo_path
            skin_weights[obj] = weights

        for (obj, skin_node), (_, geo_path), (_, skin_weights) in zip(
                skin_nodes.items(), geo_paths.items(), skin_weights.items()):

            logger.info('Exporting weights: {} > {} --> "{}"'.format(
                obj, skin_node, geo_path))

            info_lines = list()
            info_file = fileio.create_file('influence.info', geo_path)

            for influence in skin_weights:
                if influence is None or influence == 'None':
                    continue
                weight_list = skin_weights[influence]
                if not weight_list:
                    continue

                influence_name = skin_utils.get_skin_influence_at_index(
                    influence, skin_node)
                if not influence_name or not dcc.node_exists(influence_name):
                    continue

                influence_position = dcc.node_world_space_translation(
                    influence_name)
                influence_line = "{'%s' : {'position' : %s}}" % (
                    influence_name, str(influence_position))
                info_lines.append(influence_line)

            writer = fileio.FileWriter(info_file)
            writer.write(info_lines)

            settings_file = fileio.create_file('settings.info', geo_path)
            setting_lines = list()
            if shape_utils.has_shape_of_type(obj, 'mesh'):
                self._export_mesh_obj(obj, geo_path)

            setting_lines.append("['skinNodeName', '{}']".format(
                dcc.node_short_name(skin_node)))
            if dcc.attribute_exists(skin_node, 'blendWeights'):
                blend_weights = skin_utils.get_skin_blend_weights(skin_node)
                setting_lines.append(
                    "['blendWeights', {}]".format(blend_weights))
            if dcc.attribute_exists(skin_node, 'skinningMethod'):
                skin_method = dcc.get_attribute_value(skin_node,
                                                      'skinningMethod')
                setting_lines.append(
                    "['skinningMethod', {}]".format(skin_method))

            write_settings = fileio.FileWriter(settings_file)
            write_settings.write(setting_lines)

            ng_skin_file_name = os.path.join(geo_path, 'ngdata.json')
            ngst_api.export_json(obj, file=ng_skin_file_name)

            logger.info(
                'Skin weights exported successfully: {} > {} --> "{}"'.format(
                    obj, skin_node, geo_path))

        data_to_save = OrderedDict()
        for obj, obj_filename in obj_dirs.items():
            data_to_save[obj] = {'enabled': True, 'folder': obj_filename}
        with open(filepath, 'w') as fh:
            json.dump(data_to_save, fh)

        logger.info('Skin weights export operation completed successfully!')

        return True
コード例 #18
0
def unbind_influences(skinned_objects=None,
                      influences_to_unbind=None,
                      delete=False,
                      use_parent=True):
    """
    Unbind given influences from given meshes and stores the unbind influences weights into other influences

    :param skinned_objects: list(str), meshes which joints need to be removed
    :param influences_to_unbind: list(str), list of joints that need to be unbind
    :param delete: bool, Whether or not to delete unbind influences after unbind process is completed
    :param use_parent: bool, If True, removed influences weights will be stored on its parent; if False it will look
        for the closest joint using a point cloud system
    :return: bool
    """

    selected_transforms = dcc.selected_nodes_of_type('transform')
    selected_joints = dcc.selected_nodes_of_type('joint')
    influences_to_unbind = influences_to_unbind or selected_joints
    if not skinned_objects:
        skinned_objects = [
            xform for xform in selected_transforms
            if xform not in selected_joints
        ]
    if not skinned_objects or not influences_to_unbind:
        return False
    skinned_objects = python.force_list(skinned_objects)
    influences_to_unbind = python.force_list(influences_to_unbind)
    influences_to_unbind_short = [
        dcc.node_short_name(joint_node) for joint_node in influences_to_unbind
    ]

    skin_clusters = list()
    skin_percentage = 100.0 / len(skinned_objects)

    for skin_index, skin_object in enumerate(skinned_objects):
        skin_cluster_name = skin_utils.find_related_skin_cluster(skin_object)
        if not skin_cluster_name:
            continue
        joints_attached = maya.cmds.skinCluster(skin_cluster_name,
                                                query=True,
                                                inf=True)

        if not use_parent:
            for joint_to_remove in influences_to_unbind_short:
                if joint_to_remove in joints_attached:
                    joints_attached.remove(joint_to_remove)

        source_positions = list()
        source_joints = list()
        for joint_attached in joints_attached:
            pos = maya.cmds.xform(joint_attached,
                                  query=True,
                                  worldSpace=True,
                                  t=True)
            source_positions.append(pos)
            source_joints.append([joint_attached, pos])

        source_kdtree = kdtree.KDTree.construct_from_data(source_positions)

        joint_percentage = skin_percentage / len(influences_to_unbind)
        for joint_index, jnt in enumerate(influences_to_unbind):
            jnt1 = jnt
            if use_parent:
                jnt2 = maya.cmds.listRelatives(jnt, parent=True)
                jnt2 = jnt2[0] if jnt2 else None
                if jnt2 is None:
                    remove_pos = maya.cmds.xform(jnt,
                                                 query=True,
                                                 worldSpace=True,
                                                 t=True)
                    points = source_kdtree.query(query_point=remove_pos, t=1)
                    for index, position in enumerate(source_joints):
                        if position[1] != points[0]:
                            continue
                        jnt2 = position[0]
            else:
                remove_pos = maya.cmds.xform(jnt,
                                             query=True,
                                             worldSpace=True,
                                             t=True)
                points = source_kdtree.query(query_point=remove_pos, t=True)
                for index, position in enumerate(source_joints):
                    if position[1] != points[0]:
                        continue
                    jnt2 = position[0]

            move_skin_weights(jnt1, jnt2, skin_object)

            library.Command.progressCommand.emit(
                ((joint_index + 1) * joint_percentage) +
                (skin_index * skin_percentage), 'Unbinding Influence: {}')

        skin_clusters.append(skin_cluster_name)

    for skin_cluster in skin_clusters:
        joints_attached = maya.cmds.skinCluster(skin_cluster,
                                                query=True,
                                                inf=True)
        for jnt in influences_to_unbind_short:
            if jnt not in joints_attached:
                continue
            maya.cmds.skinCluster(skin_cluster, edit=True, removeInfluence=jnt)

    if delete:
        for joint_to_remove in influences_to_unbind:
            child_joints = maya.cmds.listRelatives(joint_to_remove,
                                                   children=True)
            parent = maya.cmds.listRelatives(joint_to_remove, parent=True)
            if not child_joints:
                continue
            if not parent:
                maya.cmds.parent(child_joints, world=True)
                continue
            maya.cmds.parent(child_joints, parent)
        maya.cmds.delete(influences_to_unbind)

    return True
コード例 #19
0
def unbind_influences_quick(skinned_objects=None,
                            influences_to_unbind=None,
                            delete=False):
    """
    Unbind given influences from given meshes and stores the unbind influences weights into other influences
    The weights reassignation is handled by Maya
    :param skinned_objects: list(str), meshes which joints need to be removed
    :param influences_to_unbind: list(str), list of joints that need to be unbind
    :param delete: bool, Whether or not to delete unbind influences after unbind process is completed
    :return: bool
    """

    selected_transforms = dcc.selected_nodes_of_type('transform')
    selected_joints = dcc.selected_nodes_of_type('joint')
    influences_to_unbind = influences_to_unbind or selected_joints
    if not skinned_objects:
        skinned_objects = [
            xform for xform in selected_transforms
            if xform not in selected_joints
        ]
    if not skinned_objects or not influences_to_unbind:
        return False
    skinned_objects = python.force_list(skinned_objects)
    influences_to_unbind = python.force_list(influences_to_unbind)
    influences_to_unbind_short = [
        dcc.node_short_name(joint_node) for joint_node in influences_to_unbind
    ]

    skin_clusters = list()
    skin_percentage = 100.0 / len(skinned_objects)

    for i, skin_object in enumerate(skinned_objects):

        library.Command.progressCommand.emit(
            skin_percentage * (i + 1), 'Unbinding Influence: {}'.format(i))

        skin_cluster_name = skin_utils.find_related_skin_cluster(skin_object)
        if not skin_cluster_name:
            continue
        joints_attached = maya.cmds.skinCluster(skin_cluster_name,
                                                query=True,
                                                inf=True)

        influence_verts = skin_utils.get_influence_vertices(
            influences_to_unbind, skin_object)
        if not influence_verts:
            continue

        joints = list()
        for joint_to_remove in influences_to_unbind_short:
            if joint_to_remove not in joints_attached:
                continue
            joints.append((joint_to_remove, 0.0))

        maya.cmds.select(influence_verts, replace=True)
        maya.cmds.skinPercent(skin_cluster_name,
                              transformValue=joints,
                              normalize=True)

        skin_clusters.append(skin_cluster_name)

    for skin_cluster in skin_clusters:
        joints_attached = maya.cmds.skinCluster(skin_cluster,
                                                query=True,
                                                inf=True)
        for jnt in influences_to_unbind_short:
            if jnt not in joints_attached:
                continue
            maya.cmds.skinCluster(skin_cluster, edit=True, removeInfluence=jnt)

    if delete:
        for joint_to_remove in influences_to_unbind:
            child_joints = maya.cmds.listRelatives(joint_to_remove,
                                                   children=True)
            parent = maya.cmds.listRelatives(joint_to_remove, parent=True)
            if not child_joints:
                continue
            if not parent:
                maya.cmds.parent(child_joints, world=True)
                continue
            maya.cmds.parent(child_joints, parent)
        maya.cmds.delete(influences_to_unbind)

    return True
コード例 #20
0
    def _get_joints(self, part_name, side=None):
        joints = [joint for joint in self._joints if dcc.node_short_name(joint).startswith(part_name)]
        if side:
            joints = [joint for joint in joints if dcc.node_short_name(joint).endswith('_{}'.format(side))]

        return natsort.natsorted(joints)