def _get_curve_type(self, curve): curve_type_value = None if dcc.attribute_exists(curve, 'curveType'): curve_type_value = dcc.get_attribute_value(curve, 'curveType') elif dcc.attribute_exists(curve, 'type'): curve_type_value = dcc.get_attribute_value(curve, 'type') return curve_type_value
def _update_curve_data(self, transform, curve_data): if dcc.attribute_exists(transform, 'curveType') or dcc.attribute_exists( transform, 'type'): value = dcc.get_attribute_value(transform, 'curveType') if value: curve_data['type'] = value if dcc.attribute_exists(transform, 'type'): value = dcc.get_attribute_value(transform, 'type') if value: curve_data['type'] = value
def match_rotation(source_transform=None, target_transform=None): """ Matches rotation of the source node to the rotation of the given target node(s) """ out_dict = {'success': False, 'result': list()} selection = dcc.selected_nodes_of_type(node_type='transform') source_transform = source_transform or selection[ 0] if python.index_exists_in_list(selection, 0) else None if not source_transform: out_dict[ 'msg'] = 'No source transform given to match against target rotation.' return out_dict target_transform = target_transform or selection[1:] if len( selection) > 1 else None if not source_transform: out_dict[ 'msg'] = 'No target transform(s) given to match source rotation against.' return out_dict source_transform = python.force_list(source_transform) target_transform = python.force_list(target_transform) percentage = 100.0 / len(source_transform) for i, source in enumerate(source_transform): library.Command.progressCommand.emit( percentage * (i + 1), 'Matching rotation: {}'.format(source)) try: maya.cmds.delete( maya.cmds.orientConstraint(target_transform, source, maintainOffset=False)) # For joints, we store now rotation data in jointOrient attribute if dcc.node_type(source) == 'joint': for axis in 'XYZ': joint_orient_attr = 'jointOrient{}'.format(axis) joint_rotation_attr = 'rotate{}'.format(axis) dcc.set_attribute_value(source, joint_orient_attr, 0.0) joint_rotation = dcc.get_attribute_value( source, joint_rotation_attr) dcc.set_attribute_value(source, joint_orient_attr, joint_rotation) dcc.set_attribute_value(source, joint_rotation_attr, 0.0) out_dict['result'].append(source) except Exception as exc: out_dict[ 'msg'] = 'Was not possible to match node "{}" rotation to "{}" : {}'.format( source_transform, target_transform, exc) return out_dict matched_nodes = out_dict.get('result', None) if matched_nodes: dcc.select_node(matched_nodes) out_dict['success'] = True return out_dict
def _create_squash_stretch_setup(self): dcc.add_title_attribute(self._global_control.get(), 'volume') dcc.add_bool_attribute(self._global_control.get(), 'enable') curve_shape = dcc.list_shapes(self._wire_curve)[0] curve_info = dcc.create_node('curveInfo', self._get_name('curveLength', node_type='curveInfo')) dcc.connect_attribute(curve_shape, 'worldSpace[0]', curve_info, 'inputCurve') current_length = dcc.get_attribute_value(curve_info, 'arcLength') squash_stretch_divide = dcc.create_node( 'multiplyDivide', node_name=self._get_name('squashStretchDivide', node_type='multiplyDivide')) dcc.set_attribute_value(squash_stretch_divide, 'operation', 2) # divide dcc.set_attribute_value(squash_stretch_divide, 'input1X', current_length) dcc.connect_attribute(curve_info, 'arcLength', squash_stretch_divide, 'input2X') squash_stretch_volume_multiplier = dcc.create_node( 'multiplyDivide', node_name=self._get_name('squashStretchVolume', node_type='multiplyDivide')) dcc.set_attribute_value(squash_stretch_volume_multiplier, 'input1X', 1.0) dcc.connect_attribute(squash_stretch_divide, 'outputX', squash_stretch_volume_multiplier, 'input2X') squash_stretch_enabled = dcc.create_node( 'condition', node_name=self._get_name('squashStretchEnable', node_type='condition')) dcc.connect_attribute(self._global_control.get(), 'enable', squash_stretch_enabled, 'firstTerm') dcc.set_attribute_value(squash_stretch_enabled, 'secondTerm', 1.0) dcc.connect_attribute(squash_stretch_volume_multiplier, 'outputX', squash_stretch_enabled, 'colorIfTrueR') for joint in self._joints: dcc.connect_attribute(squash_stretch_enabled, 'outColorR', joint, 'scaleY') dcc.connect_attribute(squash_stretch_enabled, 'outColorR', joint, 'scaleZ')
def set_curve_type(self, type_name=None, keep_color=True, **kwargs): """ Updates the curves of the control with the given control type :param type_name: str :param keep_color: bool """ if not type_name: if dcc.attribute_exists(self.get(), 'curveType'): type_name = dcc.get_attribute_value(self.get(), 'curveType') shapes = shape_utils.get_shapes(self.get()) color = kwargs.pop('color', None) kwargs['color'] = color or (node_utils.get_rgb_color(shapes[0]) if shapes else 0) super(MayaRigControl, self).set_curve_type(type_name=type_name, keep_color=keep_color, **kwargs) self.update_shapes() string_attr = attr_utils.StringAttribute('curveType') string_attr.create(self._name) string_attr.set_value(type_name) return True
def save(self, *args, **kwargs): dependencies = dict() filepath = self.format_identifier() if not filepath.endswith(MayaAttributesData.EXTENSION): filepath = '{}{}'.format(filepath, MayaAttributesData.EXTENSION) if not filepath: LOGGER.warning( 'Impossible to save Attributes file because save file path not defined!' ) return objects = kwargs.get('objects', None) scope = self._get_scope(objects) if not scope: LOGGER.warning( 'Nothing selected to export attributes of. Please, select a mesh,' ' curve, NURBS surface or lattice with skin weights to export') return False LOGGER.debug('Saving {} | {}'.format(filepath, kwargs)) if not os.path.isdir(filepath): attributes_folder = folder.create_folder(filepath) dependencies[attributes_folder] = 'attributes_folder' for obj in scope: LOGGER.info('Exporting attributes of {}'.format(obj)) file_name = fileio.create_file('{}{}'.format(obj, self.EXTENSION), filepath) lines = list() attributes_to_export = self._get_attributes(obj) shapes = self._get_shapes(obj) if shapes: shape = shapes[0] shape_attributes = self._get_shape_attributes(shape) if shape_attributes: attributes_to_export = list( set(attributes_to_export).union(shape_attributes)) if not attributes_to_export: continue for attribute_to_export in attributes_to_export: try: value = dcc.get_attribute_value(obj, attribute_to_export) except Exception: continue lines.append("[ '{}', {} ]".format(attribute_to_export, value)) write_file = fileio.FileWriter(file_name) write_file.write(lines) if file_name and os.path.isfile(file_name): dependencies[file_name] = 'attribute' LOGGER.info('Attributes data export operation completed successfully!') return dependencies
def create_buffer_groups(target=None, depth=1): """ Creates buffer groups for the given target :param target: str :param depth: int :return: list(str) """ result = list() target = python.force_list(target) for tgt in target: rotate_order = dcc.get_attribute_value(tgt, 'rotateOrder') for i in range(depth): obj_parent = maya.cmds.listRelatives(tgt, parent=True, fullPath=True) empty_group_name = 'buffer' if i == 0 else 'buffer{}'.format(i) empty_group = maya.cmds.group(empty=True, n=empty_group_name, world=True) dcc.set_attribute_value(empty_group, 'rotateOrder', rotate_order) if obj_parent: maya.cmds.parent(empty_group, obj_parent) obj_transform = [ dcc.get_attribute_value(tgt, xform) for xform in 'tsr' ] tgt = maya.cmds.parent(tgt, empty_group)[0] for j, xform in enumerate('tsr'): maya.cmds.setAttr('{}.{}'.format(empty_group, xform), *obj_transform[j], type='double3') reset_xform = (0, 0, 0) if j != 1 else (1, 1, 1) maya.cmds.setAttr('{}.{}'.format(tgt, xform), *reset_xform, type='double3') result.append(empty_group) return result
def _dcc_native_attribute(self, attribute_name, default=None): """ Internal function that returns the value of the attribute of the wrapped DCC object :param attribute_name: str, name of the attribute we want retrieve value of :param default: variant, fallback default value if attribute does not exists in wrapped DCC object :return: """ node_name = node_utils.get_name(self._dcc_native_object, fullname=True) try: return dcc.get_attribute_value(node_name, attribute_name) except Exception: return default
def transfer_static(self, source_object, target_object, mirror_axis=None, attrs=None, option=None): option = option or MirrorOptions.Swap source_value = None target_value = None source_valid = self.is_valid_mirror(source_object, option) target_valid = self.is_valid_mirror(target_object, option) attrs = attrs or dcc.list_attributes(source_object, keyable=True) or list() for attr in attrs: target_attr = '{}.{}'.format(target_object, attr) if dcc.node_exists(target_attr): if target_valid: source_value = dcc.get_attribute_value(source_object, attr) if source_valid: target_value = dcc.get_attribute_value(target_object, attr) if target_valid: self.set_attribute(target_object, attr, source_value, mirror_axis=mirror_axis) if source_valid: self.set_attribute(source_object, attr, target_value, mirror_axis=mirror_axis) else: logger.warning('Cannot find destination attribute "{}"'.format(target_attr))
def get_render_globals_attribute(attribute_name, default_value=None): """ Internal function that returns value of the given attribute of the render global Maya node :param attribute_name: str, name of the attribute to get :param default_value: object, value that is returned if the attribute does not exists :return: object """ render_globals_node = get_render_globals_node_name() if not dcc.attribute_exists(render_globals_node, attribute_name): LOGGER.warning( 'Attribute "{}" does not exists in RenderGlobals node "{}"!'. format(attribute_name, render_globals_node)) return default_value return dcc.get_attribute_value(render_globals_node, attribute_name)
def get_default_resolution_attribute(attribute_name, default_value=None): """ Internal function that returns value of the given attribute of the default resolution Maya node :param attribute_name: str, name of the attribute to get :param default_value: object, value that is returned if the attribute does not exists :return: object """ default_resolution_node = get_default_resolution_node_name() if not dcc.attribute_exists(default_resolution_node, attribute_name): LOGGER.warning( 'Attribute "{}" does not exists in DefaultResolution node "{}"!'. format(attribute_name, default_resolution_node)) return default_value return dcc.get_attribute_value(default_resolution_node, attribute_name)
def get_attributes_data_to_store(self, data, reply): node = data['node'] transform_attributes = data['transform_attributes'] user_attributes = data['user_attributes'] if not node: reply['msg'] = 'No note given to retrieve attributes to store of' reply['success'] = False return attributes_data = dict() if transform_attributes: xform_attribute_names = [ constants.TRANSLATION_ATTR_NAME, constants.ROTATION_ATTR_NAME, constants.SCALE_ATTR_NAME ] for xform in xform_attribute_names: xform_value = dcc.get_attribute_value(node, xform) if xform == constants.ROTATION_ATTR_NAME: rotation_as_euler = rt.quatToEuler(xform_value) xform_value = [ rotation_as_euler.x, rotation_as_euler.y, rotation_as_euler.z ] else: xform_value = list(xform_value) for i, axis in enumerate(constants.AXES): channel = '{}{}'.format(xform, axis.upper()) attributes_data[channel] = xform_value[i] if dcc.is_attribute_locked(node, channel): attributes_data[channel][i] = None if user_attributes: pass reply['result'] = attributes_data reply['success'] = True
def get_attributes_data_to_store(self, data, reply): node = data['node'] transform_attributes = data['transform_attributes'] user_attributes = data['user_attributes'] if not node: reply['msg'] = 'No note given to retrieve attributes to store of' reply['success'] = False return attributes_data = dict() attributes_to_store = list() if transform_attributes: xform_attribute_names = [ constants.TRANSLATION_ATTR_NAME, constants.ROTATION_ATTR_NAME, constants.SCALE_ATT_NAME] for xform in xform_attribute_names: for axis in constants.AXES: channel = '{}{}'.format(xform, axis.upper()) if dcc.is_attribute_locked(node, channel): continue attributes_to_store.append(channel) if user_attributes: for attr in dcc.list_user_attributes(node): attr_name = dcc.node_attribute_name(attr) if dcc.get_attribute_type(node, attr_name) in ('double', 'int'): continue if dcc.is_attribute_locked(node, attr_name): continue attributes_to_store.append(attr_name) for attr in attributes_to_store: attributes_data[attr] = dcc.get_attribute_value(node, attr) reply['result'] = attributes_data reply['success'] = 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 is_control(transform_node, only_tagged=False, **kwargs): """ Returns whether or not given transform is a rig control :param transform_node: str :param only_tagged: bool :return: bool """ maybe_control = False names_to_skip = python.force_list(kwargs.get('names_to_skip', list())) prefixes_to_check = python.force_list( kwargs.get('prefixes_to_check', list())) prefixes_to_skip = python.force_list(kwargs.get('prefixes_to_skip', list())) suffixes_to_check = python.force_list( kwargs.get('suffixes_to_check', list())) suffixes_to_skip = python.force_list(kwargs.get('suffixes_to_skip', list())) attributes_to_check = python.force_list( kwargs.get('attributes_to_check', list())) attributes_to_skip = python.force_list( kwargs.get('attributes_to_skip', list())) names_to_skip.extend(consts.CONTROLS_NAMES_TO_SKIP) prefixes_to_check.extend(consts.CONTROLS_PREFIXES) prefixes_to_skip.extend(consts.CONTROLS_PREFIXES_TO_SKIP) suffixes_to_check.extend(consts.CONTROLS_SUFFIXES) suffixes_to_skip.extend(consts.CONTROLS_SUFFIXES_TO_SKIP) attributes_to_check.extend(consts.CONTROLS_ATTRIBUTES) attributes_to_skip.extend(consts.CONTROLS_ATTRIBUTES_TO_SKIP) names_to_skip = tuple(set(names_to_skip)) prefixes_to_check = tuple(set(prefixes_to_check)) prefixes_to_skip = tuple(set(prefixes_to_skip)) suffixes_to_check = tuple(set(suffixes_to_check)) suffixes_to_skip = tuple(set(suffixes_to_skip)) attributes_to_check = tuple(set(attributes_to_check)) attributes_to_skip = tuple(set(attributes_to_skip)) # TODO: We could use nomenclature to check control validity transform = name_utils.remove_namespace_from_string(transform_node) if only_tagged: if dcc.attribute_exists(transform, 'tag'): value = dcc.get_attribute_value(transform, 'tag') if value: return True return False else: # Check names if transform in names_to_skip: return False # Check prefix and suffix if transform.startswith(prefixes_to_skip) or transform.endswith( suffixes_to_skip): return False if transform.startswith(prefixes_to_check) or transform.endswith( suffixes_to_check): maybe_control = True # Check attributes for attr_to_skip in attributes_to_skip: if dcc.attribute_exists(transform, attr_to_skip): return False for attr_to_check in attributes_to_check: if dcc.attribute_exists(transform, attr_to_check): return True if dcc.attribute_exists(transform, 'tag'): value = dcc.get_attribute_value(transform, 'tag') if value: maybe_control = True if dcc.attribute_exists(transform, 'curveType') or dcc.attribute_exists( transform, 'type'): value = dcc.get_attribute_value(transform, 'curveType') if value: maybe_control = True if dcc.attribute_exists(transform, 'type'): value = dcc.get_attribute_value(transform, 'type') if value: maybe_control = True if maybe_control: if dcc.node_has_shape_of_type( transform, 'nurbsCurve') or dcc.node_has_shape_of_type( transform, 'nurbsSurface'): return True return False
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