From 92f2f8eff6e5fc82af889f8b3bf846545b49c5e9 Mon Sep 17 00:00:00 2001 From: sketchaholic Date: Tue, 14 Jul 2026 09:18:38 -0700 Subject: [PATCH 1/4] Add files via upload adding game ready ik fk chain --- .../EPIC_chain_spline_IK_FK_01/__init__.py | 624 ++++++++++++++++++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 26689 bytes .../__pycache__/guide.cpython-311.pyc | Bin 0 -> 12348 bytes .../__pycache__/settingsUI.cpython-311.pyc | Bin 0 -> 14741 bytes .../EPIC_chain_spline_IK_FK_01/guide.py | 206 ++++++ .../EPIC_chain_spline_IK_FK_01/icon.jpg | Bin 0 -> 2083 bytes .../EPIC_chain_spline_IK_FK_01/settingsUI.py | 167 +++++ .../EPIC_chain_spline_IK_FK_01/settingsUI.ui | 247 +++++++ 8 files changed, 1244 insertions(+) create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/__init__.cpython-311.pyc create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/guide.cpython-311.pyc create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/settingsUI.cpython-311.pyc create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py new file mode 100644 index 00000000..cd3dd184 --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py @@ -0,0 +1,624 @@ +"""Component Chain 01 module""" + +import mgear.pymaya as pm +from mgear.pymaya import datatypes + +from mgear.shifter import component + +from mgear.core import node, applyop, vector +from mgear.core import attribute, transform, primitive, curve + +########################################################## +# COMPONENT +########################################################## + + +class Component(component.Main): + """Shifter component Class""" + + # ===================================================== + # OBJECTS + # ===================================================== + def addObjects(self): + """Add all the objects needed to create the component.""" + + self.normal = self.guide.blades["blade"].z * -1 + self.binormal = self.guide.blades["blade"].x + + self.isFk = self.settings["mode"] != 1 + self.isIk = self.settings["mode"] != 0 + self.isFkIk = self.settings["mode"] == 2 + + self.WIP = self.options["mode"] + + # FK controllers ------------------------------------ + if self.isFk: + self.fk_npo = [] + self.fk_ctl = [] + self.fk_ref = [] + self.fk_off = [] + t = self.guide.tra["root"] + self.fk_cns = primitive.addTransform( + self.root, self.getName("fk_cns"), t) + parent = self.fk_cns + tOld = False + fk_ctl = None + self.previusTag = self.parentCtlTag + for i, t in enumerate(transform.getChainTransform(self.guide.apos, + self.normal, + self.negate)): + dist = vector.getDistance(self.guide.apos[i], + self.guide.apos[i + 1]) + if self.settings["neutralpose"] or not tOld: + tnpo = t + else: + tnpo = transform.setMatrixPosition( + tOld, + transform.getPositionFromMatrix(t)) + if i: + tref = transform.setMatrixPosition( + tOld, + transform.getPositionFromMatrix(t)) + fk_ref = primitive.addTransform( + fk_ctl, + self.getName("fk%s_ref" % i), + tref) + self.fk_ref.append(fk_ref) + else: + tref = t + fk_off = primitive.addTransform( + parent, self.getName("fk%s_off" % i), tref) + fk_npo = primitive.addTransform( + fk_off, self.getName("fk%s_npo" % i), tnpo) + fk_ctl = self.addCtl( + fk_npo, + "fk%s_ctl" % i, + t, + self.color_fk, + "cube", + w=dist, + h=self.size * .1, + d=self.size * .1, + po=datatypes.Vector(dist * .5 * self.n_factor, 0, 0), + tp=self.previusTag) + + self.fk_off.append(fk_off) + self.fk_npo.append(fk_npo) + self.fk_ctl.append(fk_ctl) + tOld = t + self.previusTag = fk_ctl + + # IK controllers ------------------------------------ + if self.isIk: + + normal = vector.getTransposedVector( + self.normal, + [self.guide.apos[0], self.guide.apos[1]], + [self.guide.apos[-2], self.guide.apos[-1]]) + t = transform.getTransformLookingAt(self.guide.apos[-2], + self.guide.apos[-1], + normal, + "xy", + self.negate) + t = transform.setMatrixPosition(t, self.guide.apos[-1]) + + self.ik_cns = primitive.addTransform(self.root, + self.getName("ik_cns"), + t) + # Plain structural group (not a control) - just organizes the + # spline IK internals under ik_cns, not meant to be animated. + self.ikcns_grp = primitive.addTransform( + self.ik_cns, self.getName("ikcns_grp"), t) + + # Spline IK curves ----------------------------------- + # The curve that actually drives the ikSplineSolver is built + # directly through every guide position (one CV per guide + # locator) as a degree-1 (linear) curve. Degree matters here: + # a degree-3 curve treats these positions as B-spline control + # vertices, which the curve does NOT pass through except at + # the two ends - only a polyline (degree 1) is guaranteed to + # retrace every guide point exactly, matching the discrete + # per-segment look-at orientation add2DChain/getChainTransform + # already used to build the FK chain and self.chain. + ik_nb = self.settings["ikNb"] + self.ik_crv = curve.addCurve( + self.ikcns_grp, self.getName("ik_crv"), + [list(p) for p in self.guide.apos], + False, 1) + self.ik_crv.attr("visibility").set(False) + + # Compute good rest CV positions for an ikNb-CV curve by + # rebuilding a throwaway duplicate of the dense curve (this + # only needs to exist long enough to read its CVs - the real + # wrap curve is built fresh below). + wrap_degree = min(3, ik_nb - 1) + tmp_wrap_crv = pm.duplicate( + self.ik_crv, n=self.getName("ikWrapTmp_crv"))[0] + pm.rebuildCurve( + tmp_wrap_crv, + ch=False, + replaceOriginal=True, + rebuildType=0, + keepRange=0, + keepControlPoints=False, + keepEndPoints=True, + keepTangents=True, + spans=(ik_nb - wrap_degree), + degree=wrap_degree, + tolerance=0.001) + + # IK controls, evenly spaced by arc length along the chain -- + # (independent of the wrap curve's CVs, which are wherever + # rebuildCurve put them, not necessarily evenly spaced). + ik_positions = curve.get_uniform_world_positions_on_curve( + self.ik_crv, ik_nb) + ik_transforms = transform.getChainTransform( + ik_positions, self.normal, self.negate) + ik_transforms.append(transform.setMatrixPosition( + ik_transforms[-1], ik_positions[-1])) + + wrap_shape = tmp_wrap_crv.getShape() + cv_rest_positions = [] + for i in range(ik_nb): + pos = pm.xform("{}.cv[{}]".format(wrap_shape, i), + q=True, ws=True, t=True) + # Guard against whatever pm.xform hands back here (tuple, + # extra/odd-typed elements, etc.) so datatypes.Vector always + # gets exactly 3 plain floats. + cv_rest_positions.append( + [float(pos[0]), float(pos[1]), float(pos[2])]) + pm.delete(tmp_wrap_crv) + + # Guard against duplicate/rebuildCurve flipping the curve's + # parametric direction. If Maya's CV[0] actually landed near + # the tip instead of the root, every control needs to bind to + # the OPPOSITE CV index than a naive 1:1 mapping would assume - + # otherwise the last (tip) control ends up wired to the CV + # nearest the root (which barely needs to move), while the CV + # that should follow the tip stays wired to the first control + # instead. Check empirically which way it goes, and remap the + # control->CV-index correspondence accordingly (NOT just the + # rest-position list - the correspondence used when wiring + # gear_curvecns_op must match too). + cv0_v = datatypes.Vector(*cv_rest_positions[0]) + root_v = datatypes.Vector( + ik_positions[0].x, ik_positions[0].y, ik_positions[0].z) + tip_v = datatypes.Vector( + ik_positions[-1].x, ik_positions[-1].y, ik_positions[-1].z) + first_to_root = (cv0_v - root_v).length() + first_to_tip = (cv0_v - tip_v).length() + cvs_reversed = first_to_tip < first_to_root + + self.ik_ctl = [] + self.ik_npo = [] + cv_drivers = [None] * ik_nb + self.previusTag = self.parentCtlTag + for i, ik_t in enumerate(ik_transforms): + ik_npo = primitive.addTransform( + self.ikcns_grp, self.getName("ik%s_npo" % i), ik_t) + self.ik_npo.append(ik_npo) + + ik_ctl = self.addCtl( + ik_npo, + "ik%s_ctl" % i, + ik_t, + self.color_ik, + "cube", + w=self.size * .15, + h=self.size * .15, + d=self.size * .15, + tp=self.previusTag) + + # IK controls only position the spline curve; axial twist is + # driven by the dedicated "twist" attribute, so expose + # translate only. + attribute.setKeyableAttributes(ik_ctl, self.t_params) + + self.ik_ctl.append(ik_ctl) + self.previusTag = ik_ctl + + # Driver for this CV: parented under the control but + # positioned at the wrap curve's rebuilt rest CV location, + # so the constant offset between an evenly-spaced control + # and its (not evenly-spaced) CV is baked in via ordinary + # DAG parenting - not a skinCluster/wire bind-pose cache. + # A skinCluster's bindPreMatrix is captured once and can + # go stale relative to a component's final position once + # it's parented deep in a character hierarchy, which is + # what was actually causing the double transformation + # (disabling inheritsTransform doesn't touch a deformer's + # internal bind-pose cache, which is why that didn't fix + # it here). A live, ordinary matrix connection has no such + # cache to go stale. + # + # The first/last controls are a special case: arc-length + # sampling already puts them exactly at guide.apos[0]/[-1] + # - the same positions as the real chain's first/last + # joints - so their drivers use that exact position + # directly instead of the rebuilt curve's approximation + # (which only exists to make interior CVs approximate the + # guide shape; the endpoints don't need approximating). + cv_index = (ik_nb - 1 - i) if cvs_reversed else i + if i == 0: + cv_pos = self.guide.apos[0] + elif i == len(ik_transforms) - 1: + cv_pos = self.guide.apos[-1] + else: + cv_pos = datatypes.Vector(*cv_rest_positions[cv_index]) + cv_m = transform.setMatrixPosition(ik_t, cv_pos) + cv_driver = primitive.addTransform( + ik_ctl, self.getName("ikCvDriver%s" % i), cv_m) + cv_drivers[cv_index] = cv_driver + + # The real wrap curve, built FRESH (never touched by + # rebuildCurve) and immediately bound to the CV drivers via + # curve.addCnsCurve - the exact same helper (and pattern: + # fresh curve + immediate gear_curvecns_op) mgear uses + # everywhere else for "curve driven by N controls". Applying + # gear_curvecns_op to a curve that had already been through + # duplicate+rebuildCurve first (the previous approach) isn't + # a pattern used anywhere else in mgear and was the likely + # reason the last CV wasn't responding correctly. + self.ik_wrap_crv = curve.addCnsCurve( + self.ikcns_grp, self.getName("ikWrap_crv"), + cv_drivers, wrap_degree) + self.ik_wrap_crv.attr("visibility").set(False) + + # Twist reference transforms for the ikSplineSolver's advanced + # twist (wired in addOperators). These must be oriented to + # match self.chain's own start/end bind tangent exactly, NOT + # ik_ctl[0]/[-1]'s own orientation - the ikNb controls are + # sampled independently from the dense per-joint chain, so + # their tangent generally differs from self.chain[0]/[-1]'s + # actual orientation. Feeding that mismatched orientation + # straight into dWorldUpMatrix biased the twist calculation, + # which is what caused the extra rotation on the first joint + # once this component was parented under another (its own + # world orientation no longer canceled the mismatch out). + # Building these under ik_ctl[0]/[-1] keeps a baked, zero-error + # offset at rest while still following control rotation. + dense_transforms = transform.getChainTransform( + self.guide.apos, self.normal, self.negate) + dense_transforms.append(transform.setMatrixPosition( + dense_transforms[-1], self.guide.apos[-1])) + + # Parented under the (static) ik_npo, NOT the IK controls, so the + # solver's base twist is independent of user twist. All user twist + # is driven explicitly by the per-control "twist" attribute layer + # (see addOperators), which avoids double-counting at the ends. + self.ik_twist_start_ref = primitive.addTransform( + self.ik_npo[0], self.getName("ikTwistStart_ref"), + dense_transforms[0]) + self.ik_twist_end_ref = primitive.addTransform( + self.ik_npo[-1], self.getName("ikTwistEnd_ref"), + dense_transforms[-1]) + + # A wire deformer relays the wrap curve's shape onto the dense + # curve. Since the wrap curve hasn't moved from its rebuilt + # rest shape yet, the wire's base shape == current shape, so + # this contributes zero deformation at rest. + ik_wire = pm.wire( + self.ik_crv, + w=self.ik_wrap_crv, + n=self.getName("ikWire"), + groupWithBase=False, + envelope=1.0)[0] + + # Without an explicit dropoff distance, Maya's default falls + # short of the curve's full length, so influence (and control) + # quietly cuts off before reaching the tip. Base it on the + # curve's own length so it scales correctly with chain size. + dense_length = pm.arclen(self.ik_crv) + pm.setAttr( + "{}.dropoffDistance[0]".format(ik_wire), + dense_length * 10) + + # Chain (real joints, driven by the spline ikHandle in + # addOperators). Kept identical to the previous rotate-plane + # setup so translation always stays on the chain's local X + # (bone) axis only. + self.chain = primitive.add2DChain(self.root, + self.getName("chain"), + self.guide.apos, + self.normal, + self.negate) + self.chain[0].attr("visibility").set(self.WIP) + + # Twist layer: a twist_ref leaf under each chain joint. The IK + # deform target is this node (not the raw chain joint), so its + # rotateX - driven per IK control in addOperators - adds axial + # twist on top of the solver's curve-following, without moving the + # bone off its primary axis. + self.twist_ref = [] + for i, jnt in enumerate(self.chain): + twist_ref = primitive.addTransform( + jnt, self.getName("%s_twist_ref" % i), + transform.getTransform(jnt)) + self.twist_ref.append(twist_ref) + + # Chain of deformers ------------------------------- + self.loc = [] + parent = self.root + for i, t in enumerate(transform.getChainTransform(self.guide.apos, + self.normal, + self.negate)): + loc = primitive.addTransform(parent, self.getName("%s_loc" % i), t) + + self.loc.append(loc) + # self.jnt_pos.append([loc, i, None, False]) + + jnt_name = "_".join([self.name, str(i + 1).zfill(2)]) + if i: + guide_relative_name = "{}_loc".format(str(i - 1)) + else: + guide_relative_name = "root" + self.jnt_pos.append( + { + "obj": loc, + "name": jnt_name, + "guide_relative": guide_relative_name, + } + ) + + # ===================================================== + # ATTRIBUTES + # ===================================================== + def addAttributes(self): + """Create the anim and setupr rig attributes for the component""" + + # Anim ------------------------------------------- + if self.isFkIk: + self.blend_att = self.addAnimParam( + "blend", "Fk/Ik Blend", "double", self.settings["blend"], 0, 1) + + if self.isIk: + self.roll_att = self.addAnimParam( + "roll", "Roll", "double", 0, -180, 180) + + self.stretch_att = self.addAnimParam( + "stretch", "Stretch", "double", 1, 0, 1) + + # A single "twist" handle on every IK control. Twisting one + # control twists the chain joints nearest it, reaching zero at the + # neighbouring controls (width = "twistFalloff") - so the first + # control twists the base, the last the tip, an interior control + # the middle, each without disturbing the far ends. Wired to the + # twist_ref layer in addOperators (NOT the ikHandle, which would + # give an end-weighted ramp). + self.ik_twist_att = [ + self.addAnimParam( + "twist", "Twist", "double", 0, -360, 360, uihost=ctl) + for ctl in self.ik_ctl] + + # Ref + if self.settings["ikrefarray"]: + ref_names = self.get_valid_alias_list( + self.settings["ikrefarray"].split(",")) + if len(ref_names) > 1: + self.ikref_att = self.addAnimEnumParam( + "ikref", + "Ik Ref", + 0, + ref_names) + + # ===================================================== + # OPERATORS + # ===================================================== + def addOperators(self): + """Create operators and set the relations for the component rig + + Apply operators, constraints, expressions to the hierarchy. + In order to keep the code clean and easier to debug, + we shouldn't create any new object in this method. + + """ + + # Visibilities ------------------------------------- + if self.isFkIk: + # fk + fkvis_node = node.createReverseNode(self.blend_att) + + for fk_ctl in self.fk_ctl: + for shp in fk_ctl.getShapes(): + pm.connectAttr(fkvis_node + ".outputX", + shp.attr("visibility")) + + # ik + for ik_ctl in self.ik_ctl: + for shp in ik_ctl.getShapes(): + pm.connectAttr(self.blend_att, shp.attr("visibility")) + + # FK Chain ----------------------------------------- + if self.isFk: + for off, ref in zip(self.fk_off[1:], self.fk_ref): + applyop.gear_mulmatrix_op( + ref.worldMatrix, off.parentInverseMatrix, off, "rt") + # IK Chain ----------------------------------------- + if self.isIk: + # Real ikSplineSolver handle riding the control-driven curve. + # Using a real joint chain + native spline solver (instead of + # a motion-path/matrix-decompose rig) guarantees translation + # never leaves the chain's local X (bone) axis. + ikh_name = pm.ikHandle( + n=self.getName("ikh"), + sj=self.chain[0], + ee=self.chain[-1], + sol="ikSplineSolver", + ccv=False, + curve=self.ik_crv)[0] + self.ikh = pm.PyNode(ikh_name) + self.root.addChild(self.ikh) + self.ikh.attr("visibility").set(False) + + # Roll + pm.connectAttr(self.roll_att, self.ikh.attr("roll")) + + # Twist is handled per IK control by the twist_ref layer below, + # not the ikHandle (which would give an end-weighted ramp). + + # Advanced twist - first/last IK control orientation drives + # the twist along the length of the chain. Same dWorldUpType/ + # dWorldUpAxis convention as mgear's own EPIC_arm_02/leg_02 + # (also X-forward, Z-up "xz" chains). + self.ikh.attr("dTwistControlEnable").set(True) + self.ikh.attr("dWorldUpType").set(4) # Object Rotation Up (Start/End) + self.ikh.attr("dWorldUpAxis").set(3) + self.ikh.attr("dWorldUpVectorZ").set(1.0) + self.ikh.attr("dWorldUpVectorY").set(0.0) + self.ikh.attr("dWorldUpVectorEndZ").set(1.0) + self.ikh.attr("dWorldUpVectorEndY").set(0.0) + if self.negate: + self.ikh.attr("dForwardAxis").set(1) + + pm.connectAttr(self.ik_twist_start_ref.attr("worldMatrix"), + self.ikh.attr("dWorldUpMatrix")) + pm.connectAttr(self.ik_twist_end_ref.attr("worldMatrix"), + self.ikh.attr("dWorldUpMatrixEnd")) + + # Stretch - single axis only (chain's local X/bone length), + # scaled uniformly by the live curve-length ratio. + curve_info = pm.arclen(self.ik_crv, ch=True) + rest_length = curve_info.attr("arcLength").get() + ratio_node = node.createDivNode( + curve_info.attr("arcLength"), rest_length) + stretch_blend = node.createBlendNode( + ratio_node + ".outputX", 1.0, self.stretch_att) + + for jnt in self.chain[1:]: + rest_tx = jnt.attr("tx").get() + node.createMulNode( + stretch_blend + ".outputR", rest_tx, jnt.attr("tx")) + + # Per-control twist distribution ---------------------------- + # Each chain joint's twist_ref.rx is a sum of every IK control's + # "twist" attribute weighted by an eased TENT (hat) falloff: a + # control's influence is 1 at its own position and drops to + # exactly 0 at its neighbouring controls (radius = twistFalloff x + # control spacing). So the middle control twists only the middle + # and reaches zero at the base and tip controls - the ends do NOT + # move. The weights are additive, and at twistFalloff = 1 they sum + # to 1, so an equal twist on all controls gives a uniform roll. + M = len(self.ik_ctl) + N = len(self.chain) + falloff = max(self.settings["twistFalloff"], 1e-4) + p_ctl = [i / float(M - 1) for i in range(M)] if M > 1 else [0.0] + spacing = 1.0 / (M - 1) if M > 1 else 1.0 + radius = falloff * spacing + + for j in range(N): + p_j = j / float(N - 1) if N > 1 else 0.0 + + if M == 1: + pm.connectAttr(self.ik_twist_att[0], + self.twist_ref[j].attr("rx")) + continue + + pma = pm.createNode( + "plusMinusAverage", n=self.getName("twist%s_pma" % j)) + pma.attr("operation").set(1) # sum + idx = 0 + for i in range(M): + d = abs(p_j - p_ctl[i]) + if d >= radius: + continue + t = d / radius + # eased tent: 1 at the control, 0 at the neighbour control + w = 1.0 - (3.0 * t * t - 2.0 * t * t * t) + if w < 0.001: + continue + mul = node.createMulNode(self.ik_twist_att[i], w) + pm.connectAttr(mul + ".outputX", + pma.attr("input1D[%d]" % idx)) + idx += 1 + + pm.connectAttr(pma.attr("output1D"), + self.twist_ref[j].attr("rx")) + + # Chain of deformers ------------------------------- + for i, loc in enumerate(self.loc): + + if self.settings["mode"] == 0: # fk only + pm.parentConstraint(self.fk_ctl[i], loc, maintainOffset=False) + pm.connectAttr(self.fk_ctl[i] + ".scale", loc + ".scale") + + elif self.settings["mode"] == 1: # ik only + pm.parentConstraint( + self.twist_ref[i], loc, maintainOffset=False) + + elif self.settings["mode"] == 2: # fk/ik + + rev_node = node.createReverseNode(self.blend_att) + + # orientation + cns = pm.parentConstraint( + self.fk_ctl[i], self.twist_ref[i], loc, + maintainOffset=False) + cns.interpType.set(0) + weight_att = pm.parentConstraint( + cns, query=True, weightAliasList=True) + pm.connectAttr(rev_node + ".outputX", cns + "." + weight_att[0]) + pm.connectAttr(self.blend_att, cns + "." + weight_att[1]) + + # scaling + blend_node = pm.createNode("blendColors") + pm.connectAttr(self.chain[i].attr("scale"), + blend_node + ".color1") + pm.connectAttr(self.fk_ctl[i].attr("scale"), + blend_node + ".color2") + pm.connectAttr(self.blend_att, blend_node + ".blender") + pm.connectAttr(blend_node + ".output", loc + ".scale") + + # ===================================================== + # CONNECTOR + # ===================================================== + def setRelation(self): + """Set the relation beetween object from guide to rig""" + + self.relatives["root"] = self.loc[0] + self.jointRelatives["root"] = 0 + + if not self.isIk: + self.controlRelatives["root"] = self.fk_ctl[0] + self.controlRelatives["%s_loc" % (len(self.loc) - 1)] = self.fk_ctl[-1] + else: + self.controlRelatives["root"] = self.ik_ctl[0] + self.controlRelatives["%s_loc" % (len(self.loc) - 1)] = self.ik_ctl[-1] + + for i in range(0, len(self.loc) - 1): + self.relatives["%s_loc" % i] = self.loc[i + 1] + self.jointRelatives["%s_loc" % i] = i + 1 + self.aliasRelatives["%s_ctl" % i] = i + 1 + if not self.isIk: + self.controlRelatives["%s_loc" % i] = self.fk_ctl[i + 1] + else: + # proportionally map each deformer division to the + # nearest IK control (counts are independent: ikNb vs + # number of guide locators). + ik_idx = min( + int(round((i + 1) * (len(self.ik_ctl) - 1) / + float(len(self.loc) - 1))), + len(self.ik_ctl) - 1) + self.controlRelatives["%s_loc" % i] = self.ik_ctl[ik_idx] + + self.relatives["%s_loc" % (len(self.loc) - 1)] = self.loc[-1] + self.jointRelatives["%s_loc" % (len(self.loc) - 1)] = len(self.loc) - 1 + self.aliasRelatives["%s_loc" % (len(self.loc) - 1)] = len(self.loc) - 1 + + # @param self + def addConnection(self): + """Add more connection definition to the set""" + + self.connections["standard"] = self.connect_standard + self.connections["orientation"] = self.connect_orientation + self.connections["parent"] = self.connect_parent + + def connect_orientation(self): + """orientation connection definition for the component""" + self.connect_orientCns() + + def connect_standard(self): + """standard connection definition for the component""" + self.connect_standardWithSimpleIkRef() + + def connect_parent(self): + self.connect_standardWithSimpleIkRef() diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/__init__.cpython-311.pyc b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff511fb8cc9b1c2acf603920554b317eb8963c02 GIT binary patch literal 26689 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 |207sY3z#OFIZ!P$ZkK_!&|JH6 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 W1ti=*jTWtyz2KjHo2Ld(=>HGFlOC}E literal 0 HcmV?d00001 diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/guide.cpython-311.pyc b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/guide.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9be3eaa2f0522157315f3061ef3958f532ee3510 GIT binary patch literal 12348 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 Tc%xT2I`fM5zu5+hs^k9w`WGXQ literal 0 HcmV?d00001 diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/settingsUI.cpython-311.pyc b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/settingsUI.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d01140d684c578d0246e48277f792e673da68177 GIT binary patch literal 14741 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 F{{@>Z%O3y$ literal 0 HcmV?d00001 diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py new file mode 100644 index 00000000..1b27f1f1 --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py @@ -0,0 +1,206 @@ +"""Guide Chain 01 module""" + +from functools import partial +from mgear.shifter.component import guide +from mgear.core import pyqt +from mgear.vendor.Qt import QtWidgets, QtCore +from maya.app.general.mayaMixin import MayaQWidgetDockableMixin +from maya.app.general.mayaMixin import MayaQDockWidget +from . import settingsUI as sui + + +# guide info +AUTHOR = "Vi Cuong Van" +URL = "www.mcsGear.com" +EMAIL = "" +VERSION = [1, 0, 0] +TYPE = "EPIC_chain_spline_IK_FK_01" +NAME = "chain" +DESCRIPTION = ( + "Game ready component for EPIC's UE and other Game Engines\n" + "Simple IK/FK chain, With IK space switch" +) + +########################################################## +# CLASS +########################################################## + + +class Guide(guide.ComponentGuide): + """Component Guide Class""" + + compType = TYPE + compName = NAME + description = DESCRIPTION + + author = AUTHOR + url = URL + email = EMAIL + version = VERSION + + def postInit(self): + """Initialize the position for the guide""" + + self.save_transform = ["root", "#_loc"] + self.save_blade = ["blade"] + self.addMinMax("#_loc", 1, -1) + + def addObjects(self): + """Add the Guide Root, blade and locators""" + + self.root = self.addRoot() + self.locs = self.addLocMulti("#_loc", self.root) + self.blade = self.addBlade("blade", self.root, self.locs[0]) + + centers = [self.root] + centers.extend(self.locs) + self.dispcrv = self.addDispCurve("crv", centers) + + def addParameters(self): + """Add the configurations settings""" + + self.pType = self.addParam("mode", "long", 0, 0) + self.pBlend = self.addParam("blend", "double", 1, 0, 1) + self.pNeutralPose = self.addParam("neutralpose", "bool", True) + self.pIkRefArray = self.addParam("ikrefarray", "string", "") + self.pUseIndex = self.addParam("useIndex", "bool", False) + self.pParentJointIndex = self.addParam( + "parentJointIndex", "long", -1, None, None) + self.pIkNb = self.addParam("ikNb", "long", 3, 2, None) + # Width of each IK control's twist influence, as a fraction of the + # spacing to its neighbouring control. 1.0 = the tent reaches exactly + # to the neighbour controls (base/tip stay put when the middle control + # twists, and equal twist on all controls gives a uniform roll). + # Smaller = tighter / more localized twist. + self.pTwistFalloff = self.addParam( + "twistFalloff", "double", 1.0, 0.05, 2.0) + + +########################################################## +# Setting Page +########################################################## + + +class settingsTab(QtWidgets.QDialog, sui.Ui_Form): + + def __init__(self, parent=None): + super(settingsTab, self).__init__(parent) + self.setupUi(self) + + +class componentSettings(MayaQWidgetDockableMixin, guide.componentMainSettings): + + def __init__(self, parent=None): + self.toolName = TYPE + # Delete old instances of the componet settings window. + pyqt.deleteInstances(self, MayaQDockWidget) + + super(componentSettings, self).__init__(parent=parent) + self.settingsTab = settingsTab() + + self.setup_componentSettingWindow() + self.create_componentControls() + self.populate_componentControls() + self.create_componentLayout() + self.create_componentConnections() + + def setup_componentSettingWindow(self): + self.mayaMainWindow = pyqt.maya_main_window() + + self.setObjectName(self.toolName) + self.setWindowFlags(QtCore.Qt.Window) + self.setWindowTitle(TYPE) + self.resize(350, 350) + + def create_componentControls(self): + return + + def populate_componentControls(self): + """Populate Controls + + Populate the controls values from the custom attributes of the + component. + + """ + # populate tab + self.tabs.insertTab(1, self.settingsTab, "Component Settings") + + # populate component settings + self.settingsTab.ikfk_slider.setValue( + int(self.root.attr("blend").get() * 100)) + self.settingsTab.ikfk_spinBox.setValue( + int(self.root.attr("blend").get() * 100)) + self.settingsTab.mode_comboBox.setCurrentIndex( + self.root.attr("mode").get()) + + if self.root.attr("neutralpose").get(): + self.settingsTab.neutralPose_checkBox.setCheckState( + QtCore.Qt.Checked) + else: + self.settingsTab.neutralPose_checkBox.setCheckState( + QtCore.Qt.Unchecked) + + ikRefArrayItems = self.root.attr("ikrefarray").get().split(",") + for item in ikRefArrayItems: + self.settingsTab.ikRefArray_listWidget.addItem(item) + + self.settingsTab.ikNb_spinBox.setValue( + self.root.attr("ikNb").get()) + self.settingsTab.twistFalloff_spinBox.setValue( + self.root.attr("twistFalloff").get()) + + def create_componentLayout(self): + + self.settings_layout = QtWidgets.QVBoxLayout() + self.settings_layout.addWidget(self.tabs) + self.settings_layout.addWidget(self.close_button) + + self.setLayout(self.settings_layout) + + def create_componentConnections(self): + + self.settingsTab.ikfk_slider.valueChanged.connect( + partial(self.updateSlider, self.settingsTab.ikfk_slider, "blend")) + self.settingsTab.ikfk_spinBox.valueChanged.connect( + partial(self.updateSlider, self.settingsTab.ikfk_spinBox, "blend")) + + self.settingsTab.mode_comboBox.currentIndexChanged.connect( + partial(self.updateComboBox, + self.settingsTab.mode_comboBox, + "mode")) + + self.settingsTab.neutralPose_checkBox.stateChanged.connect( + partial(self.updateCheck, + self.settingsTab.neutralPose_checkBox, + "neutralpose")) + + self.settingsTab.ikRefArrayAdd_pushButton.clicked.connect( + partial(self.addItem2listWidget, + self.settingsTab.ikRefArray_listWidget, + "ikrefarray")) + self.settingsTab.ikRefArrayRemove_pushButton.clicked.connect( + partial(self.removeSelectedFromListWidget, + self.settingsTab.ikRefArray_listWidget, + "ikrefarray")) + self.settingsTab.ikRefArray_listWidget.installEventFilter(self) + + self.settingsTab.ikNb_spinBox.valueChanged.connect( + partial(self.updateSpinBox, + self.settingsTab.ikNb_spinBox, + "ikNb")) + + self.settingsTab.twistFalloff_spinBox.valueChanged.connect( + partial(self.updateSpinBox, + self.settingsTab.twistFalloff_spinBox, + "twistFalloff")) + + def eventFilter(self, sender, event): + if event.type() == QtCore.QEvent.ChildRemoved: + if sender == self.settingsTab.ikRefArray_listWidget: + self.updateListAttr(sender, "ikrefarray") + return True + else: + return QtWidgets.QDialog.eventFilter(self, sender, event) + + def dockCloseEventTriggered(self): + pyqt.deleteInstances(self, MayaQDockWidget) diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1329f178489c2f8cdd0148f8bde7c8621c02e137 GIT binary patch literal 2083 zcma)&c~H^`8^?bL2)OHkXPTPgm3MfRR^~y97b+^HXpRS&x0s}+uX$xu8F{}cF!{>J4% zAXzHgAHhF8q+9?A2dn{WFbE0AAwggyNID2q%KAaTAX&{Hg2E6AUg}8($GYp-R;za$qoHQG!IW>6D_3dU(mk*DD-at zDF>*^7(=wYJ1RJ)NR3QuuFZK(d#Lkbs<8*}=ti`~`xX(GoUb2?ba|?> zl}~bnS6$pJiH}leuImMEvwkcEC>;2`t~;dF?fuQrf^Rk{wn@kHEAEqTV>P;ec|fEf zHC*WBcKh6v3q$be-`UW)EW>qm&jB}X}an6fQQL`19HkrdSizBhsK|Q zL+4C}!iB56n^wA#h>5Ivc5$fEt9r1v<*{W=oa)y_DXh8g0;u+4a?MFy4M0SM8r^^)TR_Fa3DX zpu%Y=M*qt~qixsJrEb815p_{nv$##a9;sN}awk)sa1|gtF`$uGt~!TzD&9~dWJoi)KEy( z{C&5kW^%9D3yD}qxPpPS1#xD=RV2EvlOtY6O&|`O?yCvEB_?_qoO8%38r#u*gbZ@V zQbCDq`J&dFH|@gToo|Zs=QR)E-WV8#A;d<%ei~-_5x(>=N8y5t0g8*0Za* z7G&WGlvRzO^o&d|z+;@f&tD;>?Osfw=;HMWABMHEjM1Ub1#80@Z391tiIHxQvsR&) z0T5e3&P27Q43@9>I>p{;x?KuHxvU)hZE#n>`oZ)^#M<7}yu+IxuFboeH%(b2ig>lw zt*{Y_>I2)KQizrQ#9Dj=F5I4#o`&n9rK~#Vc3DC3d#edaoc-rAi-LkP8NsM{=d*8Z z63(_)-nXVoEJHGKz~MFS^3iI|MuQLqY^A5$_Vyo3 z`DW?-b8SA~IOpa?pK2A}2KYr2?!$+-QxR-YoijavLj^f|a^v7<3c<}~y=xmdeD_hW zitcPeG%JK`Bm~B7w#{0Zk7*+zXuA%9gr?%yx zC~DoKg_D*^Z{3-1^|(%I-C=wbK<0;N4a)NkTsluwh%BBjwx^~?B=Sg|N-**)?>Plq zw&qu!z2#BT=QicImf~@zy=~uO?3GuOD`)bmW_lD7dK=S7et~E~=7lE_7(SI+Awt-7 zu+Ql;Qa_p+aZr47}JHnB6v83Wn3W2z&l^)LmYQZhQ1^xksEA$x6${>r1jv^`(o zogkY|?dT%RuGicEw`}jl_MMM#3SNNSYoQdG;Py_SNd66ztek1ryF&t}hoY<99EYUp z$g%n6P^H3?TP+M!_AIG3?f>9b@>)a%q0sK?Z^42y_s^)9H=xhJ|r zN!f>VoVQRPW0s?D_MNC8*8QA)1Nz5P?YZXr;!V$`(QoZNOH#mlhTWdqINFBe)Ph%yME@;JzvW{PLNMJu4ZM1G_fWxg++6irE}cDQ()L3nkH*!cA^I( zdA+XbeRIA@3S3R28PPY)r*s(^-l2({jlCt3sJ^}eX2GUo?cqZ+9*nq0TgMr5D!1pN zxpmu?qOuAa9~%X}84&7+@Rgn>_XI9q-xrXW&A!VV>PHWkmo0 literal 0 HcmV?d00001 diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py new file mode 100644 index 00000000..bd50ec55 --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py @@ -0,0 +1,167 @@ +# MGEAR is under the terms of the MIT License + +# Copyright (c) 2016 Jeremie Passerin, Miquel Campos + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Author: Jeremie Passerin +# Author: Miquel Campos www.mcsgear.com +# Date: 2016 / 10 / 10 + +import mgear.core.pyqt as gqt +QtGui, QtCore, QtWidgets, wrapInstance = gqt.qt_import() + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(255, 290) + self.gridLayout = QtWidgets.QGridLayout(Form) + self.gridLayout.setObjectName("gridLayout") + self.groupBox = QtWidgets.QGroupBox(Form) + self.groupBox.setTitle("") + self.groupBox.setObjectName("groupBox") + self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox) + self.gridLayout_2.setObjectName("gridLayout_2") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.formLayout = QtWidgets.QFormLayout() + self.formLayout.setObjectName("formLayout") + self.mode_label = QtWidgets.QLabel(self.groupBox) + self.mode_label.setObjectName("mode_label") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.mode_label) + self.mode_comboBox = QtWidgets.QComboBox(self.groupBox) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mode_comboBox.sizePolicy().hasHeightForWidth()) + self.mode_comboBox.setSizePolicy(sizePolicy) + self.mode_comboBox.setObjectName("mode_comboBox") + self.mode_comboBox.addItem("") + self.mode_comboBox.addItem("") + self.mode_comboBox.addItem("") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.mode_comboBox) + self.ikfk_label = QtWidgets.QLabel(self.groupBox) + self.ikfk_label.setObjectName("ikfk_label") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.ikfk_label) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.ikfk_slider = QtWidgets.QSlider(self.groupBox) + self.ikfk_slider.setMinimumSize(QtCore.QSize(0, 15)) + self.ikfk_slider.setMaximum(100) + self.ikfk_slider.setOrientation(QtCore.Qt.Horizontal) + self.ikfk_slider.setObjectName("ikfk_slider") + self.horizontalLayout_3.addWidget(self.ikfk_slider) + self.ikfk_spinBox = QtWidgets.QSpinBox(self.groupBox) + self.ikfk_spinBox.setMaximum(100) + self.ikfk_spinBox.setObjectName("ikfk_spinBox") + self.horizontalLayout_3.addWidget(self.ikfk_spinBox) + self.formLayout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3) + self.verticalLayout.addLayout(self.formLayout) + self.neutralPose_checkBox = QtWidgets.QCheckBox(self.groupBox) + self.neutralPose_checkBox.setObjectName("neutralPose_checkBox") + self.verticalLayout.addWidget(self.neutralPose_checkBox) + self.gridLayout_2.addLayout(self.verticalLayout, 0, 0, 1, 1) + self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1) + self.ikRefArray_groupBox = QtWidgets.QGroupBox(Form) + self.ikRefArray_groupBox.setObjectName("ikRefArray_groupBox") + self.gridLayout_3 = QtWidgets.QGridLayout(self.ikRefArray_groupBox) + self.gridLayout_3.setObjectName("gridLayout_3") + self.ikRefArray_horizontalLayout = QtWidgets.QHBoxLayout() + self.ikRefArray_horizontalLayout.setObjectName("ikRefArray_horizontalLayout") + self.ikRefArray_verticalLayout_1 = QtWidgets.QVBoxLayout() + self.ikRefArray_verticalLayout_1.setObjectName("ikRefArray_verticalLayout_1") + self.ikRefArray_listWidget = QtWidgets.QListWidget(self.ikRefArray_groupBox) + self.ikRefArray_listWidget.setDragDropOverwriteMode(True) + self.ikRefArray_listWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) + self.ikRefArray_listWidget.setDefaultDropAction(QtCore.Qt.MoveAction) + self.ikRefArray_listWidget.setAlternatingRowColors(True) + self.ikRefArray_listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.ikRefArray_listWidget.setSelectionRectVisible(False) + self.ikRefArray_listWidget.setObjectName("ikRefArray_listWidget") + self.ikRefArray_verticalLayout_1.addWidget(self.ikRefArray_listWidget) + self.ikRefArray_horizontalLayout.addLayout(self.ikRefArray_verticalLayout_1) + self.ikRefArray_verticalLayout_2 = QtWidgets.QVBoxLayout() + self.ikRefArray_verticalLayout_2.setObjectName("ikRefArray_verticalLayout_2") + self.ikRefArrayAdd_pushButton = QtWidgets.QPushButton(self.ikRefArray_groupBox) + self.ikRefArrayAdd_pushButton.setObjectName("ikRefArrayAdd_pushButton") + self.ikRefArray_verticalLayout_2.addWidget(self.ikRefArrayAdd_pushButton) + self.ikRefArrayRemove_pushButton = QtWidgets.QPushButton(self.ikRefArray_groupBox) + self.ikRefArrayRemove_pushButton.setObjectName("ikRefArrayRemove_pushButton") + self.ikRefArray_verticalLayout_2.addWidget(self.ikRefArrayRemove_pushButton) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.ikRefArray_verticalLayout_2.addItem(spacerItem) + self.ikRefArray_horizontalLayout.addLayout(self.ikRefArray_verticalLayout_2) + self.gridLayout_3.addLayout(self.ikRefArray_horizontalLayout, 0, 0, 1, 1) + self.gridLayout.addWidget(self.ikRefArray_groupBox, 1, 0, 1, 1) + + self.splineIk_groupBox = QtWidgets.QGroupBox(Form) + self.splineIk_groupBox.setObjectName("splineIk_groupBox") + self.gridLayout_4 = QtWidgets.QGridLayout(self.splineIk_groupBox) + self.gridLayout_4.setObjectName("gridLayout_4") + self.ikNb_horizontalLayout = QtWidgets.QHBoxLayout() + self.ikNb_horizontalLayout.setObjectName("ikNb_horizontalLayout") + self.ikNb_label = QtWidgets.QLabel(self.splineIk_groupBox) + self.ikNb_label.setObjectName("ikNb_label") + self.ikNb_horizontalLayout.addWidget(self.ikNb_label) + self.ikNb_spinBox = QtWidgets.QSpinBox(self.splineIk_groupBox) + self.ikNb_spinBox.setMinimum(2) + self.ikNb_spinBox.setMaximum(999) + self.ikNb_spinBox.setProperty("value", 3) + self.ikNb_spinBox.setObjectName("ikNb_spinBox") + self.ikNb_horizontalLayout.addWidget(self.ikNb_spinBox) + self.gridLayout_4.addLayout(self.ikNb_horizontalLayout, 0, 0, 1, 1) + self.twistFalloff_horizontalLayout = QtWidgets.QHBoxLayout() + self.twistFalloff_horizontalLayout.setObjectName( + "twistFalloff_horizontalLayout") + self.twistFalloff_label = QtWidgets.QLabel(self.splineIk_groupBox) + self.twistFalloff_label.setObjectName("twistFalloff_label") + self.twistFalloff_horizontalLayout.addWidget(self.twistFalloff_label) + self.twistFalloff_spinBox = QtWidgets.QDoubleSpinBox( + self.splineIk_groupBox) + self.twistFalloff_spinBox.setMinimum(0.05) + self.twistFalloff_spinBox.setMaximum(2.0) + self.twistFalloff_spinBox.setSingleStep(0.05) + self.twistFalloff_spinBox.setProperty("value", 1.0) + self.twistFalloff_spinBox.setObjectName("twistFalloff_spinBox") + self.twistFalloff_horizontalLayout.addWidget( + self.twistFalloff_spinBox) + self.gridLayout_4.addLayout( + self.twistFalloff_horizontalLayout, 1, 0, 1, 1) + self.gridLayout.addWidget(self.splineIk_groupBox, 2, 0, 1, 1) + + self.retranslateUi(Form) + QtCore.QObject.connect(self.ikfk_slider, QtCore.SIGNAL("sliderMoved(int)"), self.ikfk_spinBox.setValue) + QtCore.QObject.connect(self.ikfk_spinBox, QtCore.SIGNAL("valueChanged(int)"), self.ikfk_slider.setValue) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + Form.setWindowTitle(gqt.fakeTranslate("Form", "Form", None, -1)) + self.mode_label.setText(gqt.fakeTranslate("Form", "Mode:", None, -1)) + self.mode_comboBox.setItemText(0, gqt.fakeTranslate("Form", "FK", None, -1)) + self.mode_comboBox.setItemText(1, gqt.fakeTranslate("Form", "IK", None, -1)) + self.mode_comboBox.setItemText(2, gqt.fakeTranslate("Form", "FK/IK", None, -1)) + self.ikfk_label.setText(gqt.fakeTranslate("Form", "IK/FK Blend:", None, -1)) + self.neutralPose_checkBox.setText(gqt.fakeTranslate("Form", "Neutral pose", None, -1)) + self.ikRefArray_groupBox.setTitle(gqt.fakeTranslate("Form", "IK Reference Array", None, -1)) + self.ikRefArrayAdd_pushButton.setText(gqt.fakeTranslate("Form", "<<", None, -1)) + self.ikRefArrayRemove_pushButton.setText(gqt.fakeTranslate("Form", ">>", None, -1)) + self.splineIk_groupBox.setTitle(gqt.fakeTranslate("Form", "Spline IK", None, -1)) + self.ikNb_label.setText(gqt.fakeTranslate("Form", "IK Controls:", None, -1)) + self.twistFalloff_label.setText(gqt.fakeTranslate("Form", "Twist Falloff", None, -1)) + diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui new file mode 100644 index 00000000..05759189 --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui @@ -0,0 +1,247 @@ + + + Form + + + + 0 + 0 + 255 + 290 + + + + Form + + + + + + + + + + + + + + + + Mode: + + + + + + + + 0 + 0 + + + + + FK + + + + + IK + + + + + FK/IK + + + + + + + + IK/FK Blend: + + + + + + + + + + 0 + 15 + + + + 100 + + + Qt::Horizontal + + + + + + + 100 + + + + + + + + + + + Nuetral pose + + + + + + + + + + + + IK Reference Array + + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + false + + + + + + + + + + + << + + + + + + + >> + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + Spline IK + + + + + + + + IK Controls: + + + + + + + 2 + + + 999 + + + 3 + + + + + + + + + + + + + + ikfk_slider + sliderMoved(int) + ikfk_spinBox + setValue(int) + + + 144 + 47 + + + 220 + 45 + + + + + ikfk_spinBox + valueChanged(int) + ikfk_slider + setValue(int) + + + 222 + 37 + + + 182 + 37 + + + + + From 3b0d8785f0d974c60fed1086dad7bd108e673947 Mon Sep 17 00:00:00 2001 From: sketchaholic Date: Tue, 14 Jul 2026 09:45:22 -0700 Subject: [PATCH 2/4] Delete release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__ directory --- .../__pycache__/__init__.cpython-311.pyc | Bin 26689 -> 0 bytes .../__pycache__/guide.cpython-311.pyc | Bin 12348 -> 0 bytes .../__pycache__/settingsUI.cpython-311.pyc | Bin 14741 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/__init__.cpython-311.pyc delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/guide.cpython-311.pyc delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/settingsUI.cpython-311.pyc diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/__init__.cpython-311.pyc b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index ff511fb8cc9b1c2acf603920554b317eb8963c02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26689 zcmb__TWlLwmS9nQh>|F&C{YqcQX(npMZIM`EX$H@S+e|=pWW$p;#SitTBaVPT%`O^ z$V>*?jZtQe89GpJ=s=Ayfjgl)Y78@z*--`o){E(n?Bj!_EkHy82810LgY5puNQ|9r zEOz&tDps+I5+_bi%GBkpTlb!O?m6e4d+VHY=}$^aiwU?I-+sjjK1f?p8VG?W@rEv11}M`^qY74lvteRdqVOxU)`+d4ohTRBt4=JZ)Y-l$%{&hq>K2O6^VZc$b@ks_S2xd( z0^khcnd_i`MdPKm6Jgh#Z1emsk#H;7O9yHdqnnc_~8O9h>f1N=o5W=9@TgZu?aZ$O=xG&yxWs(8gen&xgjGNdX{J(S7$v7stVbCI4VhaK zIxE%yX>JztVI5VGsox@&;XS3Lv`WmN4{1(rRDKM4>r+X0@v~RbG3DrS4DNDHVX*CJvuF(>I6>N{sX zrOD3aS&%!~YL|s#%GSwKqaa&jwm`|7h(R%n-6EQKAbqdC!Un1e;AHhDNXMtrDhgUs zu9LI+17%c1LryK(C~~yXzh?ERR`PtG)u&BFCeOSj=A;^VXK>AT_R=o)0;A9Ba&wk* zkDO%NXg;7>*11XV<7K34DzWx{I~?@2!Fz*A{5*k5X%*~mMU?G zkct_S-L;U=O0HY7GYm?zZx9h<*tox@Q^3v6JR>EWV_|sz$V#6QGbQa*M7TUYD2_<} zt(2um#Vir23)_Uj7}f2y2zt0Z-uHhU;V4y?-k0 z;3KARWgcoQXW@lSa?UHU3n}Z#bo0MhPbZ_IVy8Q+R<=_S58hUcUD`#4%X4eyS(7W} z*#89)smfE2l-HY+Xaqc$tx0nuB3*LIrJZNaT2h_2H<=_UdXtySdy|*v>#ZhS6|Tnl z79SPylz->Qo*8P_ymIZ@aBbK;&x4Ct_&22^B5eNdsCY*e&#JnCSR1j2LF)@|<#Z+VA|fE)ugGRn#Ni(eYeVly)red< z5Z1TvfGC?40MV3ZwpE7hS&aiaz>!DW+rkbdy&}#$@&^>jv#)5~SL&QOQr8e}2wTH; zoZzs3zOPsbPlz-DDt@<=yOrDmsAWnm54@X{L6q`8@a{y~Yk(|f*X4cX0LJwyYqcxv z+FwHRaL*-Qo%2YbOkR|nR#Fl6CN7a?Fdn(}M$#4Xbs?`bv?<|+UGi2+nE*bW6#IOI z-en`n+k5+--es-!^nOa}UA~7rEtUdBrJXJ0B{|C5dxx@j*b#PabSZbva%w~x6)XLj z{rXixj5H})oE2M8-Ffy!jmq}nCaMQDu~HIpcz;fW3S+L!=JU+{-kcu3pZo(Z*|$NE zhx6I~=A^uwX8)Qw4C5&g;VZFL5&ViiB|eo~4WX5UiLw>!RIg`N?dCl4+niTc4(z8j z74!Uph)uXz*{@QLBQ1cTg!%sZBiy2d0K3ee@gu${^&#VRq(DA?1x`PRo>SriRNRtm zru*xl{MNR<{ivAx{vK%s3P~f)L!nkBR^Yekk6#k0BmV|im&Nv_?Yk@^Z@m3IVgKf2$8rXkU@97nGB+B;6yGWbAfGUi%0p>pvamYu7ushsF z4X*EfEBSA{x8~qV-xx(XJPQks&@HbcaAOX9NkNC->!rMuBNTAV&|Xi-%RiKSI9;6f z#)60Pe*OR8;xyp5@_vn`ej0c6Ut_{)=s+OE>2A(X&-jCrQ(TGPy9^$wg{45yyQ052 z-xQpty*D$tz)dmdUkYT(W`W@h<@clP~9cztnnRv7BeQMZ;;-Q`rbI2FYeECslN z(9+lc7cnWgcc0Vw_%693-#qj@Jxecf8sGfX4dDTON8xz_ZAG9){H6nv_jsLg-3T@; z98Bf{*;8=xVZnonE5s85d~%uvUohk@;0n5WIMq@R$L(mcEdth7pQu`UlqT@WbUHL?(x@hk+7Kt`M2=yWv~#h3!_gw!PW+?$4RdnC37+@43>>IoJgAVS1 zubgUnMfYX|voL5iOfGQUdKSH0+3d29@{mug|pt!6rf61;hka0Lj!-uLkm|uvt02K?Y-k$4r0E@ z>tBXipdx5|t>cW)j(2EF@HmYJCOr(tJBzgnu~rGx5-G_UVNB;e;J3baF%Sedb->S6 z!E^Cuk`62ik2xK9yuE%3z9|ZD;q)_sg#bN$6X+21t$1l({&hd*z2ThRKYi1KesQi6 zpz-LS@}V-1u?q>ubAiA-P<1Rsquq>lV1hdfPLDmI%bae&TMhcxIW-{6sTX~I&_3W@ zrQYWP-w_k(0It_3^jc7G&(Xk6JUAP zd*5?o!8;~K1q3;LXc~CwSp*pYFkA_a&mR;JE&)JyL3QwRsRC{Ufd+xk%qEy$nKIx4 zFH4%cFq?oM^bA5Pgf6i!t{A``nBe7r#$7fj0 |207sY3z#OFIZ!P$ZkK_!&|JH6 z)3>m|70&qq+d$CW4*c~l+@#SY#OVZCP64Fz)#%8(G65QsT_4W7`i zWAYPBaP!4!kyJOh5`L5!9c2iCrj&*a$|cJs)TPrQabke3;j0CCZW(9p00m&aOFZS& zkOh5kr)T;Or^EFRzO^CW5`2~3^wCf^6qx4MMT2w;k2%B49b64T$Ur=~VgO9>+6^Z; z_W!4d|J#Va9-jI9l=mK|gB*6Jfp3sOsSQU`UVqRl$GQ-J@N6;&k22z;2I|s(fuN&2 zXe2hToGO*di7yP|TfF1dFB&~&yclHGxfRoXs~=j{fhm!rG~-U3-0XGF@q2ok&;J zLax4nt?!0M7oM4&zj3n-qnp&1b6*D1M~;6v&nGXm1+KHIZwX9a5Cyg8Fc0Z2(N^ky zfyqNnR2A)gsUytwY>hk7xY4%Jz3F7zM$&CvLVnZpMHAb03~(_Yhvt|@H8}9mcpY2W zy-R3~Bgqi|mA2ZV6VZw1)<)Jk2!sZ-So6V66=NP|&BK6Q<4?sUKyl;qKOFBRN8Ds7T213z`LUn!pw*(~7$z44GbUM3jYThz6 z$L}zvF4okQ;y*ix-m}^UwzfNMvcx7nIQRa!geB>DQqS0iSliHMBMj1V3V&$Ny` z?S1NDYEH2=r_vz2f3jOeSejlC#g^7@F^TGS&4jUPtz?^Q*diO^gACcml5Hun4Mpdb zOKZ$gO}e2qs#z<4SS|_dvbw1Ed6ku|>gIu^p&!edJ#J);uK3l**B(zNFR@JrQ{<5N zq=Gdz#;M11j|0gYZ1bTMISf3g2JS+#I{8vh7%eQ>oz#3@{CWA)0=8#7-P14255g^$ zl)Na?7)QS)AXy!ISi zVa!$wjM-|zjzRKDjStRl8*Ez!TkH+S(8e0twhdidhOWdiV;Eo!11ZD6O9Nr8k9p!% z3FC*gjHR8mw6C6hX02Nv;YlA0eGq;>oVdW)M_BvFu3BX~@`8W_aJC&uA36Fhl#HoX z&pkJkJ-Co2ZJ-U(j>fdDE^V=<8(Kgmr0bmN8e8m5rlu`j(~!2KG6}D=Iz?9t=cZ8wui0lSv`|> zG^7l5&#El3froFby|GIamX3p3!ZfOSR&8Fp6?=2r-n(V*O}1@%82d5Sek|>5N;D=L zfhaXY&_fL(RWlS-?}FeoY?G~9WGe#fi``xye0Y8B`fpAoTnW!c^M~gjpa0u|&C1P7 zUsiuM`uS+eGzO>|$8n-V3Mfh`lJ#_SfibvPgDYily#xuVXHAW%W>8~n^Ki;^7z721 zs+y2Om)6RK;j11WWnKNrg{Qrr2iU=r0KWP(f`@c<^0}!FKw$mLEa_lj@}5gpu#Np} z1B546gzYVnpB~B#&q?>PF+L%$B%xtZR^{z^)a>q);6$uK5cWxPdqxmZSCE% z_A=H!*4npvE^Tc|bZ%J>uAbY;S#s)Q)$yiB^+4>$Ul&^L5(i6rUtsdE5SD;` zQ7@X;rs5Nyocrip(z5CKte$B+#x@>%%4^0`h@RyXXvUp=Rn9(2&@5AepaESE1A+!r zWO!{?$!ohJ)OL_WbuVqaHt0&+Nrpa;FyslAJdq+#?B8=nqwRJP-G@`wvDk_D*rU@4 z)uZ#V^G{V#eY(;VQ@uMF9fY~Der@Q{Bg&n%V9%sk*b#NnU{;NcL}5Gd0K9nqyJTGn_aFvMWpd zrjv0WW^5y@ZDf-I{?<((0U?bR?X>hni(^9!*$AJP)r6yowex!bQTM#TIEGoraGJEl z&Vqh(xIw=;+^Z9;!3J8p)Ck|(2IrQ+8Sh{W-K?Q|+t9aV=u4hp48yEpIAs`qSx-3J ztQ|n$ZlgSIdTdQl8}opN12v%IaLQoYK`5>*gX=e^*~X)3(h;v%^9UNChE*o=TXLSjCCACSVop~0q!kLfO|_5;_iZ_ys3+Am+m_7R|~&g*gVK|jk8_jaJwZD0n ztG_aSvB316WqZ$lb&09Cz*byX9p8b?@~OYP@aGp|KNI)#B>~GJC3Ft$C#Qiwq|VgRJxqpCDf&z>E^Ca0v`qb zc4pIwJJ!$UKA&Tn$Jpkv6zPJ`PICvOY3TCYw!yw-uSZoGDnO~B zF539`OCF9+X&^XK2aXT;6Rvk;0qM|63dNwm%G-mEDx}l^Ne^LDxviG@pkG{kolD=W2vr> zsNN}vL&@HY;!n5m0;LVBHgq2Y!#|an4mNB%oYW`;>jau*IPF#ZqQ3w*JfglBnA3f? zU@Cx>Tes}H6#zT8ibFHu7!c(m8j##-x*6(oYOvVSzk*y4=as|pHvj5RyWwV~UGdJJ zwL?iH@i;?`lC8Zj;U}(wR0{W3R<;fu`x-tbBT9R}Fhf zSnG^uaIJ=PSFLEyYXP(qgH^~w)1Lc(P<5as2yRz&z^KdKn^W#G8a*lWKVX91Ag+jK z<}@-KJwBWs*@m}VNrqMYv$ydX8jS=47d|(k-S3WP!ADI);sHxAPvan12}ATRu|An0 zO8X>$fa$MxsT}E32dTA0Amd5+B>DY)Vx&rJLN_O~YGF!)dcU zZFZ#1wP~{x*8gVMIzSSgSeuH%zujIKG(Ym)R_K%O7#H1U0I_#nL2bydD>0bkf96yk zVxs>hgwHZpb`1gdom8Hy`{0?hYJfv^B2o}8h)e5$2n)YIhQCjx0;t;=QHLZQ2caL* zv=6H&6*@G->bRuirG}89a-5dAK?M)G9-IKYB+U*ET*Ur`-RD7b{`oxk!1805H^0UG*d_)C6lOHr=RuG$d10*6tu!HgRv%6*0$ zMx;=Io3svx3zc#T0Uqd~<1c_Mbrwf&-JT0-NVS!4_@F-N*{exsTncZOcSP+uR-zb5 zb}q<{Fv#)B)3-s1uToB43HzL&=seix?1)|oa!wd=9>}C}1g``+C#*FO^wTtyjXiO4%k4gxJMqye+1q^czmgLT}yqKWd_J_s9ro{bU#Ke*597B_(U~#H!qfXf>q7UoyP%0;2 zIqno8%DeWXaSt3dxWf4yoSa6l5?W@QIXixu!(61-wz*Fc$Ibd6$ZPRJS@QK8gh71{*IGUXWnqFt4?@tcl|;ix+1oPH*Ke~ zpWOF&2oS5DRYgb{96e>iI}CSUffq`@iZa1rqVh0*Kr~VzhYkVQ@a~X3zRe$KNJJ{( z5R`~jRk$*QuS}T>pCDvqyG%dT5x(b4ApFt1vlyMs{3YLu01q%ai@(B^S(@dMrkns- zC0&8PUPbSnB4jx9kS`p*Ib6B-Nb*LD5^|&}T$Kl-oIu1^)w&SiS1sqsUXEXXFH#e_ zE{*`C%M>QVHCYPnbmkeUb{$;oPvt}L&q!p!VC ztr%sm2ou^#?~`&F3EQaRjqY46sDP2>NFEpvf)3pPB>p>>0}p)`k5OOipra_D=s zbWn$|cD@-HEqKsCX~5t+>`}wAo-uJlU@L@e2I!z@O;7=>8aP_V<{y$3O(!5{{sVh}lomf!M0fAq|)`&|-@(|$*Qrr_6s zpbCZ&XYA>e*8$;#9zTzm*As+>Sc8HP<=GCY@m;SYcq_2HK*6t|h*9bu|9uEuznckT zcfijog>LzRjzw?iR)FftBEO>V3M_|~mP0?GanHdU=BKy<8p1k#^S1ll>U2cQ%UjW^i0(B2|q?)UgZqb zYZwChqa_Tn{u-q!TQqjh7o<_uaphtyAxiZceSDwNpUy%de{mJc`X~Ir(X0IU)A6AaqZn;bqRi8^0&v5COy$ z&vuU6{^x%mv>a7UP`}hS>g^HXfsK z>0c@F`9UIkCth!&u5)@pK6@u`|^Cmc?R&-rKI5*&>k*IJ6L3?}I zOW)_p?|OZ+w?bp+cQ^-bhd-#gxDwuzF^-|q7@&`>RtmfL(CJ<=a{#!>SM$ z6MQ1LTjY$wIgrajl523Mwd}h2czWGz&nuGEG z453V7f^Wc_?&5tOITlMySi*;J--5FZG>v8d2@?vFRZQN&Wk-`{3TV%8+c&hvmC(r8%cZ1*zD!w@n$2_L}NcNv%D|PVJk`NNC=0o|xS;WCn zid#YiZ!sTQ4w1q)19-9n0*_$;7&2ZA8s8V-;c%s*8;j>V{Wl1ZK zA!f=r#0*7eRnKedAh;C59?O{8qipTb)iXPm`t^1UPym-1BvD=3>fE-rZ&}+JYbR^% z1V@??y=lh7(eY=dY6!j>P8Kt+;k2vylcA4>66YA#A=Y&WoKRI5hf@W0r9_QQiwUb8 zdnhP93vn2j?$d|Lhz~Ipd+C3CB-!_OLw`NAd5-Bj&GwyUOlMfrnH2wl2s>=RyZXRk zr!G3JdSy=}wz!)-J@a zuuUVIGf(>gev zl7?V9Q!NIP&F_}#%&r#%BoMb@c7b=VvFTeVYhFDC5lR5e)dALUAZ0iJ5iO!T1O-TGIZzwO-AF>OcLwxf*U7;89|G925jz(5}!PRi2F7^DnVeVD$abl=~249cHcQbCn01Nez3ptpi)uf#f1%onWmKvW6p!b(FP^ zV#A{_PfCv>PDeqaEVlL9c>m+W$^KL=#7GUXwL=gQX=(hR`~7Z+3$_fhmceby@Rntm zv5c^m5eUY`N!$*Rckh+ITOKcA$PSk5NMPU`Ms-25dgi&!!P>wrZHZlD$d*K9f?~Ul zK!rL!cpTDIbYGioYb$YQydmDO_7={56nadf>a?ZqgVy(3z#=*)^c#u#8e+)tBm%@fiV1Tw!{PF{L)Z{r?h z8PgEUZ(P1oKV4ouk#1>C^nSD)UyjbiF2#+He)MoLx|nnUoGM%FE>qPEVXhE++xze* zYd_f~tfiwbFo~+tZS4uqZ<`WLQO~cN;;Q)AhuTLS>m5vWbE>*Ic_pge5o0q2?bnZC zw3k@d!L+LdRFo5Lw_S&}To8-KxFE*%DA1s448sZ_1s+4XW3oMmfZ@fosX5X3R~^6Y zNJgICW)7ZW+D@}=r!iU>4?kcEtssRy$;$Wi@9JY#hHPTVrft%_MYV+iV1 zw)4p5ZMG8*12C2otmTBLulo`f#?>dT3ge{QwoshY;m zyBO;jYaNT~L95r)!nhFf*|-WLp8@4wws9aCOuJflwFNb85a(Fa24RjhZ4hVAMAtAJQNM$2Z-8AOW(9^6Z`G4}rC%;xxKzNc+pUHW|| zGkAd^FS6vt6nRmk%Ug+H^2(F@o5N2%Uyd@}rzE-ns`ZZb>xogO?hspdC|WGGSV=h7 zBbi9~7REk|5x;hK;xuC)K#m?n+8jiV9u)A%(07HcB zlPq~MMV=HoU*UU61~-55*;@=b$&!;Pa`J!g9!2pGL}hPm3tC~&GV#rSg|}8^WQ#ls z5^QL&#}Y28U<5K0{^*}%V-w2cmbTzH+)4J899X=K8lg$;vNdJ)C4i8vsgYZZ;E8|? zq)HnQ#sB0F+Kv%R|8b~^uc!R=omgaR{-9Wd@HH=UQL67V4yC0Id~N>=UaDndho~Pd zT8FZ>VtAV66hughln`se^DNi)e=fxUQ94GfPbYb`;sgxJ_55ocoq`=G;Txl zV{^j!*b0XX%A$7|eSNwaj?A)pYiyFyyKonAA5M@KT~(z!dN$5(sy3$hb06_$cu7i6 zocjIQ)WyrczaW&g{%!^q@zy7c8;fu(U_Mjz=! zw;zN}NG+a`tHslDRTwi{b?7;4QZ(x&jHwZhq{VMDrdBw_Xn_q5Byhx3PaHf9I5o3_ zr}yBqb7=HS^V5beZ9Eh2{iZJ-dVKH0(Z{37UdGkW)C@5C!DmOto;Loz^^3(X7r*i_ zM=l6FKJ`0wVq)XelhTb+I1Jo+h_MYb`orI+Zc2X`K6|$lxRv0y6F-NHaytRjI=e8b zaB-Ha6~$A2g8?N7iN6$2)%^AYT^`yM_Za-X&0>HCr-ERW1QRUy5^my;v|u*lo`8A4 z>G8KcD6o=qB>~z8KX`%GGC%$Apo1y+-xYAGqFHk~TP+JR$W@AuWo@}Ku~0DcJy8j< z@;FRcMEAIaSx!WE_QB6_a-#8^pPa27Y|998${e#W`bIeU&9)sE?&}zR3&c3HO+&)H znb9|-_|INm;4;ti0!`G&i#UAmy*`GbPo-k~A25Kj1uK2Bf#xCZoU;b z(LPaeb`f{6G5)~J72o30g7@@1oIThx_6{7oW$)O<_UO!(54>Yb-@)j+SbbMY{QPhO zC(!3IMGpW@dl2nLM9q5<6+}IdMKg{3$%p2Y+ra6kdBgSeG*>)5E&MkqkT*d^Qa!zZ|E2S0-3 zZRuZH<4kFsyTw%RaXv!4}Wiy}yY1~BeP8a@%c=%t6;eWV?{~;jZH*YlO!OJ9&OG&~%=tl(J z9HGYm7x)mte}DvDpH-?fVNVhE9ild+{G^G7l=8DfxKhf`4$+ay|49?0sqgyimi@d) W1ti=*jTWtyz2KjHo2Ld(=>HGFlOC}E diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/guide.cpython-311.pyc b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/guide.cpython-311.pyc deleted file mode 100644 index 9be3eaa2f0522157315f3061ef3958f532ee3510..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12348 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 Tc%xT2I`fM5zu5+hs^k9w`WGXQ diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/settingsUI.cpython-311.pyc b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__pycache__/settingsUI.cpython-311.pyc deleted file mode 100644 index d01140d684c578d0246e48277f792e673da68177..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14741 zcmcILNlYABmYJ-N)drg?ip|NAkbnWR7|MXnYB0veX1B4cm^2aK#0iNbgR+fWQXT4G zwMsp6tLl+P%0st@nsRASt4qzHN8&ICE+a*R{wPZ!A&oR@jeK*f`qY=6_apQFj0iH) zP}38(%a85T>niU*p(RvetL+&*Dk>&xPz_@_ez@E zwUM@z?slcQ#PszHM*bRs@7ROuA>x-Zm$H_!mmtkG?h?|zbP4HV&g*of!hf-ADOYgM zx*9Xy(#NTlIM!&~@rR$v5ct|9x|ZC6Ybi}|f9YCEr_z=(gmlWQXU_+g$3oJ^N}7p3 z8)2&Er(af3_LuI6sWSlz?$@a3JEqL#rj-0CLFYmTg!I>_;XAIy?UK3kE?0y*W#)Qm zlmF7S%z0kA62Xg@nK&$D*sZf@WCO(MDUd{C79&879FEWk9lL}~yG9GoF?xy+vg|w- zE0f2?WQmmgI5SAuX+tZRNI&Nsfg${$Nd1)Mrcq+KKu&9Enp~8v#=4%d=9) zo{s6uXH=+%+Q4o4W$(h9j&Zk!utd(T*i;}1ff zdF9W0&`C%Tbl-YjznOtSk!Qlu)T3VqXN!;J( zTcKqRB%jG#Z%W@TSd583So058CJ&>+m~lA5kd{_BWB12wYoyvj9Dhi;a?~&mq~dU- zmSN4Ud?WD`@h0E|B;*Nat?Pr`T8ozY1ZM&gic<14Bq$S**|K#!%*C$tMh2yn$cQYh zP@IIv!$=}c3^KQ7-|XC$QEE1GF*xNYT{KZ~ICAp%C3u)1ODmkS+l$tC7>T2|Txt^T ze9qm%Aag4?%`x-(;b9#1Ifm9HPAjd9%G@Fax4>?lWh~mvTntV*7MDy^9F8!gr4`QG zbz3ZV$|5c`33uXD=;Sw#49>njZSB4F4fA7t851okGeVRp!)+Wx9xm(vQ@p)q>sZ6`aVpl z?__8BE|WeEJLoG-sqZqAWNytPX644%b~8$(;&7ygVa=`Zk(JxX#{|#EW?BZDM}GYZ z!>;!-D5bTlTzYUMrCf_F;kh| zRL2;dL;rc!{~C9>@9~xn%yTXYcVee;nh9LK(@u*va4#J9 zYrgN{j-PbbH%BXS(*1gzMGtM@aD*W(tx#?E-lEZIzvfb{+_p* zUr#W1P#=dK^wp-+=k!MF?CVXFZVo%>uCwd5*z2^4aH)1~%P2X$nK)ZMX|kNdky{LF zZUwJhH(H+HN&YrdpOWXcSw4e>2Ek{23pLId2~CX9(h7}9G$r!DDXv^<67EFSI?a79 z-)Zh!M(1Sb&MD>=Mwi18hP1Rolig~IzSHON9VQ-ibJ#(5vt75vUZ;3-sdjFQ<#(AK z<`(5U=kYY7L@EwD59y9M@@kJ?G*P6ZnMj^KF3HS^9(Y#XdADV znfW3MW-0~?m#h&&ZeBZCUu0DFDu-F_3GCq#&3q2_v>&sFdvij2?lG$)_Zehvk4IN< z;U~+EDLsRJuzdShBo5TLT`oO$O$rFN{4YY=vhLDhO$u#q4TqlV`3d}ThhF%ZD9M2p ze=ts&yNXg6yb%(_<)HtO7?jdbu3lguRze$(LR3<2;K}NfSpQqp=+cWHhorzxXj5k5 zmOI~`!H%~A?aedD!@+tZipr5yp z$93$Sjg94PbXM>MHs!{hBEz1M$NtT=II3|;F9?hBok#29iah1t5cMoc3O;Z#yibrLp)|MJsZm_=>w{SMl-%fP$C9SkHlxLL_bGL z^h={eKPF1_YoSCx`C)`_@tG#kS4F)PWsOSywNWXwbq8nZGbtd8w{hXo&zO1)MA7un zqcAQLD>4nuLO^_`=TB_PqO|D`(j+tnXjEMFZwFCiXlrQ2@GF=0#o-T8=1|aJk#S^a zLeEA*!H^Wz&tk`{7{nySP{g!de=dugf+%pbPPzHh4AL$H!huIPG_<&mzTM@^fkZ!J zN%UMvlWNPqB1*<^X2-`yqT7O0OK)}b=B2kryv0T+Hh_8_>br}MV0OKb=wU%>rX+>a zqoHk7%f^ImP{u5qMVAmQi(7gDCa2`z3qp3sLYW3--*l@`L!d^18pQp5-Y@e0 zVdR^0DpV4vRG{*xtTtNKrj@nrWvFGBN!jIB8DE`2!mrLm!K;Bc5>%m+K&JwoN2S%# z(q^r+d3RJTZ6~GeNLoNi3!+e^LDe^sSrf(9YET=wsX`lpHU-*_D!kE(cCDg)ukLp( zYQ-R_7<`pM;3JfQ_b4=I(6C#mLJxr+1$vIE8lqL5T2-f7b(vJ5VFbzz(())YYS5@O z^{UWEpihB5?uJ^{PRgidPEJsNo3yf~-L5xR)v_z3?20pSJPy?wR7a|RbDwy7iFfos z`1gY3D-2zuK~1DX zg?a+@3e=w(O_RkQBr=w;${3x$VbE)uqcuHRO^;f0h1B5q5x8L3d?5-g8nh^_!zzpr z7*Sy4sJt#(eo-sGxI6de6ScgLl=o5kQiHxUHU!^i{cHZeVX^;_FkPpFqC? z{fx-S;}O%CV7_wRH#x-DufkOVR~5L*Sn4&Xk9@AeWdfHKxXjF>dAnOj%$Z9kMXOfP zx+}l=T&=iDDy~uk`J^z$epZD>f*B8uhM?nf#V+6CL-OJIM1@ZYe5$~w1~X17BlUz| zV|I5F=vJWnsIDbi*Q3?->}UL;K&=}ibz{`FYJ;IV3T+zB>q}!Qj1w4FVB9K*)DNgI zNMKNbL8AxFHX=DnP-4;k(EihdY86Haj4CjS6x`0bU$^WP?G5cc-N*j;SRB<}jMjE* zwcUHNTH8-*`>7=!L#`(Zts1l{ZKEn&CvaVX>!ziKblg-cCP~GlC9L+frnxKHJg7Af zs?Ecsd6<&o)Wc!+MWI{6VD!wXFi&7!f%&7;(0W^=-d@eyyWd6nW>qgv;yEguuEltd zLZ1eG`)KzNfguHk7`j7)jvua&&MDIQK!t|{9xCvVb@ph`^KV&y$RQu!QsFj%+X~!f zeZ3l>E^FLTO{WH(KTMFWJERNOmPZ60De%a`bNQGJmo&JfwBJx+g203V69zfPgW9&& zMLKV*FhyWWfhl_rrt2JmIR)lU6)RtR)OS_$UELSRwFT9;NPLSlR$fEAH|B)WHKoEG z0(TU+V^@1ug=qrQ3QTjg2j|JqeYJUsG%uwbQ@bdW2W%zZ?bwy*klY}hcmH$d0hw7N zGf&kLiIhlmIgjNEjoBWqyXFPN6taX^gTXW*Mi2alMTbL&Pq`0Bk(dyqrZ+ZU4yO5R z`(t%ew7N^H?%JDEt9waxFZHm>5L^|7iyB;1S_b*3T4uxzwPJ!)Oi%{C(AZ}L%Uspl zPrSx#<4J4J4Ac5fnupY~VN!-u$Y~2UEX16o7UHVB%-2rC#Mn|u+ODe=W2C}ZZ@7uZ zT8IH?Akd&d1B;=<5@{@FYr7cHb^`4Rv@?2}r&fDN%QY1S2n;ANz!=!7b?v}?&`t+R zg-HUF3QQigbVpl;wU%MEWt6mx(tzSNj=9hjg^xA(_{aR_Kc%~KTK??9+dF#uBi+0@ zv>IayO{g_DNzF~l=D33~aa|hVNR!KnId3e_{aRiBkNIawehn(})}bPA9hF!8U(^g- zq+?-Lp@Tq&0v(1phQ@p}$0oj{<5AGEZy}m)tGjD^VSeCZVv=qF=sdO7Fw0B*Z=n7+ zQ2!eYv*L+w5VY>O_uBXT`$d$npAz;{!hR$)=l{q1)%(*l{(Ox`g7^^^*E5-b0`7@h1hy2|LIynF#Umo{!!e$Z@$XM~0C!n_;+}&DT1@!q5Kr^Y zKXI2SP5fG?)d=Gf-1rB7)NcJeXf22t9}GBmguXKzyBwvbpGZ3Y1Q((2DaS4$!%==_ z68Y$z`RDmqV0@qaEJyj-e-ruOC_m>!^0Qe;P8t2&B+{|!XZeo~<{#IeZfx^TB%S7` zaYblV=-A~Lnfwz;r==z1I%Q-EP9&X{seX?1!V^j7pOj7+nKLJnPHSjC$M&-)l1^)H zKSz4e`=uMj@!~0?WbyW|crPX{X1#buI=Q^8r5A-OoV3y zOW{%|81CIEn4{%bPrM%6c-APsEe1A)&@-c~si&_!mGy#E{}XYJm2D)Wa!Jc4bK-M3 zUZKP%T1YZ5v><3Iwb5ThB);Hdy?_e`Skl*($u#?wKVXn|3@-(AQvQ<`izq%F;u%=@Zgqdnd&gI}@ccigceU zfA>gjS|Lp%c+QVaB1L%q^&`i==f?g?j|oWLaX-IbqiQZ`8G)#Y5_yzy-F73HHv+JG9beGc$GCy^{ zb0xpWs6wfJ8rj zN^P_+|C!|9n%E4>ew^)kR>=5{VHhgCq(szn!-Ky-f6U=)(wB&%-^0RR;O$+y+wFeW z?Q&NsuF9l;e{AgDJ@=;a+Y4_lsIK}iGQarj)r)T~zRS3g=B6$>H?LOz6zlzS3L?Y8 F{{@>Z%O3y$ From 03595d7e0da5ef150e111c6fd10c956881efbf13 Mon Sep 17 00:00:00 2001 From: sketchaholic Date: Tue, 14 Jul 2026 10:15:46 -0700 Subject: [PATCH 3/4] Delete release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01 directory --- .../EPIC_chain_spline_IK_FK_01/__init__.py | 624 ------------------ .../EPIC_chain_spline_IK_FK_01/guide.py | 206 ------ .../EPIC_chain_spline_IK_FK_01/icon.jpg | Bin 2083 -> 0 bytes .../EPIC_chain_spline_IK_FK_01/settingsUI.py | 167 ----- .../EPIC_chain_spline_IK_FK_01/settingsUI.ui | 247 ------- 5 files changed, 1244 deletions(-) delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py delete mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py deleted file mode 100644 index cd3dd184..00000000 --- a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py +++ /dev/null @@ -1,624 +0,0 @@ -"""Component Chain 01 module""" - -import mgear.pymaya as pm -from mgear.pymaya import datatypes - -from mgear.shifter import component - -from mgear.core import node, applyop, vector -from mgear.core import attribute, transform, primitive, curve - -########################################################## -# COMPONENT -########################################################## - - -class Component(component.Main): - """Shifter component Class""" - - # ===================================================== - # OBJECTS - # ===================================================== - def addObjects(self): - """Add all the objects needed to create the component.""" - - self.normal = self.guide.blades["blade"].z * -1 - self.binormal = self.guide.blades["blade"].x - - self.isFk = self.settings["mode"] != 1 - self.isIk = self.settings["mode"] != 0 - self.isFkIk = self.settings["mode"] == 2 - - self.WIP = self.options["mode"] - - # FK controllers ------------------------------------ - if self.isFk: - self.fk_npo = [] - self.fk_ctl = [] - self.fk_ref = [] - self.fk_off = [] - t = self.guide.tra["root"] - self.fk_cns = primitive.addTransform( - self.root, self.getName("fk_cns"), t) - parent = self.fk_cns - tOld = False - fk_ctl = None - self.previusTag = self.parentCtlTag - for i, t in enumerate(transform.getChainTransform(self.guide.apos, - self.normal, - self.negate)): - dist = vector.getDistance(self.guide.apos[i], - self.guide.apos[i + 1]) - if self.settings["neutralpose"] or not tOld: - tnpo = t - else: - tnpo = transform.setMatrixPosition( - tOld, - transform.getPositionFromMatrix(t)) - if i: - tref = transform.setMatrixPosition( - tOld, - transform.getPositionFromMatrix(t)) - fk_ref = primitive.addTransform( - fk_ctl, - self.getName("fk%s_ref" % i), - tref) - self.fk_ref.append(fk_ref) - else: - tref = t - fk_off = primitive.addTransform( - parent, self.getName("fk%s_off" % i), tref) - fk_npo = primitive.addTransform( - fk_off, self.getName("fk%s_npo" % i), tnpo) - fk_ctl = self.addCtl( - fk_npo, - "fk%s_ctl" % i, - t, - self.color_fk, - "cube", - w=dist, - h=self.size * .1, - d=self.size * .1, - po=datatypes.Vector(dist * .5 * self.n_factor, 0, 0), - tp=self.previusTag) - - self.fk_off.append(fk_off) - self.fk_npo.append(fk_npo) - self.fk_ctl.append(fk_ctl) - tOld = t - self.previusTag = fk_ctl - - # IK controllers ------------------------------------ - if self.isIk: - - normal = vector.getTransposedVector( - self.normal, - [self.guide.apos[0], self.guide.apos[1]], - [self.guide.apos[-2], self.guide.apos[-1]]) - t = transform.getTransformLookingAt(self.guide.apos[-2], - self.guide.apos[-1], - normal, - "xy", - self.negate) - t = transform.setMatrixPosition(t, self.guide.apos[-1]) - - self.ik_cns = primitive.addTransform(self.root, - self.getName("ik_cns"), - t) - # Plain structural group (not a control) - just organizes the - # spline IK internals under ik_cns, not meant to be animated. - self.ikcns_grp = primitive.addTransform( - self.ik_cns, self.getName("ikcns_grp"), t) - - # Spline IK curves ----------------------------------- - # The curve that actually drives the ikSplineSolver is built - # directly through every guide position (one CV per guide - # locator) as a degree-1 (linear) curve. Degree matters here: - # a degree-3 curve treats these positions as B-spline control - # vertices, which the curve does NOT pass through except at - # the two ends - only a polyline (degree 1) is guaranteed to - # retrace every guide point exactly, matching the discrete - # per-segment look-at orientation add2DChain/getChainTransform - # already used to build the FK chain and self.chain. - ik_nb = self.settings["ikNb"] - self.ik_crv = curve.addCurve( - self.ikcns_grp, self.getName("ik_crv"), - [list(p) for p in self.guide.apos], - False, 1) - self.ik_crv.attr("visibility").set(False) - - # Compute good rest CV positions for an ikNb-CV curve by - # rebuilding a throwaway duplicate of the dense curve (this - # only needs to exist long enough to read its CVs - the real - # wrap curve is built fresh below). - wrap_degree = min(3, ik_nb - 1) - tmp_wrap_crv = pm.duplicate( - self.ik_crv, n=self.getName("ikWrapTmp_crv"))[0] - pm.rebuildCurve( - tmp_wrap_crv, - ch=False, - replaceOriginal=True, - rebuildType=0, - keepRange=0, - keepControlPoints=False, - keepEndPoints=True, - keepTangents=True, - spans=(ik_nb - wrap_degree), - degree=wrap_degree, - tolerance=0.001) - - # IK controls, evenly spaced by arc length along the chain -- - # (independent of the wrap curve's CVs, which are wherever - # rebuildCurve put them, not necessarily evenly spaced). - ik_positions = curve.get_uniform_world_positions_on_curve( - self.ik_crv, ik_nb) - ik_transforms = transform.getChainTransform( - ik_positions, self.normal, self.negate) - ik_transforms.append(transform.setMatrixPosition( - ik_transforms[-1], ik_positions[-1])) - - wrap_shape = tmp_wrap_crv.getShape() - cv_rest_positions = [] - for i in range(ik_nb): - pos = pm.xform("{}.cv[{}]".format(wrap_shape, i), - q=True, ws=True, t=True) - # Guard against whatever pm.xform hands back here (tuple, - # extra/odd-typed elements, etc.) so datatypes.Vector always - # gets exactly 3 plain floats. - cv_rest_positions.append( - [float(pos[0]), float(pos[1]), float(pos[2])]) - pm.delete(tmp_wrap_crv) - - # Guard against duplicate/rebuildCurve flipping the curve's - # parametric direction. If Maya's CV[0] actually landed near - # the tip instead of the root, every control needs to bind to - # the OPPOSITE CV index than a naive 1:1 mapping would assume - - # otherwise the last (tip) control ends up wired to the CV - # nearest the root (which barely needs to move), while the CV - # that should follow the tip stays wired to the first control - # instead. Check empirically which way it goes, and remap the - # control->CV-index correspondence accordingly (NOT just the - # rest-position list - the correspondence used when wiring - # gear_curvecns_op must match too). - cv0_v = datatypes.Vector(*cv_rest_positions[0]) - root_v = datatypes.Vector( - ik_positions[0].x, ik_positions[0].y, ik_positions[0].z) - tip_v = datatypes.Vector( - ik_positions[-1].x, ik_positions[-1].y, ik_positions[-1].z) - first_to_root = (cv0_v - root_v).length() - first_to_tip = (cv0_v - tip_v).length() - cvs_reversed = first_to_tip < first_to_root - - self.ik_ctl = [] - self.ik_npo = [] - cv_drivers = [None] * ik_nb - self.previusTag = self.parentCtlTag - for i, ik_t in enumerate(ik_transforms): - ik_npo = primitive.addTransform( - self.ikcns_grp, self.getName("ik%s_npo" % i), ik_t) - self.ik_npo.append(ik_npo) - - ik_ctl = self.addCtl( - ik_npo, - "ik%s_ctl" % i, - ik_t, - self.color_ik, - "cube", - w=self.size * .15, - h=self.size * .15, - d=self.size * .15, - tp=self.previusTag) - - # IK controls only position the spline curve; axial twist is - # driven by the dedicated "twist" attribute, so expose - # translate only. - attribute.setKeyableAttributes(ik_ctl, self.t_params) - - self.ik_ctl.append(ik_ctl) - self.previusTag = ik_ctl - - # Driver for this CV: parented under the control but - # positioned at the wrap curve's rebuilt rest CV location, - # so the constant offset between an evenly-spaced control - # and its (not evenly-spaced) CV is baked in via ordinary - # DAG parenting - not a skinCluster/wire bind-pose cache. - # A skinCluster's bindPreMatrix is captured once and can - # go stale relative to a component's final position once - # it's parented deep in a character hierarchy, which is - # what was actually causing the double transformation - # (disabling inheritsTransform doesn't touch a deformer's - # internal bind-pose cache, which is why that didn't fix - # it here). A live, ordinary matrix connection has no such - # cache to go stale. - # - # The first/last controls are a special case: arc-length - # sampling already puts them exactly at guide.apos[0]/[-1] - # - the same positions as the real chain's first/last - # joints - so their drivers use that exact position - # directly instead of the rebuilt curve's approximation - # (which only exists to make interior CVs approximate the - # guide shape; the endpoints don't need approximating). - cv_index = (ik_nb - 1 - i) if cvs_reversed else i - if i == 0: - cv_pos = self.guide.apos[0] - elif i == len(ik_transforms) - 1: - cv_pos = self.guide.apos[-1] - else: - cv_pos = datatypes.Vector(*cv_rest_positions[cv_index]) - cv_m = transform.setMatrixPosition(ik_t, cv_pos) - cv_driver = primitive.addTransform( - ik_ctl, self.getName("ikCvDriver%s" % i), cv_m) - cv_drivers[cv_index] = cv_driver - - # The real wrap curve, built FRESH (never touched by - # rebuildCurve) and immediately bound to the CV drivers via - # curve.addCnsCurve - the exact same helper (and pattern: - # fresh curve + immediate gear_curvecns_op) mgear uses - # everywhere else for "curve driven by N controls". Applying - # gear_curvecns_op to a curve that had already been through - # duplicate+rebuildCurve first (the previous approach) isn't - # a pattern used anywhere else in mgear and was the likely - # reason the last CV wasn't responding correctly. - self.ik_wrap_crv = curve.addCnsCurve( - self.ikcns_grp, self.getName("ikWrap_crv"), - cv_drivers, wrap_degree) - self.ik_wrap_crv.attr("visibility").set(False) - - # Twist reference transforms for the ikSplineSolver's advanced - # twist (wired in addOperators). These must be oriented to - # match self.chain's own start/end bind tangent exactly, NOT - # ik_ctl[0]/[-1]'s own orientation - the ikNb controls are - # sampled independently from the dense per-joint chain, so - # their tangent generally differs from self.chain[0]/[-1]'s - # actual orientation. Feeding that mismatched orientation - # straight into dWorldUpMatrix biased the twist calculation, - # which is what caused the extra rotation on the first joint - # once this component was parented under another (its own - # world orientation no longer canceled the mismatch out). - # Building these under ik_ctl[0]/[-1] keeps a baked, zero-error - # offset at rest while still following control rotation. - dense_transforms = transform.getChainTransform( - self.guide.apos, self.normal, self.negate) - dense_transforms.append(transform.setMatrixPosition( - dense_transforms[-1], self.guide.apos[-1])) - - # Parented under the (static) ik_npo, NOT the IK controls, so the - # solver's base twist is independent of user twist. All user twist - # is driven explicitly by the per-control "twist" attribute layer - # (see addOperators), which avoids double-counting at the ends. - self.ik_twist_start_ref = primitive.addTransform( - self.ik_npo[0], self.getName("ikTwistStart_ref"), - dense_transforms[0]) - self.ik_twist_end_ref = primitive.addTransform( - self.ik_npo[-1], self.getName("ikTwistEnd_ref"), - dense_transforms[-1]) - - # A wire deformer relays the wrap curve's shape onto the dense - # curve. Since the wrap curve hasn't moved from its rebuilt - # rest shape yet, the wire's base shape == current shape, so - # this contributes zero deformation at rest. - ik_wire = pm.wire( - self.ik_crv, - w=self.ik_wrap_crv, - n=self.getName("ikWire"), - groupWithBase=False, - envelope=1.0)[0] - - # Without an explicit dropoff distance, Maya's default falls - # short of the curve's full length, so influence (and control) - # quietly cuts off before reaching the tip. Base it on the - # curve's own length so it scales correctly with chain size. - dense_length = pm.arclen(self.ik_crv) - pm.setAttr( - "{}.dropoffDistance[0]".format(ik_wire), - dense_length * 10) - - # Chain (real joints, driven by the spline ikHandle in - # addOperators). Kept identical to the previous rotate-plane - # setup so translation always stays on the chain's local X - # (bone) axis only. - self.chain = primitive.add2DChain(self.root, - self.getName("chain"), - self.guide.apos, - self.normal, - self.negate) - self.chain[0].attr("visibility").set(self.WIP) - - # Twist layer: a twist_ref leaf under each chain joint. The IK - # deform target is this node (not the raw chain joint), so its - # rotateX - driven per IK control in addOperators - adds axial - # twist on top of the solver's curve-following, without moving the - # bone off its primary axis. - self.twist_ref = [] - for i, jnt in enumerate(self.chain): - twist_ref = primitive.addTransform( - jnt, self.getName("%s_twist_ref" % i), - transform.getTransform(jnt)) - self.twist_ref.append(twist_ref) - - # Chain of deformers ------------------------------- - self.loc = [] - parent = self.root - for i, t in enumerate(transform.getChainTransform(self.guide.apos, - self.normal, - self.negate)): - loc = primitive.addTransform(parent, self.getName("%s_loc" % i), t) - - self.loc.append(loc) - # self.jnt_pos.append([loc, i, None, False]) - - jnt_name = "_".join([self.name, str(i + 1).zfill(2)]) - if i: - guide_relative_name = "{}_loc".format(str(i - 1)) - else: - guide_relative_name = "root" - self.jnt_pos.append( - { - "obj": loc, - "name": jnt_name, - "guide_relative": guide_relative_name, - } - ) - - # ===================================================== - # ATTRIBUTES - # ===================================================== - def addAttributes(self): - """Create the anim and setupr rig attributes for the component""" - - # Anim ------------------------------------------- - if self.isFkIk: - self.blend_att = self.addAnimParam( - "blend", "Fk/Ik Blend", "double", self.settings["blend"], 0, 1) - - if self.isIk: - self.roll_att = self.addAnimParam( - "roll", "Roll", "double", 0, -180, 180) - - self.stretch_att = self.addAnimParam( - "stretch", "Stretch", "double", 1, 0, 1) - - # A single "twist" handle on every IK control. Twisting one - # control twists the chain joints nearest it, reaching zero at the - # neighbouring controls (width = "twistFalloff") - so the first - # control twists the base, the last the tip, an interior control - # the middle, each without disturbing the far ends. Wired to the - # twist_ref layer in addOperators (NOT the ikHandle, which would - # give an end-weighted ramp). - self.ik_twist_att = [ - self.addAnimParam( - "twist", "Twist", "double", 0, -360, 360, uihost=ctl) - for ctl in self.ik_ctl] - - # Ref - if self.settings["ikrefarray"]: - ref_names = self.get_valid_alias_list( - self.settings["ikrefarray"].split(",")) - if len(ref_names) > 1: - self.ikref_att = self.addAnimEnumParam( - "ikref", - "Ik Ref", - 0, - ref_names) - - # ===================================================== - # OPERATORS - # ===================================================== - def addOperators(self): - """Create operators and set the relations for the component rig - - Apply operators, constraints, expressions to the hierarchy. - In order to keep the code clean and easier to debug, - we shouldn't create any new object in this method. - - """ - - # Visibilities ------------------------------------- - if self.isFkIk: - # fk - fkvis_node = node.createReverseNode(self.blend_att) - - for fk_ctl in self.fk_ctl: - for shp in fk_ctl.getShapes(): - pm.connectAttr(fkvis_node + ".outputX", - shp.attr("visibility")) - - # ik - for ik_ctl in self.ik_ctl: - for shp in ik_ctl.getShapes(): - pm.connectAttr(self.blend_att, shp.attr("visibility")) - - # FK Chain ----------------------------------------- - if self.isFk: - for off, ref in zip(self.fk_off[1:], self.fk_ref): - applyop.gear_mulmatrix_op( - ref.worldMatrix, off.parentInverseMatrix, off, "rt") - # IK Chain ----------------------------------------- - if self.isIk: - # Real ikSplineSolver handle riding the control-driven curve. - # Using a real joint chain + native spline solver (instead of - # a motion-path/matrix-decompose rig) guarantees translation - # never leaves the chain's local X (bone) axis. - ikh_name = pm.ikHandle( - n=self.getName("ikh"), - sj=self.chain[0], - ee=self.chain[-1], - sol="ikSplineSolver", - ccv=False, - curve=self.ik_crv)[0] - self.ikh = pm.PyNode(ikh_name) - self.root.addChild(self.ikh) - self.ikh.attr("visibility").set(False) - - # Roll - pm.connectAttr(self.roll_att, self.ikh.attr("roll")) - - # Twist is handled per IK control by the twist_ref layer below, - # not the ikHandle (which would give an end-weighted ramp). - - # Advanced twist - first/last IK control orientation drives - # the twist along the length of the chain. Same dWorldUpType/ - # dWorldUpAxis convention as mgear's own EPIC_arm_02/leg_02 - # (also X-forward, Z-up "xz" chains). - self.ikh.attr("dTwistControlEnable").set(True) - self.ikh.attr("dWorldUpType").set(4) # Object Rotation Up (Start/End) - self.ikh.attr("dWorldUpAxis").set(3) - self.ikh.attr("dWorldUpVectorZ").set(1.0) - self.ikh.attr("dWorldUpVectorY").set(0.0) - self.ikh.attr("dWorldUpVectorEndZ").set(1.0) - self.ikh.attr("dWorldUpVectorEndY").set(0.0) - if self.negate: - self.ikh.attr("dForwardAxis").set(1) - - pm.connectAttr(self.ik_twist_start_ref.attr("worldMatrix"), - self.ikh.attr("dWorldUpMatrix")) - pm.connectAttr(self.ik_twist_end_ref.attr("worldMatrix"), - self.ikh.attr("dWorldUpMatrixEnd")) - - # Stretch - single axis only (chain's local X/bone length), - # scaled uniformly by the live curve-length ratio. - curve_info = pm.arclen(self.ik_crv, ch=True) - rest_length = curve_info.attr("arcLength").get() - ratio_node = node.createDivNode( - curve_info.attr("arcLength"), rest_length) - stretch_blend = node.createBlendNode( - ratio_node + ".outputX", 1.0, self.stretch_att) - - for jnt in self.chain[1:]: - rest_tx = jnt.attr("tx").get() - node.createMulNode( - stretch_blend + ".outputR", rest_tx, jnt.attr("tx")) - - # Per-control twist distribution ---------------------------- - # Each chain joint's twist_ref.rx is a sum of every IK control's - # "twist" attribute weighted by an eased TENT (hat) falloff: a - # control's influence is 1 at its own position and drops to - # exactly 0 at its neighbouring controls (radius = twistFalloff x - # control spacing). So the middle control twists only the middle - # and reaches zero at the base and tip controls - the ends do NOT - # move. The weights are additive, and at twistFalloff = 1 they sum - # to 1, so an equal twist on all controls gives a uniform roll. - M = len(self.ik_ctl) - N = len(self.chain) - falloff = max(self.settings["twistFalloff"], 1e-4) - p_ctl = [i / float(M - 1) for i in range(M)] if M > 1 else [0.0] - spacing = 1.0 / (M - 1) if M > 1 else 1.0 - radius = falloff * spacing - - for j in range(N): - p_j = j / float(N - 1) if N > 1 else 0.0 - - if M == 1: - pm.connectAttr(self.ik_twist_att[0], - self.twist_ref[j].attr("rx")) - continue - - pma = pm.createNode( - "plusMinusAverage", n=self.getName("twist%s_pma" % j)) - pma.attr("operation").set(1) # sum - idx = 0 - for i in range(M): - d = abs(p_j - p_ctl[i]) - if d >= radius: - continue - t = d / radius - # eased tent: 1 at the control, 0 at the neighbour control - w = 1.0 - (3.0 * t * t - 2.0 * t * t * t) - if w < 0.001: - continue - mul = node.createMulNode(self.ik_twist_att[i], w) - pm.connectAttr(mul + ".outputX", - pma.attr("input1D[%d]" % idx)) - idx += 1 - - pm.connectAttr(pma.attr("output1D"), - self.twist_ref[j].attr("rx")) - - # Chain of deformers ------------------------------- - for i, loc in enumerate(self.loc): - - if self.settings["mode"] == 0: # fk only - pm.parentConstraint(self.fk_ctl[i], loc, maintainOffset=False) - pm.connectAttr(self.fk_ctl[i] + ".scale", loc + ".scale") - - elif self.settings["mode"] == 1: # ik only - pm.parentConstraint( - self.twist_ref[i], loc, maintainOffset=False) - - elif self.settings["mode"] == 2: # fk/ik - - rev_node = node.createReverseNode(self.blend_att) - - # orientation - cns = pm.parentConstraint( - self.fk_ctl[i], self.twist_ref[i], loc, - maintainOffset=False) - cns.interpType.set(0) - weight_att = pm.parentConstraint( - cns, query=True, weightAliasList=True) - pm.connectAttr(rev_node + ".outputX", cns + "." + weight_att[0]) - pm.connectAttr(self.blend_att, cns + "." + weight_att[1]) - - # scaling - blend_node = pm.createNode("blendColors") - pm.connectAttr(self.chain[i].attr("scale"), - blend_node + ".color1") - pm.connectAttr(self.fk_ctl[i].attr("scale"), - blend_node + ".color2") - pm.connectAttr(self.blend_att, blend_node + ".blender") - pm.connectAttr(blend_node + ".output", loc + ".scale") - - # ===================================================== - # CONNECTOR - # ===================================================== - def setRelation(self): - """Set the relation beetween object from guide to rig""" - - self.relatives["root"] = self.loc[0] - self.jointRelatives["root"] = 0 - - if not self.isIk: - self.controlRelatives["root"] = self.fk_ctl[0] - self.controlRelatives["%s_loc" % (len(self.loc) - 1)] = self.fk_ctl[-1] - else: - self.controlRelatives["root"] = self.ik_ctl[0] - self.controlRelatives["%s_loc" % (len(self.loc) - 1)] = self.ik_ctl[-1] - - for i in range(0, len(self.loc) - 1): - self.relatives["%s_loc" % i] = self.loc[i + 1] - self.jointRelatives["%s_loc" % i] = i + 1 - self.aliasRelatives["%s_ctl" % i] = i + 1 - if not self.isIk: - self.controlRelatives["%s_loc" % i] = self.fk_ctl[i + 1] - else: - # proportionally map each deformer division to the - # nearest IK control (counts are independent: ikNb vs - # number of guide locators). - ik_idx = min( - int(round((i + 1) * (len(self.ik_ctl) - 1) / - float(len(self.loc) - 1))), - len(self.ik_ctl) - 1) - self.controlRelatives["%s_loc" % i] = self.ik_ctl[ik_idx] - - self.relatives["%s_loc" % (len(self.loc) - 1)] = self.loc[-1] - self.jointRelatives["%s_loc" % (len(self.loc) - 1)] = len(self.loc) - 1 - self.aliasRelatives["%s_loc" % (len(self.loc) - 1)] = len(self.loc) - 1 - - # @param self - def addConnection(self): - """Add more connection definition to the set""" - - self.connections["standard"] = self.connect_standard - self.connections["orientation"] = self.connect_orientation - self.connections["parent"] = self.connect_parent - - def connect_orientation(self): - """orientation connection definition for the component""" - self.connect_orientCns() - - def connect_standard(self): - """standard connection definition for the component""" - self.connect_standardWithSimpleIkRef() - - def connect_parent(self): - self.connect_standardWithSimpleIkRef() diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py deleted file mode 100644 index 1b27f1f1..00000000 --- a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py +++ /dev/null @@ -1,206 +0,0 @@ -"""Guide Chain 01 module""" - -from functools import partial -from mgear.shifter.component import guide -from mgear.core import pyqt -from mgear.vendor.Qt import QtWidgets, QtCore -from maya.app.general.mayaMixin import MayaQWidgetDockableMixin -from maya.app.general.mayaMixin import MayaQDockWidget -from . import settingsUI as sui - - -# guide info -AUTHOR = "Vi Cuong Van" -URL = "www.mcsGear.com" -EMAIL = "" -VERSION = [1, 0, 0] -TYPE = "EPIC_chain_spline_IK_FK_01" -NAME = "chain" -DESCRIPTION = ( - "Game ready component for EPIC's UE and other Game Engines\n" - "Simple IK/FK chain, With IK space switch" -) - -########################################################## -# CLASS -########################################################## - - -class Guide(guide.ComponentGuide): - """Component Guide Class""" - - compType = TYPE - compName = NAME - description = DESCRIPTION - - author = AUTHOR - url = URL - email = EMAIL - version = VERSION - - def postInit(self): - """Initialize the position for the guide""" - - self.save_transform = ["root", "#_loc"] - self.save_blade = ["blade"] - self.addMinMax("#_loc", 1, -1) - - def addObjects(self): - """Add the Guide Root, blade and locators""" - - self.root = self.addRoot() - self.locs = self.addLocMulti("#_loc", self.root) - self.blade = self.addBlade("blade", self.root, self.locs[0]) - - centers = [self.root] - centers.extend(self.locs) - self.dispcrv = self.addDispCurve("crv", centers) - - def addParameters(self): - """Add the configurations settings""" - - self.pType = self.addParam("mode", "long", 0, 0) - self.pBlend = self.addParam("blend", "double", 1, 0, 1) - self.pNeutralPose = self.addParam("neutralpose", "bool", True) - self.pIkRefArray = self.addParam("ikrefarray", "string", "") - self.pUseIndex = self.addParam("useIndex", "bool", False) - self.pParentJointIndex = self.addParam( - "parentJointIndex", "long", -1, None, None) - self.pIkNb = self.addParam("ikNb", "long", 3, 2, None) - # Width of each IK control's twist influence, as a fraction of the - # spacing to its neighbouring control. 1.0 = the tent reaches exactly - # to the neighbour controls (base/tip stay put when the middle control - # twists, and equal twist on all controls gives a uniform roll). - # Smaller = tighter / more localized twist. - self.pTwistFalloff = self.addParam( - "twistFalloff", "double", 1.0, 0.05, 2.0) - - -########################################################## -# Setting Page -########################################################## - - -class settingsTab(QtWidgets.QDialog, sui.Ui_Form): - - def __init__(self, parent=None): - super(settingsTab, self).__init__(parent) - self.setupUi(self) - - -class componentSettings(MayaQWidgetDockableMixin, guide.componentMainSettings): - - def __init__(self, parent=None): - self.toolName = TYPE - # Delete old instances of the componet settings window. - pyqt.deleteInstances(self, MayaQDockWidget) - - super(componentSettings, self).__init__(parent=parent) - self.settingsTab = settingsTab() - - self.setup_componentSettingWindow() - self.create_componentControls() - self.populate_componentControls() - self.create_componentLayout() - self.create_componentConnections() - - def setup_componentSettingWindow(self): - self.mayaMainWindow = pyqt.maya_main_window() - - self.setObjectName(self.toolName) - self.setWindowFlags(QtCore.Qt.Window) - self.setWindowTitle(TYPE) - self.resize(350, 350) - - def create_componentControls(self): - return - - def populate_componentControls(self): - """Populate Controls - - Populate the controls values from the custom attributes of the - component. - - """ - # populate tab - self.tabs.insertTab(1, self.settingsTab, "Component Settings") - - # populate component settings - self.settingsTab.ikfk_slider.setValue( - int(self.root.attr("blend").get() * 100)) - self.settingsTab.ikfk_spinBox.setValue( - int(self.root.attr("blend").get() * 100)) - self.settingsTab.mode_comboBox.setCurrentIndex( - self.root.attr("mode").get()) - - if self.root.attr("neutralpose").get(): - self.settingsTab.neutralPose_checkBox.setCheckState( - QtCore.Qt.Checked) - else: - self.settingsTab.neutralPose_checkBox.setCheckState( - QtCore.Qt.Unchecked) - - ikRefArrayItems = self.root.attr("ikrefarray").get().split(",") - for item in ikRefArrayItems: - self.settingsTab.ikRefArray_listWidget.addItem(item) - - self.settingsTab.ikNb_spinBox.setValue( - self.root.attr("ikNb").get()) - self.settingsTab.twistFalloff_spinBox.setValue( - self.root.attr("twistFalloff").get()) - - def create_componentLayout(self): - - self.settings_layout = QtWidgets.QVBoxLayout() - self.settings_layout.addWidget(self.tabs) - self.settings_layout.addWidget(self.close_button) - - self.setLayout(self.settings_layout) - - def create_componentConnections(self): - - self.settingsTab.ikfk_slider.valueChanged.connect( - partial(self.updateSlider, self.settingsTab.ikfk_slider, "blend")) - self.settingsTab.ikfk_spinBox.valueChanged.connect( - partial(self.updateSlider, self.settingsTab.ikfk_spinBox, "blend")) - - self.settingsTab.mode_comboBox.currentIndexChanged.connect( - partial(self.updateComboBox, - self.settingsTab.mode_comboBox, - "mode")) - - self.settingsTab.neutralPose_checkBox.stateChanged.connect( - partial(self.updateCheck, - self.settingsTab.neutralPose_checkBox, - "neutralpose")) - - self.settingsTab.ikRefArrayAdd_pushButton.clicked.connect( - partial(self.addItem2listWidget, - self.settingsTab.ikRefArray_listWidget, - "ikrefarray")) - self.settingsTab.ikRefArrayRemove_pushButton.clicked.connect( - partial(self.removeSelectedFromListWidget, - self.settingsTab.ikRefArray_listWidget, - "ikrefarray")) - self.settingsTab.ikRefArray_listWidget.installEventFilter(self) - - self.settingsTab.ikNb_spinBox.valueChanged.connect( - partial(self.updateSpinBox, - self.settingsTab.ikNb_spinBox, - "ikNb")) - - self.settingsTab.twistFalloff_spinBox.valueChanged.connect( - partial(self.updateSpinBox, - self.settingsTab.twistFalloff_spinBox, - "twistFalloff")) - - def eventFilter(self, sender, event): - if event.type() == QtCore.QEvent.ChildRemoved: - if sender == self.settingsTab.ikRefArray_listWidget: - self.updateListAttr(sender, "ikrefarray") - return True - else: - return QtWidgets.QDialog.eventFilter(self, sender, event) - - def dockCloseEventTriggered(self): - pyqt.deleteInstances(self, MayaQDockWidget) diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg deleted file mode 100644 index 1329f178489c2f8cdd0148f8bde7c8621c02e137..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2083 zcma)&c~H^`8^?bL2)OHkXPTPgm3MfRR^~y97b+^HXpRS&x0s}+uX$xu8F{}cF!{>J4% zAXzHgAHhF8q+9?A2dn{WFbE0AAwggyNID2q%KAaTAX&{Hg2E6AUg}8($GYp-R;za$qoHQG!IW>6D_3dU(mk*DD-at zDF>*^7(=wYJ1RJ)NR3QuuFZK(d#Lkbs<8*}=ti`~`xX(GoUb2?ba|?> zl}~bnS6$pJiH}leuImMEvwkcEC>;2`t~;dF?fuQrf^Rk{wn@kHEAEqTV>P;ec|fEf zHC*WBcKh6v3q$be-`UW)EW>qm&jB}X}an6fQQL`19HkrdSizBhsK|Q zL+4C}!iB56n^wA#h>5Ivc5$fEt9r1v<*{W=oa)y_DXh8g0;u+4a?MFy4M0SM8r^^)TR_Fa3DX zpu%Y=M*qt~qixsJrEb815p_{nv$##a9;sN}awk)sa1|gtF`$uGt~!TzD&9~dWJoi)KEy( z{C&5kW^%9D3yD}qxPpPS1#xD=RV2EvlOtY6O&|`O?yCvEB_?_qoO8%38r#u*gbZ@V zQbCDq`J&dFH|@gToo|Zs=QR)E-WV8#A;d<%ei~-_5x(>=N8y5t0g8*0Za* z7G&WGlvRzO^o&d|z+;@f&tD;>?Osfw=;HMWABMHEjM1Ub1#80@Z391tiIHxQvsR&) z0T5e3&P27Q43@9>I>p{;x?KuHxvU)hZE#n>`oZ)^#M<7}yu+IxuFboeH%(b2ig>lw zt*{Y_>I2)KQizrQ#9Dj=F5I4#o`&n9rK~#Vc3DC3d#edaoc-rAi-LkP8NsM{=d*8Z z63(_)-nXVoEJHGKz~MFS^3iI|MuQLqY^A5$_Vyo3 z`DW?-b8SA~IOpa?pK2A}2KYr2?!$+-QxR-YoijavLj^f|a^v7<3c<}~y=xmdeD_hW zitcPeG%JK`Bm~B7w#{0Zk7*+zXuA%9gr?%yx zC~DoKg_D*^Z{3-1^|(%I-C=wbK<0;N4a)NkTsluwh%BBjwx^~?B=Sg|N-**)?>Plq zw&qu!z2#BT=QicImf~@zy=~uO?3GuOD`)bmW_lD7dK=S7et~E~=7lE_7(SI+Awt-7 zu+Ql;Qa_p+aZr47}JHnB6v83Wn3W2z&l^)LmYQZhQ1^xksEA$x6${>r1jv^`(o zogkY|?dT%RuGicEw`}jl_MMM#3SNNSYoQdG;Py_SNd66ztek1ryF&t}hoY<99EYUp z$g%n6P^H3?TP+M!_AIG3?f>9b@>)a%q0sK?Z^42y_s^)9H=xhJ|r zN!f>VoVQRPW0s?D_MNC8*8QA)1Nz5P?YZXr;!V$`(QoZNOH#mlhTWdqINFBe)Ph%yME@;JzvW{PLNMJu4ZM1G_fWxg++6irE}cDQ()L3nkH*!cA^I( zdA+XbeRIA@3S3R28PPY)r*s(^-l2({jlCt3sJ^}eX2GUo?cqZ+9*nq0TgMr5D!1pN zxpmu?qOuAa9~%X}84&7+@Rgn>_XI9q-xrXW&A!VV>PHWkmo0 diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py deleted file mode 100644 index bd50ec55..00000000 --- a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py +++ /dev/null @@ -1,167 +0,0 @@ -# MGEAR is under the terms of the MIT License - -# Copyright (c) 2016 Jeremie Passerin, Miquel Campos - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Author: Jeremie Passerin -# Author: Miquel Campos www.mcsgear.com -# Date: 2016 / 10 / 10 - -import mgear.core.pyqt as gqt -QtGui, QtCore, QtWidgets, wrapInstance = gqt.qt_import() - -class Ui_Form(object): - def setupUi(self, Form): - Form.setObjectName("Form") - Form.resize(255, 290) - self.gridLayout = QtWidgets.QGridLayout(Form) - self.gridLayout.setObjectName("gridLayout") - self.groupBox = QtWidgets.QGroupBox(Form) - self.groupBox.setTitle("") - self.groupBox.setObjectName("groupBox") - self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox) - self.gridLayout_2.setObjectName("gridLayout_2") - self.verticalLayout = QtWidgets.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.formLayout = QtWidgets.QFormLayout() - self.formLayout.setObjectName("formLayout") - self.mode_label = QtWidgets.QLabel(self.groupBox) - self.mode_label.setObjectName("mode_label") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.mode_label) - self.mode_comboBox = QtWidgets.QComboBox(self.groupBox) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.mode_comboBox.sizePolicy().hasHeightForWidth()) - self.mode_comboBox.setSizePolicy(sizePolicy) - self.mode_comboBox.setObjectName("mode_comboBox") - self.mode_comboBox.addItem("") - self.mode_comboBox.addItem("") - self.mode_comboBox.addItem("") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.mode_comboBox) - self.ikfk_label = QtWidgets.QLabel(self.groupBox) - self.ikfk_label.setObjectName("ikfk_label") - self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.ikfk_label) - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.ikfk_slider = QtWidgets.QSlider(self.groupBox) - self.ikfk_slider.setMinimumSize(QtCore.QSize(0, 15)) - self.ikfk_slider.setMaximum(100) - self.ikfk_slider.setOrientation(QtCore.Qt.Horizontal) - self.ikfk_slider.setObjectName("ikfk_slider") - self.horizontalLayout_3.addWidget(self.ikfk_slider) - self.ikfk_spinBox = QtWidgets.QSpinBox(self.groupBox) - self.ikfk_spinBox.setMaximum(100) - self.ikfk_spinBox.setObjectName("ikfk_spinBox") - self.horizontalLayout_3.addWidget(self.ikfk_spinBox) - self.formLayout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3) - self.verticalLayout.addLayout(self.formLayout) - self.neutralPose_checkBox = QtWidgets.QCheckBox(self.groupBox) - self.neutralPose_checkBox.setObjectName("neutralPose_checkBox") - self.verticalLayout.addWidget(self.neutralPose_checkBox) - self.gridLayout_2.addLayout(self.verticalLayout, 0, 0, 1, 1) - self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1) - self.ikRefArray_groupBox = QtWidgets.QGroupBox(Form) - self.ikRefArray_groupBox.setObjectName("ikRefArray_groupBox") - self.gridLayout_3 = QtWidgets.QGridLayout(self.ikRefArray_groupBox) - self.gridLayout_3.setObjectName("gridLayout_3") - self.ikRefArray_horizontalLayout = QtWidgets.QHBoxLayout() - self.ikRefArray_horizontalLayout.setObjectName("ikRefArray_horizontalLayout") - self.ikRefArray_verticalLayout_1 = QtWidgets.QVBoxLayout() - self.ikRefArray_verticalLayout_1.setObjectName("ikRefArray_verticalLayout_1") - self.ikRefArray_listWidget = QtWidgets.QListWidget(self.ikRefArray_groupBox) - self.ikRefArray_listWidget.setDragDropOverwriteMode(True) - self.ikRefArray_listWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) - self.ikRefArray_listWidget.setDefaultDropAction(QtCore.Qt.MoveAction) - self.ikRefArray_listWidget.setAlternatingRowColors(True) - self.ikRefArray_listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - self.ikRefArray_listWidget.setSelectionRectVisible(False) - self.ikRefArray_listWidget.setObjectName("ikRefArray_listWidget") - self.ikRefArray_verticalLayout_1.addWidget(self.ikRefArray_listWidget) - self.ikRefArray_horizontalLayout.addLayout(self.ikRefArray_verticalLayout_1) - self.ikRefArray_verticalLayout_2 = QtWidgets.QVBoxLayout() - self.ikRefArray_verticalLayout_2.setObjectName("ikRefArray_verticalLayout_2") - self.ikRefArrayAdd_pushButton = QtWidgets.QPushButton(self.ikRefArray_groupBox) - self.ikRefArrayAdd_pushButton.setObjectName("ikRefArrayAdd_pushButton") - self.ikRefArray_verticalLayout_2.addWidget(self.ikRefArrayAdd_pushButton) - self.ikRefArrayRemove_pushButton = QtWidgets.QPushButton(self.ikRefArray_groupBox) - self.ikRefArrayRemove_pushButton.setObjectName("ikRefArrayRemove_pushButton") - self.ikRefArray_verticalLayout_2.addWidget(self.ikRefArrayRemove_pushButton) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.ikRefArray_verticalLayout_2.addItem(spacerItem) - self.ikRefArray_horizontalLayout.addLayout(self.ikRefArray_verticalLayout_2) - self.gridLayout_3.addLayout(self.ikRefArray_horizontalLayout, 0, 0, 1, 1) - self.gridLayout.addWidget(self.ikRefArray_groupBox, 1, 0, 1, 1) - - self.splineIk_groupBox = QtWidgets.QGroupBox(Form) - self.splineIk_groupBox.setObjectName("splineIk_groupBox") - self.gridLayout_4 = QtWidgets.QGridLayout(self.splineIk_groupBox) - self.gridLayout_4.setObjectName("gridLayout_4") - self.ikNb_horizontalLayout = QtWidgets.QHBoxLayout() - self.ikNb_horizontalLayout.setObjectName("ikNb_horizontalLayout") - self.ikNb_label = QtWidgets.QLabel(self.splineIk_groupBox) - self.ikNb_label.setObjectName("ikNb_label") - self.ikNb_horizontalLayout.addWidget(self.ikNb_label) - self.ikNb_spinBox = QtWidgets.QSpinBox(self.splineIk_groupBox) - self.ikNb_spinBox.setMinimum(2) - self.ikNb_spinBox.setMaximum(999) - self.ikNb_spinBox.setProperty("value", 3) - self.ikNb_spinBox.setObjectName("ikNb_spinBox") - self.ikNb_horizontalLayout.addWidget(self.ikNb_spinBox) - self.gridLayout_4.addLayout(self.ikNb_horizontalLayout, 0, 0, 1, 1) - self.twistFalloff_horizontalLayout = QtWidgets.QHBoxLayout() - self.twistFalloff_horizontalLayout.setObjectName( - "twistFalloff_horizontalLayout") - self.twistFalloff_label = QtWidgets.QLabel(self.splineIk_groupBox) - self.twistFalloff_label.setObjectName("twistFalloff_label") - self.twistFalloff_horizontalLayout.addWidget(self.twistFalloff_label) - self.twistFalloff_spinBox = QtWidgets.QDoubleSpinBox( - self.splineIk_groupBox) - self.twistFalloff_spinBox.setMinimum(0.05) - self.twistFalloff_spinBox.setMaximum(2.0) - self.twistFalloff_spinBox.setSingleStep(0.05) - self.twistFalloff_spinBox.setProperty("value", 1.0) - self.twistFalloff_spinBox.setObjectName("twistFalloff_spinBox") - self.twistFalloff_horizontalLayout.addWidget( - self.twistFalloff_spinBox) - self.gridLayout_4.addLayout( - self.twistFalloff_horizontalLayout, 1, 0, 1, 1) - self.gridLayout.addWidget(self.splineIk_groupBox, 2, 0, 1, 1) - - self.retranslateUi(Form) - QtCore.QObject.connect(self.ikfk_slider, QtCore.SIGNAL("sliderMoved(int)"), self.ikfk_spinBox.setValue) - QtCore.QObject.connect(self.ikfk_spinBox, QtCore.SIGNAL("valueChanged(int)"), self.ikfk_slider.setValue) - QtCore.QMetaObject.connectSlotsByName(Form) - - def retranslateUi(self, Form): - Form.setWindowTitle(gqt.fakeTranslate("Form", "Form", None, -1)) - self.mode_label.setText(gqt.fakeTranslate("Form", "Mode:", None, -1)) - self.mode_comboBox.setItemText(0, gqt.fakeTranslate("Form", "FK", None, -1)) - self.mode_comboBox.setItemText(1, gqt.fakeTranslate("Form", "IK", None, -1)) - self.mode_comboBox.setItemText(2, gqt.fakeTranslate("Form", "FK/IK", None, -1)) - self.ikfk_label.setText(gqt.fakeTranslate("Form", "IK/FK Blend:", None, -1)) - self.neutralPose_checkBox.setText(gqt.fakeTranslate("Form", "Neutral pose", None, -1)) - self.ikRefArray_groupBox.setTitle(gqt.fakeTranslate("Form", "IK Reference Array", None, -1)) - self.ikRefArrayAdd_pushButton.setText(gqt.fakeTranslate("Form", "<<", None, -1)) - self.ikRefArrayRemove_pushButton.setText(gqt.fakeTranslate("Form", ">>", None, -1)) - self.splineIk_groupBox.setTitle(gqt.fakeTranslate("Form", "Spline IK", None, -1)) - self.ikNb_label.setText(gqt.fakeTranslate("Form", "IK Controls:", None, -1)) - self.twistFalloff_label.setText(gqt.fakeTranslate("Form", "Twist Falloff", None, -1)) - diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui deleted file mode 100644 index 05759189..00000000 --- a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui +++ /dev/null @@ -1,247 +0,0 @@ - - - Form - - - - 0 - 0 - 255 - 290 - - - - Form - - - - - - - - - - - - - - - - Mode: - - - - - - - - 0 - 0 - - - - - FK - - - - - IK - - - - - FK/IK - - - - - - - - IK/FK Blend: - - - - - - - - - - 0 - 15 - - - - 100 - - - Qt::Horizontal - - - - - - - 100 - - - - - - - - - - - Nuetral pose - - - - - - - - - - - - IK Reference Array - - - - - - - - - - true - - - QAbstractItemView::InternalMove - - - Qt::MoveAction - - - true - - - QAbstractItemView::ExtendedSelection - - - false - - - - - - - - - - - << - - - - - - - >> - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - Spline IK - - - - - - - - IK Controls: - - - - - - - 2 - - - 999 - - - 3 - - - - - - - - - - - - - - ikfk_slider - sliderMoved(int) - ikfk_spinBox - setValue(int) - - - 144 - 47 - - - 220 - 45 - - - - - ikfk_spinBox - valueChanged(int) - ikfk_slider - setValue(int) - - - 222 - 37 - - - 182 - 37 - - - - - From 92b1e7c02d1cb5693e26c6cb068b091aaab5bead Mon Sep 17 00:00:00 2001 From: sketchaholic Date: Tue, 14 Jul 2026 10:39:06 -0700 Subject: [PATCH 4/4] Add files via upload --- .../EPIC_chain_spline_IK_FK_01/__init__.py | 645 ++++++++++++++++++ .../EPIC_chain_spline_IK_FK_01/guide.py | 211 ++++++ .../EPIC_chain_spline_IK_FK_01/icon.jpg | Bin 0 -> 2083 bytes .../EPIC_chain_spline_IK_FK_01/settingsUI.py | 202 ++++++ .../EPIC_chain_spline_IK_FK_01/settingsUI.ui | 247 +++++++ 5 files changed, 1305 insertions(+) create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py create mode 100644 release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py new file mode 100644 index 00000000..ffc6a16f --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/__init__.py @@ -0,0 +1,645 @@ +"""Component Chain 01 module""" + +import mgear.pymaya as pm +from mgear.pymaya import datatypes + +from mgear.shifter import component + +from mgear.core import node +from mgear.core import applyop +from mgear.core import vector +from mgear.core import attribute +from mgear.core import transform +from mgear.core import primitive +from mgear.core import curve + +########################################################## +# COMPONENT +########################################################## + + +class Component(component.Main): + """Shifter component Class""" + + # ===================================================== + # OBJECTS + # ===================================================== + def addObjects(self): + """Add all the objects needed to create the component.""" + + self.normal = self.guide.blades["blade"].z * -1 + self.binormal = self.guide.blades["blade"].x + + self.isFk = self.settings["mode"] != 1 + self.isIk = self.settings["mode"] != 0 + self.isFkIk = self.settings["mode"] == 2 + + self.WIP = self.options["mode"] + + # FK controllers ------------------------------------ + if self.isFk: + self.fk_npo = [] + self.fk_ctl = [] + self.fk_ref = [] + self.fk_off = [] + t = self.guide.tra["root"] + self.fk_cns = primitive.addTransform(self.root, self.getName("fk_cns"), t) + parent = self.fk_cns + tOld = False + fk_ctl = None + self.previusTag = self.parentCtlTag + for i, t in enumerate( + transform.getChainTransform(self.guide.apos, self.normal, self.negate) + ): + dist = vector.getDistance(self.guide.apos[i], self.guide.apos[i + 1]) + if self.settings["neutralpose"] or not tOld: + tnpo = t + else: + tnpo = transform.setMatrixPosition( + tOld, transform.getPositionFromMatrix(t) + ) + if i: + tref = transform.setMatrixPosition( + tOld, transform.getPositionFromMatrix(t) + ) + fk_ref = primitive.addTransform( + fk_ctl, self.getName("fk%s_ref" % i), tref + ) + self.fk_ref.append(fk_ref) + else: + tref = t + fk_off = primitive.addTransform( + parent, self.getName("fk%s_off" % i), tref + ) + fk_npo = primitive.addTransform( + fk_off, self.getName("fk%s_npo" % i), tnpo + ) + fk_ctl = self.addCtl( + fk_npo, + "fk%s_ctl" % i, + t, + self.color_fk, + "cube", + w=dist, + h=self.size * 0.1, + d=self.size * 0.1, + po=datatypes.Vector(dist * 0.5 * self.n_factor, 0, 0), + tp=self.previusTag, + ) + + self.fk_off.append(fk_off) + self.fk_npo.append(fk_npo) + self.fk_ctl.append(fk_ctl) + tOld = t + self.previusTag = fk_ctl + + # IK controllers ------------------------------------ + if self.isIk: + + normal = vector.getTransposedVector( + self.normal, + [self.guide.apos[0], self.guide.apos[1]], + [self.guide.apos[-2], self.guide.apos[-1]], + ) + t = transform.getTransformLookingAt( + self.guide.apos[-2], self.guide.apos[-1], normal, "xy", self.negate + ) + t = transform.setMatrixPosition(t, self.guide.apos[-1]) + + self.ik_cns = primitive.addTransform(self.root, self.getName("ik_cns"), t) + # Plain structural group (not a control) - just organizes the + # spline IK internals under ik_cns, not meant to be animated. + self.ikcns_grp = primitive.addTransform( + self.ik_cns, self.getName("ikcns_grp"), t + ) + + # Spline IK curves ----------------------------------- + # The curve that actually drives the ikSplineSolver is built + # directly through every guide position (one CV per guide + # locator) as a degree-1 (linear) curve. Degree matters here: + # a degree-3 curve treats these positions as B-spline control + # vertices, which the curve does NOT pass through except at + # the two ends - only a polyline (degree 1) is guaranteed to + # retrace every guide point exactly, matching the discrete + # per-segment look-at orientation add2DChain/getChainTransform + # already used to build the FK chain and self.chain. + ik_nb = self.settings["ikNb"] + self.ik_crv = curve.addCurve( + self.ikcns_grp, + self.getName("ik_crv"), + [list(p) for p in self.guide.apos], + False, + 1, + ) + self.ik_crv.attr("visibility").set(False) + + # Compute good rest CV positions for an ikNb-CV curve by + # rebuilding a throwaway duplicate of the dense curve (this + # only needs to exist long enough to read its CVs - the real + # wrap curve is built fresh below). + wrap_degree = min(3, ik_nb - 1) + tmp_wrap_crv = pm.duplicate( + self.ik_crv, name=self.getName("ikWrapTmp_crv") + )[0] + pm.rebuildCurve( + tmp_wrap_crv, + ch=False, + replaceOriginal=True, + rebuildType=0, + keepRange=0, + keepControlPoints=False, + keepEndPoints=True, + keepTangents=True, + spans=(ik_nb - wrap_degree), + degree=wrap_degree, + tolerance=0.001, + ) + + # IK controls, evenly spaced by arc length along the chain -- + # (independent of the wrap curve's CVs, which are wherever + # rebuildCurve put them, not necessarily evenly spaced). + ik_positions = curve.get_uniform_world_positions_on_curve( + self.ik_crv, ik_nb + ) + ik_transforms = transform.getChainTransform( + ik_positions, self.normal, self.negate + ) + ik_transforms.append( + transform.setMatrixPosition(ik_transforms[-1], ik_positions[-1]) + ) + + wrap_shape = tmp_wrap_crv.getShape() + cv_rest_positions = [] + for i in range(ik_nb): + pos = pm.xform( + "{}.cv[{}]".format(wrap_shape, i), q=True, ws=True, t=True + ) + # Guard against whatever pm.xform hands back here (tuple, + # extra/odd-typed elements, etc.) so datatypes.Vector always + # gets exactly 3 plain floats. + cv_rest_positions.append([float(pos[0]), float(pos[1]), float(pos[2])]) + pm.delete(tmp_wrap_crv) + + # Guard against duplicate/rebuildCurve flipping the curve's + # parametric direction. If Maya's CV[0] actually landed near + # the tip instead of the root, every control needs to bind to + # the OPPOSITE CV index than a naive 1:1 mapping would assume - + # otherwise the last (tip) control ends up wired to the CV + # nearest the root (which barely needs to move), while the CV + # that should follow the tip stays wired to the first control + # instead. Check empirically which way it goes, and remap the + # control->CV-index correspondence accordingly (NOT just the + # rest-position list - the correspondence used when wiring + # gear_curvecns_op must match too). + cv0_v = datatypes.Vector(*cv_rest_positions[0]) + root_v = datatypes.Vector( + ik_positions[0].x, ik_positions[0].y, ik_positions[0].z + ) + tip_v = datatypes.Vector( + ik_positions[-1].x, ik_positions[-1].y, ik_positions[-1].z + ) + first_to_root = (cv0_v - root_v).length() + first_to_tip = (cv0_v - tip_v).length() + cvs_reversed = first_to_tip < first_to_root + + self.ik_ctl = [] + self.ik_npo = [] + cv_drivers = [None] * ik_nb + self.previusTag = self.parentCtlTag + for i, ik_t in enumerate(ik_transforms): + ik_npo = primitive.addTransform( + self.ikcns_grp, self.getName("ik%s_npo" % i), ik_t + ) + self.ik_npo.append(ik_npo) + + ik_ctl = self.addCtl( + ik_npo, + "ik%s_ctl" % i, + ik_t, + self.color_ik, + "cube", + w=self.size * 0.15, + h=self.size * 0.15, + d=self.size * 0.15, + tp=self.previusTag, + ) + + # IK controls only position the spline curve; axial twist is + # driven by the dedicated "twist" attribute, so expose + # translate only. + attribute.setKeyableAttributes(ik_ctl, self.t_params) + + self.ik_ctl.append(ik_ctl) + self.previusTag = ik_ctl + + # Driver for this CV: parented under the control but + # positioned at the wrap curve's rebuilt rest CV location, + # so the constant offset between an evenly-spaced control + # and its (not evenly-spaced) CV is baked in via ordinary + # DAG parenting - not a skinCluster/wire bind-pose cache. + # A skinCluster's bindPreMatrix is captured once and can + # go stale relative to a component's final position once + # it's parented deep in a character hierarchy, which is + # what was actually causing the double transformation + # (disabling inheritsTransform doesn't touch a deformer's + # internal bind-pose cache, which is why that didn't fix + # it here). A live, ordinary matrix connection has no such + # cache to go stale. + # + # The first/last controls are a special case: arc-length + # sampling already puts them exactly at guide.apos[0]/[-1] + # - the same positions as the real chain's first/last + # joints - so their drivers use that exact position + # directly instead of the rebuilt curve's approximation + # (which only exists to make interior CVs approximate the + # guide shape; the endpoints don't need approximating). + cv_index = (ik_nb - 1 - i) if cvs_reversed else i + if i == 0: + cv_pos = self.guide.apos[0] + elif i == len(ik_transforms) - 1: + cv_pos = self.guide.apos[-1] + else: + cv_pos = datatypes.Vector(*cv_rest_positions[cv_index]) + cv_m = transform.setMatrixPosition(ik_t, cv_pos) + cv_driver = primitive.addTransform( + ik_ctl, self.getName("ikCvDriver%s" % i), cv_m + ) + cv_drivers[cv_index] = cv_driver + + # The real wrap curve, built FRESH (never touched by + # rebuildCurve) and immediately bound to the CV drivers via + # curve.addCnsCurve - the exact same helper (and pattern: + # fresh curve + immediate gear_curvecns_op) mgear uses + # everywhere else for "curve driven by N controls". Applying + # gear_curvecns_op to a curve that had already been through + # duplicate+rebuildCurve first (the previous approach) isn't + # a pattern used anywhere else in mgear and was the likely + # reason the last CV wasn't responding correctly. + self.ik_wrap_crv = curve.addCnsCurve( + self.ikcns_grp, self.getName("ikWrap_crv"), cv_drivers, wrap_degree + ) + self.ik_wrap_crv.attr("visibility").set(False) + + # Twist reference transforms for the ikSplineSolver's advanced + # twist (wired in addOperators). These must be oriented to + # match self.chain's own start/end bind tangent exactly, NOT + # ik_ctl[0]/[-1]'s own orientation - the ikNb controls are + # sampled independently from the dense per-joint chain, so + # their tangent generally differs from self.chain[0]/[-1]'s + # actual orientation. Feeding that mismatched orientation + # straight into dWorldUpMatrix biased the twist calculation, + # which is what caused the extra rotation on the first joint + # once this component was parented under another (its own + # world orientation no longer canceled the mismatch out). + # Building these under ik_ctl[0]/[-1] keeps a baked, zero-error + # offset at rest while still following control rotation. + dense_transforms = transform.getChainTransform( + self.guide.apos, self.normal, self.negate + ) + dense_transforms.append( + transform.setMatrixPosition(dense_transforms[-1], self.guide.apos[-1]) + ) + + # Parented under the (static) ik_npo, NOT the IK controls, so the + # solver's base twist is independent of user twist. All user twist + # is driven explicitly by the per-control "twist" attribute layer + # (see addOperators), which avoids double-counting at the ends. + self.ik_twist_start_ref = primitive.addTransform( + self.ik_npo[0], self.getName("ikTwistStart_ref"), dense_transforms[0] + ) + self.ik_twist_end_ref = primitive.addTransform( + self.ik_npo[-1], self.getName("ikTwistEnd_ref"), dense_transforms[-1] + ) + + # A wire deformer relays the wrap curve's shape onto the dense + # curve. Since the wrap curve hasn't moved from its rebuilt + # rest shape yet, the wire's base shape == current shape, so + # this contributes zero deformation at rest. + ik_wire = pm.wire( + self.ik_crv, + w=self.ik_wrap_crv, + n=self.getName("ikWire"), + groupWithBase=False, + envelope=1.0, + )[0] + + # Without an explicit dropoff distance, Maya's default falls + # short of the curve's full length, so influence (and control) + # quietly cuts off before reaching the tip. Base it on the + # curve's own length so it scales correctly with chain size. + dense_length = pm.arclen(self.ik_crv) + pm.setAttr("{}.dropoffDistance[0]".format(ik_wire), dense_length * 10) + + # Chain (real joints, driven by the spline ikHandle in + # addOperators). Kept identical to the previous rotate-plane + # setup so translation always stays on the chain's local X + # (bone) axis only. + self.chain = primitive.add2DChain( + self.root, + self.getName("chain"), + self.guide.apos, + self.normal, + self.negate, + ) + self.chain[0].attr("visibility").set(self.WIP) + + # Twist layer: a twist_ref leaf under each chain joint. The IK + # deform target is this node (not the raw chain joint), so its + # rotateX - driven per IK control in addOperators - adds axial + # twist on top of the solver's curve-following, without moving the + # bone off its primary axis. + self.twist_ref = [] + for i, jnt in enumerate(self.chain): + twist_ref = primitive.addTransform( + jnt, self.getName("%s_twist_ref" % i), transform.getTransform(jnt) + ) + self.twist_ref.append(twist_ref) + + # Chain of deformers ------------------------------- + self.loc = [] + parent = self.root + for i, t in enumerate( + transform.getChainTransform(self.guide.apos, self.normal, self.negate) + ): + loc = primitive.addTransform(parent, self.getName("%s_loc" % i), t) + + self.loc.append(loc) + # self.jnt_pos.append([loc, i, None, False]) + + jnt_name = "_".join([self.name, str(i + 1).zfill(2)]) + if i: + guide_relative_name = "{}_loc".format(str(i - 1)) + else: + guide_relative_name = "root" + self.jnt_pos.append( + { + "obj": loc, + "name": jnt_name, + "guide_relative": guide_relative_name, + } + ) + + # ===================================================== + # ATTRIBUTES + # ===================================================== + def addAttributes(self): + """Create the anim and setupr rig attributes for the component""" + + # Anim ------------------------------------------- + if self.isFkIk: + self.blend_att = self.addAnimParam( + "blend", "Fk/Ik Blend", "double", self.settings["blend"], 0, 1 + ) + + if self.isIk: + self.roll_att = self.addAnimParam("roll", "Roll", "double", 0, -180, 180) + + self.stretch_att = self.addAnimParam( + "stretch", "Stretch", "double", 1, 0, 1 + ) + + # A single "twist" handle on every IK control. Twisting one + # control twists the chain joints nearest it, reaching zero at the + # neighbouring controls (width = "twistFalloff") - so the first + # control twists the base, the last the tip, an interior control + # the middle, each without disturbing the far ends. Wired to the + # twist_ref layer in addOperators (NOT the ikHandle, which would + # give an end-weighted ramp). + self.ik_twist_att = [ + self.addAnimParam("twist", "Twist", "double", 0, -360, 360, uihost=ctl) + for ctl in self.ik_ctl + ] + + # Ref + if self.settings["ikrefarray"]: + ref_names = self.get_valid_alias_list( + self.settings["ikrefarray"].split(",") + ) + if len(ref_names) > 1: + self.ikref_att = self.addAnimEnumParam( + "ikref", "Ik Ref", 0, ref_names + ) + + # ===================================================== + # OPERATORS + # ===================================================== + def addOperators(self): + """Create operators and set the relations for the component rig + + Apply operators, constraints, expressions to the hierarchy. + In order to keep the code clean and easier to debug, + we shouldn't create any new object in this method. + + """ + + # Visibilities ------------------------------------- + if self.isFkIk: + # fk + fkvis_node = node.createReverseNode(self.blend_att) + + for fk_ctl in self.fk_ctl: + for shp in fk_ctl.getShapes(): + pm.connectAttr(fkvis_node + ".outputX", shp.attr("visibility")) + + # ik + for ik_ctl in self.ik_ctl: + for shp in ik_ctl.getShapes(): + pm.connectAttr(self.blend_att, shp.attr("visibility")) + + # FK Chain ----------------------------------------- + if self.isFk: + for off, ref in zip(self.fk_off[1:], self.fk_ref): + applyop.gear_mulmatrix_op( + ref.worldMatrix, off.parentInverseMatrix, off, "rt" + ) + # IK Chain ----------------------------------------- + if self.isIk: + # Real ikSplineSolver handle riding the control-driven curve. + # Using a real joint chain + native spline solver (instead of + # a motion-path/matrix-decompose rig) guarantees translation + # never leaves the chain's local X (bone) axis. + ikh_name = pm.ikHandle( + n=self.getName("ikh"), + sj=self.chain[0], + ee=self.chain[-1], + sol="ikSplineSolver", + ccv=False, + curve=self.ik_crv, + )[0] + self.ikh = pm.PyNode(ikh_name) + self.root.addChild(self.ikh) + self.ikh.attr("visibility").set(False) + + # Roll + pm.connectAttr(self.roll_att, self.ikh.attr("roll")) + + # Twist is handled per IK control by the twist_ref layer below, + # not the ikHandle (which would give an end-weighted ramp). + + # Advanced twist - first/last IK control orientation drives + # the twist along the length of the chain. Same dWorldUpType/ + # dWorldUpAxis convention as mgear's own EPIC_arm_02/leg_02 + # (also X-forward, Z-up "xz" chains). + self.ikh.attr("dTwistControlEnable").set(True) + self.ikh.attr("dWorldUpType").set(4) # Object Rotation Up (Start/End) + self.ikh.attr("dWorldUpAxis").set(3) + self.ikh.attr("dWorldUpVectorZ").set(1.0) + self.ikh.attr("dWorldUpVectorY").set(0.0) + self.ikh.attr("dWorldUpVectorEndZ").set(1.0) + self.ikh.attr("dWorldUpVectorEndY").set(0.0) + if self.negate: + self.ikh.attr("dForwardAxis").set(1) + + pm.connectAttr( + self.ik_twist_start_ref.attr("worldMatrix"), + self.ikh.attr("dWorldUpMatrix"), + ) + pm.connectAttr( + self.ik_twist_end_ref.attr("worldMatrix"), + self.ikh.attr("dWorldUpMatrixEnd"), + ) + + # Stretch - single axis only (chain's local X/bone length), + # scaled uniformly by the live curve-length ratio. + curve_info = pm.arclen(self.ik_crv, ch=True) + rest_length = curve_info.attr("arcLength").get() + ratio_node = node.createDivNode(curve_info.attr("arcLength"), rest_length) + stretch_blend = node.createBlendNode( + ratio_node + ".outputX", 1.0, self.stretch_att + ) + + for jnt in self.chain[1:]: + rest_tx = jnt.attr("tx").get() + node.createMulNode(stretch_blend + ".outputR", rest_tx, jnt.attr("tx")) + + # Per-control twist distribution ---------------------------- + # Each chain joint's twist_ref.rx is a sum of every IK control's + # "twist" attribute weighted by an eased TENT (hat) falloff: a + # control's influence is 1 at its own position and drops to + # exactly 0 at its neighbouring controls (radius = twistFalloff x + # control spacing). So the middle control twists only the middle + # and reaches zero at the base and tip controls - the ends do NOT + # move. The weights are additive, and at twistFalloff = 1 they sum + # to 1, so an equal twist on all controls gives a uniform roll. + M = len(self.ik_ctl) + N = len(self.chain) + falloff = max(self.settings["twistFalloff"], 1e-4) + p_ctl = [i / float(M - 1) for i in range(M)] if M > 1 else [0.0] + spacing = 1.0 / (M - 1) if M > 1 else 1.0 + radius = falloff * spacing + + for j in range(N): + p_j = j / float(N - 1) if N > 1 else 0.0 + + if M == 1: + pm.connectAttr(self.ik_twist_att[0], self.twist_ref[j].attr("rx")) + continue + + pma = pm.createNode( + "plusMinusAverage", name=self.getName("twist%s_pma" % j) + ) + pma.attr("operation").set(1) # sum + idx = 0 + for i in range(M): + d = abs(p_j - p_ctl[i]) + if d >= radius: + continue + t = d / radius + # eased tent: 1 at the control, 0 at the neighbour control + w = 1.0 - (3.0 * t * t - 2.0 * t * t * t) + if w < 0.001: + continue + mul = node.createMulNode(self.ik_twist_att[i], w) + pm.connectAttr(mul + ".outputX", pma.attr("input1D[%d]" % idx)) + idx += 1 + + pm.connectAttr(pma.attr("output1D"), self.twist_ref[j].attr("rx")) + + # Chain of deformers ------------------------------- + for i, loc in enumerate(self.loc): + + if self.settings["mode"] == 0: # fk only + pm.parentConstraint(self.fk_ctl[i], loc, maintainOffset=False) + pm.connectAttr(self.fk_ctl[i] + ".scale", loc + ".scale") + + elif self.settings["mode"] == 1: # ik only + pm.parentConstraint(self.twist_ref[i], loc, maintainOffset=False) + + elif self.settings["mode"] == 2: # fk/ik + + rev_node = node.createReverseNode(self.blend_att) + + # orientation + cns = pm.parentConstraint( + self.fk_ctl[i], self.twist_ref[i], loc, maintainOffset=False + ) + cns.interpType.set(0) + weight_att = pm.parentConstraint(cns, query=True, weightAliasList=True) + pm.connectAttr(rev_node + ".outputX", cns + "." + weight_att[0]) + pm.connectAttr(self.blend_att, cns + "." + weight_att[1]) + + # scaling + blend_node = pm.createNode("blendColors") + pm.connectAttr(self.chain[i].attr("scale"), blend_node + ".color1") + pm.connectAttr(self.fk_ctl[i].attr("scale"), blend_node + ".color2") + pm.connectAttr(self.blend_att, blend_node + ".blender") + pm.connectAttr(blend_node + ".output", loc + ".scale") + + # ===================================================== + # CONNECTOR + # ===================================================== + def setRelation(self): + """Set the relation beetween object from guide to rig""" + + self.relatives["root"] = self.loc[0] + self.jointRelatives["root"] = 0 + + if not self.isIk: + self.controlRelatives["root"] = self.fk_ctl[0] + self.controlRelatives["%s_loc" % (len(self.loc) - 1)] = self.fk_ctl[-1] + else: + self.controlRelatives["root"] = self.ik_ctl[0] + self.controlRelatives["%s_loc" % (len(self.loc) - 1)] = self.ik_ctl[-1] + + for i in range(0, len(self.loc) - 1): + self.relatives["%s_loc" % i] = self.loc[i + 1] + self.jointRelatives["%s_loc" % i] = i + 1 + self.aliasRelatives["%s_ctl" % i] = i + 1 + if not self.isIk: + self.controlRelatives["%s_loc" % i] = self.fk_ctl[i + 1] + else: + # proportionally map each deformer division to the + # nearest IK control (counts are independent: ikNb vs + # number of guide locators). + ik_idx = min( + int( + round( + (i + 1) * (len(self.ik_ctl) - 1) / float(len(self.loc) - 1) + ) + ), + len(self.ik_ctl) - 1, + ) + self.controlRelatives["%s_loc" % i] = self.ik_ctl[ik_idx] + + self.relatives["%s_loc" % (len(self.loc) - 1)] = self.loc[-1] + self.jointRelatives["%s_loc" % (len(self.loc) - 1)] = len(self.loc) - 1 + self.aliasRelatives["%s_loc" % (len(self.loc) - 1)] = len(self.loc) - 1 + + # @param self + def addConnection(self): + """Add more connection definition to the set""" + + self.connections["standard"] = self.connect_standard + self.connections["orientation"] = self.connect_orientation + self.connections["parent"] = self.connect_parent + + def connect_orientation(self): + """orientation connection definition for the component""" + self.connect_orientCns() + + def connect_standard(self): + """standard connection definition for the component""" + self.connect_standardWithSimpleIkRef() + + def connect_parent(self): + self.connect_standardWithSimpleIkRef() diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py new file mode 100644 index 00000000..f1a06dad --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/guide.py @@ -0,0 +1,211 @@ +"""Guide Chain 01 module""" + +from functools import partial +from mgear.shifter.component import guide +from mgear.core import pyqt +from mgear.vendor.Qt import QtWidgets +from mgear.vendor.Qt import QtCore +from maya.app.general.mayaMixin import MayaQWidgetDockableMixin +from maya.app.general.mayaMixin import MayaQDockWidget +from . import settingsUI + +# guide info +AUTHOR = "Vi Cuong Van" +URL = "www.mcsGear.com" +EMAIL = "" +VERSION = [1, 0, 0] +TYPE = "EPIC_chain_spline_IK_FK_01" +NAME = "chain" +DESCRIPTION = ( + "Game ready component for EPIC's UE and other Game Engines\n" + "Simple IK/FK chain, With IK space switch" +) + +########################################################## +# CLASS +########################################################## + + +class Guide(guide.ComponentGuide): + """Component Guide Class""" + + compType = TYPE + compName = NAME + description = DESCRIPTION + + author = AUTHOR + url = URL + email = EMAIL + version = VERSION + + def postInit(self): + """Initialize the position for the guide""" + + self.save_transform = ["root", "#_loc"] + self.save_blade = ["blade"] + self.addMinMax("#_loc", 1, -1) + + def addObjects(self): + """Add the Guide Root, blade and locators""" + + self.root = self.addRoot() + self.locs = self.addLocMulti("#_loc", self.root) + self.blade = self.addBlade("blade", self.root, self.locs[0]) + + centers = [self.root] + centers.extend(self.locs) + self.dispcrv = self.addDispCurve("crv", centers) + + def addParameters(self): + """Add the configurations settings""" + + self.pType = self.addParam("mode", "long", 0, 0) + self.pBlend = self.addParam("blend", "double", 1, 0, 1) + self.pNeutralPose = self.addParam("neutralpose", "bool", True) + self.pIkRefArray = self.addParam("ikrefarray", "string", "") + self.pUseIndex = self.addParam("useIndex", "bool", False) + self.pParentJointIndex = self.addParam( + "parentJointIndex", "long", -1, None, None + ) + self.pIkNb = self.addParam("ikNb", "long", 3, 2, None) + # Width of each IK control's twist influence, as a fraction of the + # spacing to its neighbouring control. 1.0 = the tent reaches exactly + # to the neighbour controls (base/tip stay put when the middle control + # twists, and equal twist on all controls gives a uniform roll). + # Smaller = tighter / more localized twist. + self.pTwistFalloff = self.addParam("twistFalloff", "double", 1.0, 0.05, 2.0) + + +########################################################## +# Setting Page +########################################################## + + +class settingsTab(QtWidgets.QDialog, settingsUI.Ui_Form): + + def __init__(self, parent=None): + super(settingsTab, self).__init__(parent) + self.setupUi(self) + + +class componentSettings(MayaQWidgetDockableMixin, guide.componentMainSettings): + + def __init__(self, parent=None): + self.toolName = TYPE + # Delete old instances of the componet settings window. + pyqt.deleteInstances(self, MayaQDockWidget) + + super(componentSettings, self).__init__(parent=parent) + self.settingsTab = settingsTab() + + self.setup_componentSettingWindow() + self.create_componentControls() + self.populate_componentControls() + self.create_componentLayout() + self.create_componentConnections() + + def setup_componentSettingWindow(self): + self.mayaMainWindow = pyqt.maya_main_window() + + self.setObjectName(self.toolName) + self.setWindowFlags(QtCore.Qt.Window) + self.setWindowTitle(TYPE) + self.resize(350, 350) + + def create_componentControls(self): + return + + def populate_componentControls(self): + """Populate Controls + + Populate the controls values from the custom attributes of the + component. + + """ + # populate tab + self.tabs.insertTab(1, self.settingsTab, "Component Settings") + + # populate component settings + self.settingsTab.ikfk_slider.setValue(int(self.root.attr("blend").get() * 100)) + self.settingsTab.ikfk_spinBox.setValue(int(self.root.attr("blend").get() * 100)) + self.settingsTab.mode_comboBox.setCurrentIndex(self.root.attr("mode").get()) + + if self.root.attr("neutralpose").get(): + self.settingsTab.neutralPose_checkBox.setCheckState(QtCore.Qt.Checked) + else: + self.settingsTab.neutralPose_checkBox.setCheckState(QtCore.Qt.Unchecked) + + ikRefArrayItems = self.root.attr("ikrefarray").get().split(",") + for item in ikRefArrayItems: + self.settingsTab.ikRefArray_listWidget.addItem(item) + + self.settingsTab.ikNb_spinBox.setValue(self.root.attr("ikNb").get()) + self.settingsTab.twistFalloff_spinBox.setValue( + self.root.attr("twistFalloff").get() + ) + + def create_componentLayout(self): + + self.settings_layout = QtWidgets.QVBoxLayout() + self.settings_layout.addWidget(self.tabs) + self.settings_layout.addWidget(self.close_button) + + self.setLayout(self.settings_layout) + + def create_componentConnections(self): + + self.settingsTab.ikfk_slider.valueChanged.connect( + partial(self.updateSlider, self.settingsTab.ikfk_slider, "blend") + ) + self.settingsTab.ikfk_spinBox.valueChanged.connect( + partial(self.updateSlider, self.settingsTab.ikfk_spinBox, "blend") + ) + + self.settingsTab.mode_comboBox.currentIndexChanged.connect( + partial(self.updateComboBox, self.settingsTab.mode_comboBox, "mode") + ) + + self.settingsTab.neutralPose_checkBox.stateChanged.connect( + partial( + self.updateCheck, self.settingsTab.neutralPose_checkBox, "neutralpose" + ) + ) + + self.settingsTab.ikRefArrayAdd_pushButton.clicked.connect( + partial( + self.addItem2listWidget, + self.settingsTab.ikRefArray_listWidget, + "ikrefarray", + ) + ) + self.settingsTab.ikRefArrayRemove_pushButton.clicked.connect( + partial( + self.removeSelectedFromListWidget, + self.settingsTab.ikRefArray_listWidget, + "ikrefarray", + ) + ) + self.settingsTab.ikRefArray_listWidget.installEventFilter(self) + + self.settingsTab.ikNb_spinBox.valueChanged.connect( + partial(self.updateSpinBox, self.settingsTab.ikNb_spinBox, "ikNb") + ) + + self.settingsTab.twistFalloff_spinBox.valueChanged.connect( + partial( + self.updateSpinBox, + self.settingsTab.twistFalloff_spinBox, + "twistFalloff", + ) + ) + + def eventFilter(self, sender, event): + if event.type() == QtCore.QEvent.ChildRemoved: + if sender == self.settingsTab.ikRefArray_listWidget: + self.updateListAttr(sender, "ikrefarray") + return True + else: + return QtWidgets.QDialog.eventFilter(self, sender, event) + + def dockCloseEventTriggered(self): + pyqt.deleteInstances(self, MayaQDockWidget) diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1329f178489c2f8cdd0148f8bde7c8621c02e137 GIT binary patch literal 2083 zcma)&c~H^`8^?bL2)OHkXPTPgm3MfRR^~y97b+^HXpRS&x0s}+uX$xu8F{}cF!{>J4% zAXzHgAHhF8q+9?A2dn{WFbE0AAwggyNID2q%KAaTAX&{Hg2E6AUg}8($GYp-R;za$qoHQG!IW>6D_3dU(mk*DD-at zDF>*^7(=wYJ1RJ)NR3QuuFZK(d#Lkbs<8*}=ti`~`xX(GoUb2?ba|?> zl}~bnS6$pJiH}leuImMEvwkcEC>;2`t~;dF?fuQrf^Rk{wn@kHEAEqTV>P;ec|fEf zHC*WBcKh6v3q$be-`UW)EW>qm&jB}X}an6fQQL`19HkrdSizBhsK|Q zL+4C}!iB56n^wA#h>5Ivc5$fEt9r1v<*{W=oa)y_DXh8g0;u+4a?MFy4M0SM8r^^)TR_Fa3DX zpu%Y=M*qt~qixsJrEb815p_{nv$##a9;sN}awk)sa1|gtF`$uGt~!TzD&9~dWJoi)KEy( z{C&5kW^%9D3yD}qxPpPS1#xD=RV2EvlOtY6O&|`O?yCvEB_?_qoO8%38r#u*gbZ@V zQbCDq`J&dFH|@gToo|Zs=QR)E-WV8#A;d<%ei~-_5x(>=N8y5t0g8*0Za* z7G&WGlvRzO^o&d|z+;@f&tD;>?Osfw=;HMWABMHEjM1Ub1#80@Z391tiIHxQvsR&) z0T5e3&P27Q43@9>I>p{;x?KuHxvU)hZE#n>`oZ)^#M<7}yu+IxuFboeH%(b2ig>lw zt*{Y_>I2)KQizrQ#9Dj=F5I4#o`&n9rK~#Vc3DC3d#edaoc-rAi-LkP8NsM{=d*8Z z63(_)-nXVoEJHGKz~MFS^3iI|MuQLqY^A5$_Vyo3 z`DW?-b8SA~IOpa?pK2A}2KYr2?!$+-QxR-YoijavLj^f|a^v7<3c<}~y=xmdeD_hW zitcPeG%JK`Bm~B7w#{0Zk7*+zXuA%9gr?%yx zC~DoKg_D*^Z{3-1^|(%I-C=wbK<0;N4a)NkTsluwh%BBjwx^~?B=Sg|N-**)?>Plq zw&qu!z2#BT=QicImf~@zy=~uO?3GuOD`)bmW_lD7dK=S7et~E~=7lE_7(SI+Awt-7 zu+Ql;Qa_p+aZr47}JHnB6v83Wn3W2z&l^)LmYQZhQ1^xksEA$x6${>r1jv^`(o zogkY|?dT%RuGicEw`}jl_MMM#3SNNSYoQdG;Py_SNd66ztek1ryF&t}hoY<99EYUp z$g%n6P^H3?TP+M!_AIG3?f>9b@>)a%q0sK?Z^42y_s^)9H=xhJ|r zN!f>VoVQRPW0s?D_MNC8*8QA)1Nz5P?YZXr;!V$`(QoZNOH#mlhTWdqINFBe)Ph%yME@;JzvW{PLNMJu4ZM1G_fWxg++6irE}cDQ()L3nkH*!cA^I( zdA+XbeRIA@3S3R28PPY)r*s(^-l2({jlCt3sJ^}eX2GUo?cqZ+9*nq0TgMr5D!1pN zxpmu?qOuAa9~%X}84&7+@Rgn>_XI9q-xrXW&A!VV>PHWkmo0 literal 0 HcmV?d00001 diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py new file mode 100644 index 00000000..888e3b04 --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.py @@ -0,0 +1,202 @@ +# MGEAR is under the terms of the MIT License + +# Copyright (c) 2016 Jeremie Passerin, Miquel Campos + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Author: Jeremie Passerin +# Author: Miquel Campos www.mcsgear.com +# Date: 2016 / 10 / 10 + +from mgear.core import pyqt + +QtGui, QtCore, QtWidgets, wrapInstance = pyqt.qt_import() + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(255, 290) + self.gridLayout = QtWidgets.QGridLayout(Form) + self.gridLayout.setObjectName("gridLayout") + self.groupBox = QtWidgets.QGroupBox(Form) + self.groupBox.setTitle("") + self.groupBox.setObjectName("groupBox") + self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox) + self.gridLayout_2.setObjectName("gridLayout_2") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.formLayout = QtWidgets.QFormLayout() + self.formLayout.setObjectName("formLayout") + self.mode_label = QtWidgets.QLabel(self.groupBox) + self.mode_label.setObjectName("mode_label") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.mode_label) + self.mode_comboBox = QtWidgets.QComboBox(self.groupBox) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed + ) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.mode_comboBox.sizePolicy().hasHeightForWidth() + ) + self.mode_comboBox.setSizePolicy(sizePolicy) + self.mode_comboBox.setObjectName("mode_comboBox") + self.mode_comboBox.addItem("") + self.mode_comboBox.addItem("") + self.mode_comboBox.addItem("") + self.formLayout.setWidget( + 0, QtWidgets.QFormLayout.FieldRole, self.mode_comboBox + ) + self.ikfk_label = QtWidgets.QLabel(self.groupBox) + self.ikfk_label.setObjectName("ikfk_label") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.ikfk_label) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.ikfk_slider = QtWidgets.QSlider(self.groupBox) + self.ikfk_slider.setMinimumSize(QtCore.QSize(0, 15)) + self.ikfk_slider.setMaximum(100) + self.ikfk_slider.setOrientation(QtCore.Qt.Horizontal) + self.ikfk_slider.setObjectName("ikfk_slider") + self.horizontalLayout_3.addWidget(self.ikfk_slider) + self.ikfk_spinBox = QtWidgets.QSpinBox(self.groupBox) + self.ikfk_spinBox.setMaximum(100) + self.ikfk_spinBox.setObjectName("ikfk_spinBox") + self.horizontalLayout_3.addWidget(self.ikfk_spinBox) + self.formLayout.setLayout( + 1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3 + ) + self.verticalLayout.addLayout(self.formLayout) + self.neutralPose_checkBox = QtWidgets.QCheckBox(self.groupBox) + self.neutralPose_checkBox.setObjectName("neutralPose_checkBox") + self.verticalLayout.addWidget(self.neutralPose_checkBox) + self.gridLayout_2.addLayout(self.verticalLayout, 0, 0, 1, 1) + self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1) + self.ikRefArray_groupBox = QtWidgets.QGroupBox(Form) + self.ikRefArray_groupBox.setObjectName("ikRefArray_groupBox") + self.gridLayout_3 = QtWidgets.QGridLayout(self.ikRefArray_groupBox) + self.gridLayout_3.setObjectName("gridLayout_3") + self.ikRefArray_horizontalLayout = QtWidgets.QHBoxLayout() + self.ikRefArray_horizontalLayout.setObjectName("ikRefArray_horizontalLayout") + self.ikRefArray_verticalLayout_1 = QtWidgets.QVBoxLayout() + self.ikRefArray_verticalLayout_1.setObjectName("ikRefArray_verticalLayout_1") + self.ikRefArray_listWidget = QtWidgets.QListWidget(self.ikRefArray_groupBox) + self.ikRefArray_listWidget.setDragDropOverwriteMode(True) + self.ikRefArray_listWidget.setDragDropMode( + QtWidgets.QAbstractItemView.InternalMove + ) + self.ikRefArray_listWidget.setDefaultDropAction(QtCore.Qt.MoveAction) + self.ikRefArray_listWidget.setAlternatingRowColors(True) + self.ikRefArray_listWidget.setSelectionMode( + QtWidgets.QAbstractItemView.ExtendedSelection + ) + self.ikRefArray_listWidget.setSelectionRectVisible(False) + self.ikRefArray_listWidget.setObjectName("ikRefArray_listWidget") + self.ikRefArray_verticalLayout_1.addWidget(self.ikRefArray_listWidget) + self.ikRefArray_horizontalLayout.addLayout(self.ikRefArray_verticalLayout_1) + self.ikRefArray_verticalLayout_2 = QtWidgets.QVBoxLayout() + self.ikRefArray_verticalLayout_2.setObjectName("ikRefArray_verticalLayout_2") + self.ikRefArrayAdd_pushButton = QtWidgets.QPushButton(self.ikRefArray_groupBox) + self.ikRefArrayAdd_pushButton.setObjectName("ikRefArrayAdd_pushButton") + self.ikRefArray_verticalLayout_2.addWidget(self.ikRefArrayAdd_pushButton) + self.ikRefArrayRemove_pushButton = QtWidgets.QPushButton( + self.ikRefArray_groupBox + ) + self.ikRefArrayRemove_pushButton.setObjectName("ikRefArrayRemove_pushButton") + self.ikRefArray_verticalLayout_2.addWidget(self.ikRefArrayRemove_pushButton) + spacerItem = QtWidgets.QSpacerItem( + 20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding + ) + self.ikRefArray_verticalLayout_2.addItem(spacerItem) + self.ikRefArray_horizontalLayout.addLayout(self.ikRefArray_verticalLayout_2) + self.gridLayout_3.addLayout(self.ikRefArray_horizontalLayout, 0, 0, 1, 1) + self.gridLayout.addWidget(self.ikRefArray_groupBox, 1, 0, 1, 1) + + self.splineIk_groupBox = QtWidgets.QGroupBox(Form) + self.splineIk_groupBox.setObjectName("splineIk_groupBox") + self.gridLayout_4 = QtWidgets.QGridLayout(self.splineIk_groupBox) + self.gridLayout_4.setObjectName("gridLayout_4") + self.ikNb_horizontalLayout = QtWidgets.QHBoxLayout() + self.ikNb_horizontalLayout.setObjectName("ikNb_horizontalLayout") + self.ikNb_label = QtWidgets.QLabel(self.splineIk_groupBox) + self.ikNb_label.setObjectName("ikNb_label") + self.ikNb_horizontalLayout.addWidget(self.ikNb_label) + self.ikNb_spinBox = QtWidgets.QSpinBox(self.splineIk_groupBox) + self.ikNb_spinBox.setMinimum(2) + self.ikNb_spinBox.setMaximum(999) + self.ikNb_spinBox.setProperty("value", 3) + self.ikNb_spinBox.setObjectName("ikNb_spinBox") + self.ikNb_horizontalLayout.addWidget(self.ikNb_spinBox) + self.gridLayout_4.addLayout(self.ikNb_horizontalLayout, 0, 0, 1, 1) + self.twistFalloff_horizontalLayout = QtWidgets.QHBoxLayout() + self.twistFalloff_horizontalLayout.setObjectName( + "twistFalloff_horizontalLayout" + ) + self.twistFalloff_label = QtWidgets.QLabel(self.splineIk_groupBox) + self.twistFalloff_label.setObjectName("twistFalloff_label") + self.twistFalloff_horizontalLayout.addWidget(self.twistFalloff_label) + self.twistFalloff_spinBox = QtWidgets.QDoubleSpinBox(self.splineIk_groupBox) + self.twistFalloff_spinBox.setMinimum(0.05) + self.twistFalloff_spinBox.setMaximum(2.0) + self.twistFalloff_spinBox.setSingleStep(0.05) + self.twistFalloff_spinBox.setProperty("value", 1.0) + self.twistFalloff_spinBox.setObjectName("twistFalloff_spinBox") + self.twistFalloff_horizontalLayout.addWidget(self.twistFalloff_spinBox) + self.gridLayout_4.addLayout(self.twistFalloff_horizontalLayout, 1, 0, 1, 1) + self.gridLayout.addWidget(self.splineIk_groupBox, 2, 0, 1, 1) + + self.retranslateUi(Form) + QtCore.QObject.connect( + self.ikfk_slider, + QtCore.SIGNAL("sliderMoved(int)"), + self.ikfk_spinBox.setValue, + ) + QtCore.QObject.connect( + self.ikfk_spinBox, + QtCore.SIGNAL("valueChanged(int)"), + self.ikfk_slider.setValue, + ) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + Form.setWindowTitle(pyqt.fakeTranslate("Form", "Form", None, -1)) + self.mode_label.setText(pyqt.fakeTranslate("Form", "Mode:", None, -1)) + self.mode_comboBox.setItemText(0, pyqt.fakeTranslate("Form", "FK", None, -1)) + self.mode_comboBox.setItemText(1, pyqt.fakeTranslate("Form", "IK", None, -1)) + self.mode_comboBox.setItemText(2, pyqt.fakeTranslate("Form", "FK/IK", None, -1)) + self.ikfk_label.setText(pyqt.fakeTranslate("Form", "IK/FK Blend:", None, -1)) + self.neutralPose_checkBox.setText( + pyqt.fakeTranslate("Form", "Neutral pose", None, -1) + ) + self.ikRefArray_groupBox.setTitle( + pyqt.fakeTranslate("Form", "IK Reference Array", None, -1) + ) + self.ikRefArrayAdd_pushButton.setText( + pyqt.fakeTranslate("Form", "<<", None, -1) + ) + self.ikRefArrayRemove_pushButton.setText( + pyqt.fakeTranslate("Form", ">>", None, -1) + ) + self.splineIk_groupBox.setTitle( + pyqt.fakeTranslate("Form", "Spline IK", None, -1) + ) + self.ikNb_label.setText(pyqt.fakeTranslate("Form", "IK Controls:", None, -1)) + self.twistFalloff_label.setText( + pyqt.fakeTranslate("Form", "Twist Falloff", None, -1) + ) diff --git a/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui new file mode 100644 index 00000000..05759189 --- /dev/null +++ b/release/scripts/mgear/shifter_epic_components/EPIC_chain_spline_IK_FK_01/settingsUI.ui @@ -0,0 +1,247 @@ + + + Form + + + + 0 + 0 + 255 + 290 + + + + Form + + + + + + + + + + + + + + + + Mode: + + + + + + + + 0 + 0 + + + + + FK + + + + + IK + + + + + FK/IK + + + + + + + + IK/FK Blend: + + + + + + + + + + 0 + 15 + + + + 100 + + + Qt::Horizontal + + + + + + + 100 + + + + + + + + + + + Nuetral pose + + + + + + + + + + + + IK Reference Array + + + + + + + + + + true + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + false + + + + + + + + + + + << + + + + + + + >> + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + Spline IK + + + + + + + + IK Controls: + + + + + + + 2 + + + 999 + + + 3 + + + + + + + + + + + + + + ikfk_slider + sliderMoved(int) + ikfk_spinBox + setValue(int) + + + 144 + 47 + + + 220 + 45 + + + + + ikfk_spinBox + valueChanged(int) + ikfk_slider + setValue(int) + + + 222 + 37 + + + 182 + 37 + + + + +