@@ -24,8 +24,6 @@ class TrackerResetsHandler(val tracker: Tracker) {
2424 Math .PI .toFloat(),
2525 0f ,
2626 ).toQuaternion()
27- private val QuarterPitch = Quaternion .rotationAroundXAxis(FastMath .HALF_PI )
28-
2927 private var driftAmount = 0f
3028 private var averagedDriftQuat = Quaternion .IDENTITY
3129 private var rotationSinceReset = Quaternion .IDENTITY
@@ -54,6 +52,16 @@ class TrackerResetsHandler(val tracker: Tracker) {
5452
5553 // Reference adjustment quats
5654
55+ /* *
56+ * Gyro fix is set by full reset. This sets the current y rotation to 0, correcting
57+ * for initial yaw rotation and the rotation incurred by mounting orientation. This
58+ * is a local offset in rotation and does not affect the axes of rotation.
59+ *
60+ * This rotation is only used to compute [attachmentFix], otherwise [yawFix] would
61+ * correct for the same rotation.
62+ */
63+ private var gyroFix = Quaternion .IDENTITY
64+
5765 /* *
5866 * Attachment fix is set by full reset. This sets the current x and z rotations to
5967 * 0, correcting for initial pitch and roll rotation. This is a global offset in
@@ -180,9 +188,12 @@ class TrackerResetsHandler(val tracker: Tracker) {
180188 /* *
181189 * Get the reference adjusted accel.
182190 */
183- // All IMU axis corrections are inverse to undo `adjustToReference` after local yaw offsets are added
184- // Order is VERY important here! Please be extremely careful! >~>
185- fun getReferenceAdjustedAccel (rawRot : Quaternion , accel : Vector3 ): Vector3 = (adjustToReference(rawRot) * (attachmentFix * mountingOrientation * mountRotFix * tposeDownFix).inv ()).sandwich(accel)
191+ // TODO: Make this actually adjusted to the corrected IMU heading. The current
192+ // implementation for heading correction doesn't appear to be correct and may simply
193+ // make acceleration worse, so I'm just leaving this until we work that out. The
194+ // output of this will be world space, but with an unknown offset to heading (yaw).
195+ // - Butterscotch
196+ fun getReferenceAdjustedAccel (rawRot : Quaternion , accel : Vector3 ): Vector3 = rawRot.sandwich(accel)
186197
187198 /* *
188199 * Converts raw or filtered rotation into reference- and
@@ -191,17 +202,22 @@ class TrackerResetsHandler(val tracker: Tracker) {
191202 */
192203 private fun adjustToReference (rotation : Quaternion ): Quaternion {
193204 var rot = rotation
194- // Correct for global pitch/roll offset
195- rot * = attachmentFix
196- // Correct for global yaw offset without affecting local yaw so we can change this
197- // later without invalidating local yaw offset corrections
205+ // Align heading axis with bone space
198206 if (! tracker.isHmd || tracker.trackerPosition != TrackerPosition .HEAD ) {
199- rot = mountingOrientation. inv () * rot * mountingOrientation
207+ rot * = mountingOrientation
200208 }
201- rot = mountRotFix.inv () * rot * mountRotFix
202- // T-pose global correction
209+ // Heading correction assuming manual orientation is correct
210+ rot = gyroFix * rot
211+ // Align attitude axes with bone space
212+ rot * = attachmentFix
213+ // Secondary heading axis alignment with bone space for automatic mounting
214+ // Note: Applying an inverse amount of heading correction corresponding to the
215+ // axis alignment quaternion will leave the correction to another variable
216+ rot = mountRotFix.inv () * (rot * mountRotFix)
217+ // More attitude axes alignment specifically for the t-pose configuration, this
218+ // probably shouldn't be a separate variable from attachmentFix?
203219 rot * = tposeDownFix
204- // Align local yaw with reference
220+ // More heading correction
205221 rot = yawFix * rot
206222 rot = constraintFix * rot
207223 return rot
@@ -211,6 +227,8 @@ class TrackerResetsHandler(val tracker: Tracker) {
211227 * Converts raw or filtered rotation into zero-reference-adjusted by
212228 * applying quaternions produced after full reset and yaw reset only
213229 */
230+ // This is essentially just adjustToReference but aligning to quaternion identity
231+ // rather than to the bone.
214232 private fun adjustToIdentity (rotation : Quaternion ): Quaternion {
215233 var rot = rotation
216234 rot = gyroFixNoMounting * rot
@@ -265,23 +283,15 @@ class TrackerResetsHandler(val tracker: Tracker) {
265283 lastResetQuaternion = oldRot
266284
267285 // Adjust raw rotation to mountingOrientation
268- val rotation = tracker.getRawRotation()
286+ val mountingAdjustedRotation = tracker.getRawRotation() * mountingOrientation
269287
270288 // Gyrofix
271- val gyroFix = if (tracker.allowMounting || (tracker.trackerPosition == TrackerPosition .HEAD && ! tracker.isHmd)) {
272- if (tracker.isComputed) {
273- fixGyroscope(rotation )
289+ if (tracker.allowMounting || (tracker.trackerPosition == TrackerPosition .HEAD && ! tracker.isHmd)) {
290+ gyroFix = if (tracker.isComputed) {
291+ fixGyroscope(tracker.getRawRotation() )
274292 } else {
275- if (tracker.trackerPosition.isFoot()) {
276- // Feet are rotated by 90 deg pitch, this means we're relying on IMU rotation
277- // to be set correctly here.
278- fixGyroscope(rotation * tposeDownFix * QuarterPitch )
279- } else {
280- fixGyroscope(rotation * tposeDownFix)
281- }
293+ fixGyroscope(mountingAdjustedRotation * tposeDownFix)
282294 }
283- } else {
284- Quaternion .IDENTITY
285295 }
286296
287297 // Mounting for computed trackers
@@ -296,15 +306,15 @@ class TrackerResetsHandler(val tracker: Tracker) {
296306 if (resetHmdPitch) {
297307 // Reset the HMD's pitch if it's assigned to head and resetHmdPitch is true
298308 // Get rotation without yaw (make sure to use the raw rotation directly!)
299- val rotBuf = getYawQuaternion(rotation) .inv () * rotation
309+ val rotBuf = getYawQuaternion(tracker.getRawRotation()) .inv () * tracker.getRawRotation()
300310 // Isolate pitch
301311 Quaternion (rotBuf.w, - rotBuf.x, 0f , 0f ).unit()
302312 } else {
303313 // Don't reset the HMD at all
304314 Quaternion .IDENTITY
305315 }
306316 } else {
307- (gyroFix * rotation). inv ( )
317+ fixAttachment(mountingAdjustedRotation )
308318 }
309319
310320 // Rotate attachmentFix by 180 degrees as a workaround for t-pose (down)
@@ -316,7 +326,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
316326
317327 // Don't adjust yaw if head and computed
318328 if (tracker.trackerPosition != TrackerPosition .HEAD || ! tracker.isComputed) {
319- yawFix = gyroFix * reference.project( Vector3 . POS_Y ).unit( )
329+ yawFix = fixYaw(mountingAdjustedRotation, reference)
320330 tracker.yawResetSmoothing.reset()
321331 }
322332
@@ -360,7 +370,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
360370 lastResetQuaternion = oldRot
361371
362372 val yawFixOld = yawFix
363- yawFix = fixYaw(tracker.getRawRotation(), reference)
373+ yawFix = fixYaw(tracker.getRawRotation() * mountingOrientation , reference)
364374 tracker.yawResetSmoothing.reset()
365375
366376 makeIdentityAdjustmentQuatsYaw()
@@ -399,9 +409,9 @@ class TrackerResetsHandler(val tracker: Tracker) {
399409 constraintFix = Quaternion .IDENTITY
400410
401411 // Get the current calibrated rotation
402- var rotBuf = adjustToDrift(tracker.getRawRotation())
412+ var rotBuf = adjustToDrift(tracker.getRawRotation() * mountingOrientation)
413+ rotBuf = gyroFix * rotBuf
403414 rotBuf * = attachmentFix
404- rotBuf = mountingOrientation.inv () * rotBuf * mountingOrientation
405415 rotBuf = yawFix * rotBuf
406416
407417 // Adjust buffer to reference
@@ -457,22 +467,14 @@ class TrackerResetsHandler(val tracker: Tracker) {
457467 mountRotFix = Quaternion .IDENTITY
458468 }
459469
460- // EulerOrder.YXZ is actually better for gyroscope fix, as it can get yaw at any roll.
461- // Consequentially, instead of the roll being limited, the pitch is limited to
462- // 90 degrees from the yaw plane. This means trackers may be mounted upside down
463- // or with incorrectly configured IMU rotation, but we will need to compensate for
464- // the pitch.
465- private fun fixGyroscope (sensorRotation : Quaternion ): Quaternion = getYawQuaternion(sensorRotation, EulerOrder .YXZ ).inv ()
470+ private fun fixGyroscope (sensorRotation : Quaternion ): Quaternion = getYawQuaternion(sensorRotation).inv ()
471+
472+ private fun fixAttachment (sensorRotation : Quaternion ): Quaternion = (gyroFix * sensorRotation).inv ()
466473
467474 private fun fixYaw (sensorRotation : Quaternion , reference : Quaternion ): Quaternion {
468- var rot = sensorRotation * attachmentFix
469- // We need to fix the global yaw offset for the euler yaw calculation
470- if (! tracker.isHmd || tracker.trackerPosition != TrackerPosition .HEAD ) {
471- rot = mountingOrientation.inv () * rot * mountingOrientation
472- }
473- rot = mountRotFix.inv () * rot * mountRotFix
474- // TODO: Get diff from ref to rot, use euler angle (YZX) yaw as output.
475- // This prevents pitch and roll from affecting the alignment.
475+ var rot = gyroFix * sensorRotation
476+ rot * = attachmentFix
477+ rot = mountRotFix.inv () * (rot * mountRotFix)
476478 rot = getYawQuaternion(rot)
477479 return rot.inv () * reference.project(Vector3 .POS_Y ).unit()
478480 }
@@ -483,7 +485,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
483485 // In both cases, the isolated yaw value changes
484486 // with the tracker's roll when pointing forward.
485487 // calling twinNearest() makes sure this rotation has the wanted polarity (+-).
486- private fun getYawQuaternion (rot : Quaternion , order : EulerOrder = EulerOrder . YZX ): Quaternion = EulerAngles (order , 0f , rot.toEulerAngles(order ).y, 0f ).toQuaternion().twinNearest(rot)
488+ private fun getYawQuaternion (rot : Quaternion ): Quaternion = EulerAngles (EulerOrder . YZX , 0f , rot.toEulerAngles(EulerOrder . YZX ).y, 0f ).toQuaternion().twinNearest(rot)
487489
488490 private fun makeIdentityAdjustmentQuatsFull () {
489491 val sensorRotation = tracker.getRawRotation()
0 commit comments