Skip to content

Commit

Permalink
Add target and end effector offset to improve solver stability
Browse files Browse the repository at this point in the history
  • Loading branch information
Stermere committed Oct 15, 2024
1 parent 56da427 commit 073eb71
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import dev.slimevr.tracking.processor.Constraint.Companion.ConstraintType
import dev.slimevr.tracking.trackers.Tracker
import io.github.axisangles.ktmath.Quaternion
import io.github.axisangles.ktmath.Vector3
import kotlin.math.*
import kotlin.math.pow

/*
* This class implements a chain of Bones
Expand Down Expand Up @@ -40,29 +40,26 @@ class IKChain(
*/
private fun prepBones() {
for (i in 0..<bones.size) {
if (bones[i].rotationConstraint.constraintType != ConstraintType.COMPLETE && bones[i].boneType.bodyPart !in IKSolver.LOCK_ROTATION) {
if (bones[i].rotationConstraint.constraintType != ConstraintType.COMPLETE) {
bones[i].setRotationRaw(rotations[i])
}
}
}

fun backwardsCCDIK(useConstraints: Boolean) {
target = computedTailPosition?.getPosition() ?: getChildTargetAvg()
var offset = Vector3.NULL

for (i in bones.size - 1 downTo 0) {
val currentBone = bones[i]

if (currentBone.boneType.bodyPart in IKSolver.LOCK_ROTATION) {
rotations[i] = currentBone.getGlobalRotation()
continue
}

// Get the local position of the end effector and the target relative to the current node
val endEffectorLocal = (getEndEffectorsAvg() - currentBone.getPosition()).unit()
val targetLocal = (target - currentBone.getPosition()).unit()
val endEffectorLocal = ((getEndEffectorsAvg() - offset) - currentBone.getPosition()).unit()
val targetLocal = ((target - offset) - currentBone.getPosition()).unit()

// Compute the axis of rotation and angle for this bone
val scalar = IKSolver.DAMPENING_FACTOR * if (currentBone.rotationConstraint.allowModifications) 1f else IKSolver.STATIC_DAMPENING
var scalar = IKSolver.DAMPENING_FACTOR * if (currentBone.rotationConstraint.hasTrackerRotation) IKSolver.STATIC_DAMPENING else 1f
scalar *= ((bones.size - i).toFloat() / bones.size).pow(IKSolver.ANNEALING_EXPONENT)
var adjustment = Quaternion.fromTo(endEffectorLocal, targetLocal).pow(scalar).unit()

// Bones that are not supposed to be modified should tend towards their origin
Expand All @@ -74,6 +71,10 @@ class IKChain(
val correctedRot = (adjustment * rotation).unit()

rotations[i] = setBoneRotation(currentBone, correctedRot, useConstraints)

if (currentBone.rotationConstraint.hasTrackerRotation) {
offset += rotations[i].sandwich(Vector3.NEG_Y) * currentBone.length
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,17 @@ import solarxr_protocol.datatypes.BodyPart
/*
* Implements CCDIK (Cyclic Coordinate Descent Inverse Kinematics) to allow
* positional trackers such as vive/tundra trackers to be used in conjunction with
* IMU trackers
* IMU only trackers
*/

class IKSolver(private val root: Bone) {
companion object {
const val TOLERANCE_SQR = 1e-8 // == 0.01 cm
const val MAX_ITERATIONS = 20
const val ANNEALING_EXPONENT = 1
const val DAMPENING_FACTOR = 0.5f
const val STATIC_DAMPENING = 0.1f
const val CORRECTION_FACTOR = 0.1f

// Short limbs positioned on the end of the skeleton are prone to over rotation
val LOCK_ROTATION = setOf(BodyPart.LEFT_HAND, BodyPart.RIGHT_HAND, BodyPart.LEFT_FOOT, BodyPart.RIGHT_FOOT)
const val STATIC_DAMPENING = 0.5f
const val CORRECTION_FACTOR = 0.01f
}

var enabled = true
Expand Down

0 comments on commit 073eb71

Please sign in to comment.