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