Beispiel #1
0
    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
Beispiel #2
0
 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
Beispiel #3
0
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')
Beispiel #5
0
    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
Beispiel #6
0
    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
Beispiel #8
0
    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))
Beispiel #10
0
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)
Beispiel #11
0
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