TwoBoneIKSolver stretchy and soft IK

Roy NieterauRoy Nieterau Posts: 258 ✭✭✭
edited February 2016 in Kraken Rigging Framework

I took a look at the Leg components that come with Kraken and noticed the stretchy + soft IK attributes on the leg component. Upon building the rig of this component these attributes are available but don't seem to do anything.

Looking at the TwoBoneIKSolver.kl it doesn't compute anything other than the IK solve and global scale. Is this something that's still on the to-do list to be implemented or am I doing something wrong?

Comments

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    Yes it's on the to list. I started working on the Canvas graph conversion last week but switched to other more pressing tasks.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭

    I'll be working on a stretchy soft-IK with elbow slide and pinning.

    I was also thinking about adding a "twist divider" along the arms to make a smooth twist from elbow to the wrist. Though I thought about maybe making that a separate component.

    The final goal is to also have it "bendy" where it can autobend when angled. I'm looking into porting our regular maya rigs over.

    Will post here when there's some progress.

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    Great @Roy Nieterau!

    My suggestion is to try to keep things as modular as possible to maximize re-usability of components. If you're implementing a bendy arm / leg / limb I'd make that a separate component as realistic arms legs won't need this or the overhead it may also incur. Though it depends on design.

    Eager to see where you go with this.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭
    edited February 2016

    @EricT Ok, I got most of it going now except for the Soft IK; that's still on the to-do list (bit busy at work so only finding myself at home with a couple of minutes to spare).

    Anyway, here's what I have so far as a solver (based on the TwoBoneIkSolver from Kraken):

    // Solve
    function TwoBoneStretchyIKSolver.solve!
    (
      Boolean drawDebug,
      Scalar rigScale,
      Boolean rightSide,
    
      Scalar ikblend,
      Boolean softIK,
      Scalar softDist,
      Boolean stretch,
      Scalar stretchBlend,
      Scalar slide,
      Scalar pin,
    
      Mat44 root,
      Mat44 bone0FK,
      Mat44 bone1FK,
      Mat44 ikHandle,
      Mat44 upV,
    
      Scalar bone0Len,
      Scalar bone1Len,
      io Mat44 bone0Out,
      io Mat44 bone1Out,
      io Mat44 bone2Out
    ){
    
      Xfo bone0FkXfo = Xfo(bone0FK);
      Xfo bone1FkXfo = Xfo(bone1FK);
      Xfo bone0Xfo = bone0FkXfo;
      Xfo bone1Xfo = bone1FkXfo;
      Xfo bone2Xfo;
    
      // TODO: Soft IK
      // TODO: Fix FK scale having influence on IK output
      // TODO: Fix FK scale functionality in blend
      // TODO: Fix FK bone0 translate (could lock this in the component?)
    
      Scalar outBone0Len = bone0Len;
      Scalar outBone1Len = bone1Len;
    
      // Scale to global rig scale
      Scalar scaledBone0Len = bone0Len * rigScale;
      Scalar scaledBone1Len = bone1Len * rigScale;
    
      if(ikblend > 0.0)
      {
    
        Vec3 ikHandlePos = ikHandle.translation();
        Vec3 rootPos = bone0Xfo.tr;
        Scalar distanceToIK = (rootPos - ikHandlePos).length();
    
        // Lock mid to the upVector (pole vector)
        // e.g. could be used to lock an elbow on a table
        if (pin > 0.0)
        {
            Vec3 pinPt = upV.translation();
            scaledBone0Len = Math_linearInterpolate(scaledBone0Len, (pinPt - rootPos).length(), pin);
            scaledBone1Len = Math_linearInterpolate(scaledBone1Len, (pinPt - ikHandlePos).length(), pin);
    
            outBone0Len = scaledBone0Len / rigScale;
            outBone1Len = scaledBone1Len / rigScale;
        }
    
        if (pin != 1.0)
        {
    
          // TODO: Allow scale of zero and the evaluation to finish
          Scalar chainLen = scaledBone0Len + scaledBone1Len;
          if (chainLen == 0) return;
    
          // Slide mid to end (+1) or to start (-1)
          if (slide != 0.0)
          {
            Scalar shift;
            if (slide > 0.0)
              shift = slide * scaledBone1Len;
            else
              shift = slide * scaledBone0Len;
    
            // Update the bone lengths
            scaledBone0Len += shift;
            scaledBone1Len -= shift;
            outBone0Len = scaledBone0Len / rigScale;
            outBone1Len = scaledBone1Len / rigScale;
          }
    
          // Soft IK scale
          if (softIK)
          {
            // For this we drag the IK handle behind after the softDist
            // See: http://www.softimageblog.com/archives/108
            // ikHandle to root
            if (distanceToIK > (chainLen * (1 - softDist)))
            { 
                Scalar softPos = chainLen - softDist * exp(-(distanceToIK - (chainLen - softDist)) / softDist);
                // TODO!!
            }
          }
    
          // Stretchy
          if (stretch && stretchBlend > 0.0)
          {
              if (softIK)
              {
                // Scale by ratio between IK and Soft IK so that
                // we preserve the soft IK motion while hitting the
                // IK handle.
                // See: http://www.softimageblog.com/archives/109
                // TODO!!
              }
    
              if (chainLen < distanceToIK)
              {
                  Scalar diff = distanceToIK / chainLen;
                  diff = Math_linearInterpolate(1.0, diff, stretchBlend);
    
                  scaledBone0Len *= diff;
                  scaledBone1Len *= diff;
                  outBone0Len *= diff;
                  outBone1Len *= diff;
              }
    
          }
        }
    
        solve2BoneIK(
          scaledBone0Len,
          scaledBone1Len,
          root.translation(),
          upV.translation(),
          ikHandlePos,
          bone0Xfo,
          bone1Xfo
        );
    
        // IK/FK Blend
        if (ikblend < 1.0)
        {
          bone0Xfo.ori = bone0FkXfo.ori.sphericalLinearInterpolate(bone0Xfo.ori, ikblend);
          bone1Xfo.tr = bone0Xfo.transformVector(Vec3(outBone0Len, 0.0, 0.0));
          bone1Xfo.ori = bone1FkXfo.ori.sphericalLinearInterpolate(bone1Xfo.ori, ikblend);
        }
      }
    
      // project bone2 to the end of bone 1
      bone2Xfo = bone1Xfo;
      bone2Xfo.tr = bone1Xfo.transformVector(Vec3(outBone1Len, 0.0, 0.0));
    
      // Set scaling
      bone0Xfo.sc = Vec3(scaledBone0Len / (rigScale * bone0Len), rigScale, rigScale);
      bone1Xfo.sc = Vec3(scaledBone1Len / (rigScale * bone1Len), rigScale, rigScale);
      bone2Xfo.sc = Vec3(rigScale, rigScale, rigScale);
    
      bone0Out = bone0Xfo.toMat44();
      bone1Out = bone1Xfo.toMat44();
      bone2Out = bone2Xfo.toMat44();
    
      // Set debugging visibility.
      this.setDebug(drawDebug);
      if(this.drawDebug){
    
        Color boneColor(1.0, 1.0, 0);
        etDrawBone(this.handle.rootTransform, 'bone0', bone0Xfo, scaledBone0Len, scaledBone0Len * 0.15, boneColor);
        etDrawBone(this.handle.rootTransform, 'bone1', bone1Xfo, scaledBone1Len, scaledBone1Len * 0.15, boneColor);
      }
    }
    

    Note that this is a WIP implementation; also see todo in the code.

    I also noticed some strange behavior with the solver in Kraken how scaling the FK controls influences the output chain (even with IK blend on 1.0). So also note that in the todo. Shouldn't be too hard to find a solution; I think the issue is coming from the fact that the second bone is moved to the end of the length of the first bone using its Xfo.transformVector, but if the first was already scaled it would get double transformations.

    So far what's working:

    • Stretchy
    • Pinning of midpoint (e.g. useful for when character rest with elbow on a table)
    • Sliding of midpoint (based on a ratio of the lengths: -1.0 to start and 1.0 to end)
  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    This is great @Roy Nieterau!

    Could I suggest creating a fork & branch on the Kraken git repo so that I can merge this in eventually and we have version control on this? :)

    Yes FK influencing IK when at 1.0 is a bug. Need to fix that. My idea for that is that we should have an FK bone01 length and an IK bone01 length, solve the pose for FK and IK then do the blend. Will probably give a better result.

    Any reason you're going pure KL for this instead of DFG?

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭

    Could I suggest creating a fork & branch on the Kraken git repo so that I can merge this in eventually and we have version control on this?

    Sure, once it's got some cool basics and feels stable I'll try to contribute along with a component.

    Yes FK influencing IK when at 1.0 is a bug. Need to fix that. My idea for that is that we should have an FK bone01 length and an IK bone01 length, solve the pose for FK and IK then do the blend. Will probably give a better result.

    Sure, that's what I was thinking as well. Just need to kind of get my brain going on what's the most efficient without getting into tons of variables. Getting "FK" data and "IK" data and just blending when required would always work, but maybe we could get away with less data at some point.

    Any reason you're going pure KL for this instead of DFG?

    I wasn't too sure yet how to build Kraken solvers using the DFG. :')
    For now this was "easiest" for me.

    Might port to DFG down the line to get some "caching" in there for the more static parts (e.g. length computations), though not sure how much it'd help in this particular scenario.

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    All sounds good. For the DFG, take a look at the simple DirectionConstraintSolver we have in the default lib (the only DFG so far). It's super easy and isn't quite as strict as the KL ones since the DFG can't require a particular Interface is implemented since, well it's just a node. drawDebug and rigScale are still required as ports though.

    There is nothing wrong with KL based solvers btw. In some instances it's better. Like when you have an IF statement and don't want to call a calculation unless a condition is met. I haven't figured out how this will work just yet and may need to wait for some execute port updates.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭

    There is nothing wrong with KL based solvers btw. In some instances it's better. Like when you have an IF statement and don't want to call a calculation unless a condition is met. I haven't figured out how this will work just yet and may need to wait for some execute port updates.

    I think I tested this some time ago in Canvas. But I think using a Logic.If node in Canvas actually does something very similar where you're able to bypass a part of the graph because it won't pull from the graph for which the condition isn't true. To be honest it's somewhat hard to figure out whether that's actually functioning the way you want outside of spamming report nodes into the graph. But I think it actually worked. It's just that the graph doesn't necessarily visualize it as clearly as a if {} block in code.

  • AhmidouAhmidou Posts: 179 ✭✭

    I think it does, from my understanding canvas graphs are converted in pure KL before being compiled.
    Can someone correct me if I'm wrong?

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    It's more that if I have a preset that is called "SolveIK" that sets variable values. I want to plug this into an IF node if a condition is true. However, how do I do this? There isn't a way to set variables unless it's plugged in to the right hand side of the graph so this is the situation I'm speaking about.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭
    edited February 2016

    To post some more of the progress after another hour of spare time, woohoo!

    The following features are now functioning:

    • Midpoint pinning (0 to 1 blend)
    • Midpoint sliding (-1 to start; 0 is default mid; 1 to end)
    • Stretchy (including 0 to 1 blend)
    • Soft IK (both stretchy and non stretchy, including correctly functioning in its transition)
    • Two Bone IK solving under these conditions
    • FK solving
    • FK scale is not breaking the IK (bugfix)

    All these features seem to functioning correctly in my first couple of tests even when used in mixed ways. (Though not too sure about how the "soft IK" behaves along with "pinning", but surely those might be "edge cases" anyway).

    Also had a look at the FK/IK switch issues with the scale which I resolved mostly by just solving IK separately from FK and blending the end results. Though once I started testing it I actually did it the wrong way. As opposed to blending the translation as I wrongly tried here I should be blending the length of each bone and use the transform vectors method. (Is there an easy way to transform a vector with a Matrix as if the matrix had a scale of 1, 1, 1? Or would I need to make a copy of the matrix and set it myself?)

    As before, here's the current solver code: http://pastebin.com/QvYMAS2C
    Had to use PasteBin otherwise I got a "post body too long" error on the forums.

    As you can see the solving of FK and IK is a bit more explicit now.
    Also I tried to comment it "thoroughly" so it's somewhat understandable to anyone who didn't get the complete ins- and outs of what it's doing.

    Getting closer... ;)

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭
    edited February 2016

    Actually, just before I posted that code I messed up as I was cleaning it. Derp! ;) Since I tried to remove the number of calls I didn't set the Bone0Xfo to the Bone0FkXfo anymore and as such the root position for IK solving didn't get resolved.

    Anyhow, easy to fix but hard to track down.

    • Got if fixed here locally now.
    • Also had a look at blending to "pin" with soft IK on and it behaved odd, so I also fixed that.
    • And reduced number of variables a bit. (Was a bit odd keeping track of both "scaled" and non scaled chain at the same time, solved that!)

    Just need to fix the IK/FK blend still and I guess then I'll start committing to my fork of Kraken.

  • Paul DoylePaul Doyle Administrator, Fabric Employee Posts: 229 admin

    @Roy Nieterau I've increased the comment character count, so you should be able to paste your code directly again. Let me know if you're still having trouble.

    CEO at Fabric Software
    Twitter

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    Great stuff @Roy Nieterau,

    This helps out a lot actually. I was planning on fleshing this component out but you're helping out with that. I really appreciate it. This should be merged in as an update to the TwoBoneIKSolver.kl. We can update the arm and leg components too.

    Thanks again!

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭
    edited February 2016

    @EricT sure, no problem.

    So I fixed the IK/FK blending issues and finally got the whole thing working. In total it still took me about a full days of work (though I did it in nightly hours at home) to get the whole algorithm down plus kind of getting the hang of building a Kraken component. Even though in this case I was just building on top of an existing example.

    Just pushed it to my fork of Kraken into a StretchyIK branch and here is the commit that adds the component and solver.

    As stated in the commit it has functioning stretchy blend, midpoint sliding and pinning, IK/FK blend plus a soft IK (both stretchy and non-stretchy) which all seem to be functioning correctly in the quick tests that I did.

    Though I did have an additional question about the default rigScale attribute any component should have? What is its intention? Should the component scale in a uniform manner? If so, from what point? Or what is its intention to actually be a "default input"? I'm pretty sure it's not functioning in any expected manner in this particular component. :smile:

    Also note that I abstracted away the terms like "ankle", "shin" or alike into start, mid and end or sometimes "upper" and "lower". This way it's not confusing when using for either arm or leg, or something else.

    --

    @Paul Doyle Thanks for changing that limit, will see next time if the message doesn't pop up.

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    @Roy Nieterau Awesome I'll have a look at the commit.

    Yes rigScale should be implemented to allow the component / solver to scale properly with a full rig's scale attribute. It's hard to tell someone how it should work for custom components but basically you need to scale all relevant Scalar input values by this rigScale input value. You'll also have to apply scaling to any other math objects that may need it too. The scaling should propagate through the rig's control layer because of the connections from the mainSRT component down through all the rest of the components. The mainSRT has a rigScale attribute that should be plugged into all components (yes need to work on this to be able to just have an IO port on each component that can handle this). Between the inherited scaling through the control layer and the rigScale value, the solvers should function properly.

    Great that you removed the ankle, shin, etc. I was planning to move the arm / leg component into a generalized "limb" component at some point and have some string attributes that you fill in on the nodes for naming these parts.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭

    Yes rigScale should be implemented to allow the component / solver to scale properly with a full rig's scale attribute. It's hard to tell someone how it should work for custom components but basically you need to scale all relevant Scalar input values by this rigScale input value. You'll also have to apply scaling to any other math objects that may need it too. The scaling should propagate through the rig's control layer because of the connections from the mainSRT component down through all the rest of the components. The mainSRT has a rigScale attribute that should be plugged into all components (yes need to work on this to be able to just have an IO port on each component that can handle this). Between the inherited scaling through the control layer and the rigScale value, the solvers should function properly.

    Sure. The idea is somewhat confusing since I'd imagine the component to scale along with the SRT of its parent once that gets "scaled" so it wouldn't need an additional "rigScale", but I can see that one might want to "compensate" other areas like the IK solver and alike so it knows what to do. Even though that is likely only a thing with dynamic solvers or anything really behaving differently under another size.

    But for an FK chain of two bones the we scale them up by rigScale and move the bones down the chain further away from its parent (as if it was parented, etc.) Then the question is, what happens to the controls. I think that's what I have now in this particular component. The rig solver 'scales' in the computation, but the controls stay pretty much the same in the scene.

    Once I see rigScale being used somewhere I can probably figure out what I'd need to with a component to make it work.

    Other than that... let me know when you had a look at it and when/if I should do a Pull Request.

  • Roy NieterauRoy Nieterau Posts: 258 ✭✭✭

    @EricT have you been able to take a look at this? Shall I set up a Pull Request?

  • EricTEricT Administrator, Moderator, Fabric Employee Posts: 305 admin

    I haven't yet. Open a pull request and I'll have a look when I have time. I just need to swap out / update the leg / arm components to use the new version as they require new params and possible controls.

    Eric Thivierge
    Kraken Developer
    Kraken Rigging Framework

Sign In or Register to comment.