def build( surface=None, ref_xforms=None, up_xform=None, num_controls=3, num_follicles=10, control_scheme="fk", name={"name": "ribbon"}, spansUV=[1, 4], scale=1.0, baseRig="auto", parent=None, ): """ Takes two points or a given surface and creates a ribbon module based on that. Args: surface (pm.nt.nurbsCurve): nurbs curve to generate things on ref_xforms [pm.nt.Transform]: the two points to make a ribbon between num_controls (int): number of controls to be generated num_follicles (int): number of follicles (and resulting joints) to be generated Returns list(grp, list(joints)) Usage: build(ref_xforms=[pm.PyNode('locator1'),pm.PyNode('locator2')], up_xform=pm.PyNode('locator3'), num_follicles=12, num_controls=2, name="path", control_scheme='ik') @param baseRig: instance of core.module.Base class @return: dictionary with rig module objects """ with pm.UndoChunk(): # make rig module namer.set(name) namer.type.set("group") rigmodule = module.Module(name=namer.get_dict(), baseObj=baseRig) mesh_grp = pm.group(n=namer.get(childType="mesh"), em=True, p=rigmodule.partsGrp) ribbon_grp = pm.group(n=namer.get(childType="ribbons"), em=True, p=rigmodule.partsGrp) fol_grp = pm.group(n=namer.get(childType="follicles"), em=True, p=rigmodule.partsGrp) # TODO: Should be able to pass namer classes in case you want to override the default namer drive_skeleton_grp = pm.group( em=True, n=namer.get(purpose="drive", childType="skeleton"), p=rigmodule.jointsGrp ) skin_skeleton_grp = pm.group(em=True, n=namer.get(purpose="skin", childType="skeleton"), p=rigmodule.jointsGrp) # Now on to the good stuff. xformA, xformB = ref_xforms # First create follicles and ribbon ribbon = mesh_factory.build_nurbs_between_points(xformA, xformB, up_xform, spansUV=spansUV) ribbon.rename(namer.get(type="nurbs", purpose="ribbon")) ribbon.setParent(mesh_grp) follicles = [] spacing = 1.0 / (num_follicles - 1) for fol_index in range(0, num_follicles): follicles.append( follicle_factory.build( ribbon, 0.5, fol_index * spacing, name=namer.get_dict(var=namer.get_alpha(fol_index)), scale=scale, parent=fol_grp, ) ) # Then create x many controls between pointA and pointB control_positions = mesh_factory.get_positions_along_mesh_UV(ribbon, num_controls, u_override=0.5, dir="v") controls = control_factory.build_control_chain( control_positions, chain_type=control_scheme, name=namer.get_dict(), scale=scale, parent=rigmodule.priControlsGrp, shape="sphere", lockChannels=["s", "v"], ) controls = [control.C for control in controls] # Then create the offset grouped joints on each follicle and connect the offset groups movements to the follicles namer.reset(name) drive_joints = joint_factory.build_from_points( controls, name=namer.get_dict(purpose="drive"), chain=True, parent=drive_skeleton_grp ) skin_joints = joint_factory.build_from_points( follicles, name=namer.get_dict(purpose="skin"), parent=skin_skeleton_grp, offset=True ) # Need to unparent the control joints in order to skin them and then reparent joint_parents = {} for joint in drive_joints: joint_parents[joint] = joint.getParent() joint.setParent(w=True) pm.skinCluster(drive_joints, ribbon) for joint in joint_parents: joint.setParent(joint_parents[joint], a=True) del (joint_parents) # Connect the controls to the drive joints for control, drive_joint in zip(controls, drive_joints): pm.parentConstraint(control, drive_joint, mo=True) # Connect the skin joints to the follicle positions with offset groups for skin_joint, follicle in zip(skin_joints, follicles): follicle.translate.connect(skin_joint.getParent().translate) follicle.rotate.connect(skin_joint.getParent().rotate) # Profit! """
def build( curve, aim_curve, num_joints = 0, prefix = 'splineIKTwist', rebuild = True, scale = 1.0, constrain_object = None, mo = True, aimVector = (0,1,0), upVector = (1,0,0), worldUpType = 'object', #objectUp baseRig = 'auto', parent = None ): """Takes two curves, creates joints on the first and aims objects at the second. Args: aim_curve (pm.nt.nurbsCurve): nurbs curve to generate things on aimCrv (pm.nt.nurbsCurve): nurbs curve to aim things to num_jnts (int): number of joints to be generated other : all of the aim vector constraint settings Returns list(grp, list(joints)) Usage: build(pm.ls(sl=True)[0], pm.ls(sl=True)[1], num_joints=20, prefix="path") @param curves: [pm.nt.Transform], list of transforms with curve shape nodes @param target_surface: pm.nt.Transform, transform with a shape node @param prefix: str, prefix to name new objects @param snapToSurface: int, snap to surface or not (0,1) @param scale: float, scale factor for size of controls @param baseRig: instance of core.module.Base class @return: dictionary with rig module objects """ # make rig module namer.name.set(prefix) namer.type.set('group') rigmodule = module.Module( prefix = prefix, baseObj = baseRig ) mesh_grp = pm.group(n=namer.get(purpose='mesh'), em=True, p=rigmodule.partsGrp) aim_grp = pm.group(n=namer.get(purpose='aimdriver'), em=True, p=rigmodule.partsGrp) clstr_grp = pm.group(n=namer.get(childType='clusters'), em=True, p=rigmodule.partsGrp) crv_grp = pm.group(n=namer.get(childType='curves'), em=True, p=rigmodule.partsGrp) # TODO: Should be able to pass namer classes in case you want to override the default namer drive_skeleton_grp = pm.group(em=True, n=namer.get(purpose='drive', childType='skeleton'), p=rigmodule.jointsGrp) ik_skeleton_grp = pm.group(em=True, n=namer.get(purpose='ik', childType='skeleton'), p=rigmodule.jointsGrp) # Actual creation of joints joints = joint_factory.make_joints_along_curve(curve, num_joints=num_joints, prefix=prefix, rebuild=rebuild) ikJoints = joint_factory.make_joints_along_curve(curve, num_joints=num_joints, prefix=prefix+'IK', rebuild=rebuild) # Getting names of next parts of the puzzle locator_names = namer.get_chain(num_joints, var='A', type='locator', name=prefix, purpose='aimdrv') poci_names = namer.get_chain(num_joints, var='A', type='poci', name=prefix, purpose='aimdrv') index = 0 # How do we get the names after the fact... for joint, ikJoint, loc_name, poci_name in zip(joints, ikJoints, locator_names, poci_names): joint.displayLocalAxis.set(1) #print joint, ikJoint, loc_name, poci_name # poci = pointOnCurveInfo joint.setParent(drive_skeleton_grp) poci_orig, cpoc, pc = curve_factory.get_closest_point_and_position_on_curve(joint, curve) # extra point on curve info for the aim curve to have parallel poci = pm.nodetypes.PointOnCurveInfo(n=poci_name) #poci.turnOnPercentage.set(1) aim_curve.getShape().attr("worldSpace[0]").connect(poci.inputCurve) poci_orig.parameter.connect(poci.parameter) # aim curve locator for look up loc = pm.spaceLocator(n=loc_name) loc.setParent(aim_grp) poci.position.connect(loc.t) pc = pm.pointConstraint(ikJoint, joint, mo=True) # make sure the joints aim down chain if index != len(joints)-1: next_joint = ikJoints[index+1] ac = pm.aimConstraint(loc, joint, mo=mo, aimVector=aimVector, upVector=upVector, worldUpType=worldUpType, worldUpObject=next_joint.name() ) else: ac = pm.aimConstraint(loc, joint, mo=mo, aimVector=aimVector, upVector=upVector) index+=1 ikHandle = pm.ikHandle(sj=ikJoints[0], ee=ikJoints[-1], sol='ikSplineSolver', c=curve, ccv=False, rtm=False)[0] # Now setup the clusters base_clusters = curve_factory.cluster_curve(curve, prefix='baseClstr', ends=True) offset_clusters = curve_factory.cluster_curve(aim_curve, prefix='aimClstr', ends=True) for b_clstr, o_clstr in zip(base_clusters, offset_clusters): pm.parentConstraint(b_clstr[1], o_clstr[1], mo=True) o_clstr[1].visibility.set(0) b_clstr[1].setParent(clstr_grp) o_clstr[1].setParent(clstr_grp) # And finally clean up the grouping/vis ikJoints[0].setParent(ik_skeleton_grp) curve.setParent(crv_grp) aim_curve.setParent(crv_grp) ikHandle.setParent(ik_skeleton_grp) aim_grp.inheritsTransform.set(0) #ik_skeleton_grp.visibility.set(0) #crv_grp.visibility.set(0) return rigmodule