Computed head reset (#1057)

This commit is contained in:
Erimel
2024-07-31 23:05:34 -04:00
committed by GitHub
parent 58ca3fe8c1
commit af4f9a96bf
15 changed files with 140 additions and 92 deletions

View File

@@ -355,6 +355,9 @@ settings-general-fk_settings-leg_fk-reset_mounting_feet = Feet Mounting Reset
settings-general-fk_settings-arm_fk = Arm tracking
settings-general-fk_settings-arm_fk-description = Force arms to be tracked from the headset (HMD) even if positional hand data is available.
settings-general-fk_settings-arm_fk-force_arms = Force arms from HMD
settings-general-fk_settings-reset_settings = Reset settings
settings-general-fk_settings-reset_settings-reset_hmd_pitch-description = Reset the HMD's pitch (vertical rotation) upon doing a full reset. Useful if wearing an HMD on the forehead for VTubing or mocap. Do not enable for VR.
settings-general-fk_settings-reset_settings-reset_hmd_pitch = Reset HMD pitch
settings-general-fk_settings-arm_fk-reset_mode-description = Change which arm pose is expected for mounting reset.
settings-general-fk_settings-arm_fk-back = Back
settings-general-fk_settings-arm_fk-back-description = The default mode, with the upper arms going back and lower arms going forward.

View File

@@ -96,6 +96,7 @@ interface SettingsForm {
armsMountingResetMode: number;
yawResetSmoothTime: number;
saveMountingReset: boolean;
resetHmdPitch: boolean;
};
}
@@ -158,6 +159,7 @@ const defaultValues: SettingsForm = {
armsMountingResetMode: 0,
yawResetSmoothTime: 0.0,
saveMountingReset: false,
resetHmdPitch: false,
},
};
@@ -294,6 +296,7 @@ export function GeneralSettings() {
values.resetsSettings.yawResetSmoothTime;
resetsSettings.saveMountingReset =
values.resetsSettings.saveMountingReset;
resetsSettings.resetHmdPitch = values.resetsSettings.resetHmdPitch;
settings.resetsSettings = resetsSettings;
}
@@ -859,24 +862,6 @@ export function GeneralSettings() {
)}
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetMountingFeet"
label={l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet'
)}
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
@@ -900,12 +885,53 @@ export function GeneralSettings() {
/>
</div>
<div className="flex flex-col pt-2">
<Typography bold>
{l10n.getString('settings-general-fk_settings-reset_settings')}
</Typography>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetHmdPitch"
label={l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch'
)}
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetMountingFeet"
label={l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet'
)}
/>
</div>
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-arm_fk-reset_mode-description'
)}
</Typography>
<div className="grid md:grid-cols-2 flex-col gap-3 pt-2">
<div className="grid md:grid-cols-2 flex-col gap-3 pt-2 pb-3">
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
@@ -951,6 +977,7 @@ export function GeneralSettings() {
value={'3'}
></Radio>
</div>
{config?.debug && (
<>
<div className="flex flex-col pt-2 pb-3">

View File

@@ -102,6 +102,8 @@ class VRServer @JvmOverloads constructor(
init {
// UwU
instance = this
configManager = ConfigManager(configPath)
configManager.loadConfig()
deviceManager = DeviceManager(this)
@@ -161,7 +163,6 @@ class VRServer @JvmOverloads constructor(
for (tracker in computedTrackers) {
registerTracker(tracker)
}
instance = this
}
fun hasBridge(bridgeClass: Class<out Bridge?>): Boolean {

View File

@@ -43,11 +43,12 @@ class ResetsConfig {
// Save automatic mounting reset calibration
var saveMountingReset = false
// Reset the HMD's pitch upon full reset
var resetHmdPitch = false
fun updateTrackersResetsSettings() {
for (t in VRServer.instance.allTrackers) {
if (t.needsReset) {
t.resetsHandler.readResetConfig(this)
}
t.resetsHandler.readResetConfig(this)
}
}
}

View File

@@ -101,8 +101,8 @@ class VRConfig {
val config = getTracker(tracker)
tracker.readConfig(config)
if (tracker.isImu()) tracker.resetsHandler.readDriftCompensationConfig(driftCompensation)
tracker.resetsHandler.readResetConfig(resetsConfig)
if (tracker.needsReset) {
tracker.resetsHandler.readResetConfig(resetsConfig)
tracker.saveMountingResetOrientation(config)
}
if (tracker.allowFiltering) {

View File

@@ -189,7 +189,8 @@ public class RPCSettingsBuilder {
humanPoseManager.getToggle(SkeletonConfigToggles.VIVE_EMULATION),
humanPoseManager.getToggle(SkeletonConfigToggles.TOE_SNAP),
humanPoseManager.getToggle(SkeletonConfigToggles.FOOT_PLANT),
humanPoseManager.getToggle(SkeletonConfigToggles.SELF_LOCALIZATION)
humanPoseManager.getToggle(SkeletonConfigToggles.SELF_LOCALIZATION),
false
);
int ratiosOffset = ModelRatios
.createModelRatios(
@@ -342,7 +343,8 @@ public class RPCSettingsBuilder {
resetsConfig.getResetMountingFeet(),
resetsConfig.getMode().getId(),
resetsConfig.getYawResetSmoothTime(),
resetsConfig.getSaveMountingReset()
resetsConfig.getSaveMountingReset(),
resetsConfig.getResetHmdPitch()
);
}

View File

@@ -337,6 +337,7 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
resetsConfig.resetMountingFeet = req.resetsSettings().resetMountingFeet()
resetsConfig.saveMountingReset = req.resetsSettings().saveMountingReset()
resetsConfig.yawResetSmoothTime = req.resetsSettings().yawResetSmoothTime()
resetsConfig.resetHmdPitch = req.resetsSettings().resetHmdPitch()
resetsConfig.updateTrackersResetsSettings()
}

View File

@@ -340,16 +340,6 @@ class HumanPoseManager(val server: VRServer?) {
@ThreadSafe
fun getComputedTracker(trackerRole: TrackerRole): Tracker = skeleton.getComputedTracker(trackerRole)
/**
* Returns all trackers if VRServer is non-null. Else, returns the
* skeleton's assigned trackers.
*
* @return a list of trackers to use for reset.
*/
val trackersToReset: List<Tracker?>
get() =
server?.allTrackers ?: skeleton.localTrackers
/**
* @return the head bone, which is the root of the skeleton
*/

View File

@@ -784,8 +784,8 @@ class HumanSkeleton(
}
/**
* Rotates the first Quaternion to match its yaw and roll to the rotation of
* the average of the second and third quaternions.
* Rotates the third Quaternion to match its yaw and roll to the rotation of
* the average of the first and second quaternions.
*
* @param leftKnee the first Quaternion
* @param rightKnee the second Quaternion
@@ -1033,7 +1033,8 @@ class HumanSkeleton(
*/
val isTrackingRightArmFromController: Boolean
get() = rightHandTracker != null && rightHandTracker!!.hasPosition && !forceArmsFromHMD
val localTrackers: List<Tracker?>
val trackersToReset: List<Tracker?>
get() = listOf(
neckTracker,
chestTracker,
@@ -1056,19 +1057,16 @@ class HumanSkeleton(
)
fun resetTrackersFull(resetSourceName: String?) {
val trackersToReset = humanPoseManager.trackersToReset
// Resets all axis of the trackers with the HMD as reference.
var referenceRotation = IDENTITY
headTracker?.let {
if (it.needsReset) {
it.resetsHandler.resetFull(referenceRotation)
} else {
referenceRotation = it.getRotation()
}
// Always reset the head (ifs in resetsHandler)
it.resetsHandler.resetFull(referenceRotation)
referenceRotation = it.getRotation()
}
// Resets all axes of the trackers with the HMD as reference.
for (tracker in trackersToReset) {
if (tracker != null && tracker.needsReset) {
// Only reset if tracker needsReset
if (tracker != null && (tracker.needsReset || tracker.isHmd)) {
tracker.resetsHandler.resetFull(referenceRotation)
}
}
@@ -1085,18 +1083,17 @@ class HumanSkeleton(
@VRServerThread
fun resetTrackersYaw(resetSourceName: String?) {
val trackersToReset = humanPoseManager.trackersToReset
// Resets the yaw of the trackers with the head as reference.
var referenceRotation = IDENTITY
headTracker?.let {
if (it.needsReset) {
// Only reset if head needsReset and isn't computed
if (it.needsReset && !it.isComputed) {
it.resetsHandler.resetYaw(referenceRotation)
} else {
referenceRotation = it.getRotation()
}
referenceRotation = it.getRotation()
}
for (tracker in trackersToReset) {
// Only reset if tracker needsReset
if (tracker != null && tracker.needsReset) {
tracker.resetsHandler.resetYaw(referenceRotation)
}
@@ -1107,19 +1104,17 @@ class HumanSkeleton(
@VRServerThread
fun resetTrackersMounting(resetSourceName: String?) {
val trackersToReset = humanPoseManager.trackersToReset
// Resets the mounting orientation of the trackers with the HMD as
// reference.
// Resets the mounting orientation of the trackers with the HMD as reference.
var referenceRotation = IDENTITY
headTracker?.let {
if (it.needsMounting) {
// Only reset if head needsMounting or is computed but not HMD
if (it.needsMounting || (it.isComputed && !it.isHmd)) {
it.resetsHandler.resetMounting(referenceRotation)
} else {
referenceRotation = it.getRotation()
}
referenceRotation = it.getRotation()
}
for (tracker in trackersToReset) {
// Only reset if tracker needsMounting
if (tracker != null && tracker.needsMounting) {
tracker.resetsHandler.resetMounting(referenceRotation)
}
@@ -1131,7 +1126,6 @@ class HumanSkeleton(
@VRServerThread
fun clearTrackersMounting(resetSourceName: String?) {
val trackersToReset = humanPoseManager.trackersToReset
headTracker?.let {
if (it.needsMounting) it.resetsHandler.clearMounting()
}

View File

@@ -323,7 +323,7 @@ class Tracker @JvmOverloads constructor(
_rotation
}
if (needsReset && !(isComputed && trackerPosition == TrackerPosition.HEAD)) {
if (needsReset || (isComputed && !isInternal)) {
// Adjust to reset, mounting and drift compensation
rot = resetsHandler.getReferenceAdjustedDriftRotationFrom(rot)
}
@@ -354,7 +354,7 @@ class Tracker @JvmOverloads constructor(
_rotation
}
if (needsReset && trackerPosition != TrackerPosition.HEAD) {
if (needsReset || (isComputed && trackerPosition == TrackerPosition.HEAD)) {
// Adjust to reset and mounting
rot = resetsHandler.getIdentityAdjustedDriftRotationFrom(rot)
}

View File

@@ -43,6 +43,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
private var yawResetSmoothTime = 0.0f
private lateinit var fpsTimer: NanoTimer
var saveMountingReset = false
var resetHmdPitch = false
var allowDriftCompensation = false
var lastResetQuaternion: Quaternion? = null
@@ -123,6 +124,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
fpsTimer = VRServer.instance.fpsTimer
}
saveMountingReset = config.saveMountingReset
resetHmdPitch = config.resetHmdPitch
}
fun trySetMountingReset(quat: Quaternion) {
@@ -155,7 +157,9 @@ class TrackerResetsHandler(val tracker: Tracker) {
*/
private fun adjustToReference(rotation: Quaternion): Quaternion {
var rot = rotation
rot *= mountingOrientation
if (!tracker.isHmd || tracker.trackerPosition != TrackerPosition.HEAD) {
rot *= mountingOrientation
}
rot = gyroFix * rot
rot *= attachmentFix
rot = mountRotFix.inv() * (rot * mountRotFix)
@@ -216,29 +220,57 @@ class TrackerResetsHandler(val tracker: Tracker) {
Quaternion.IDENTITY
}
// Old rot for drift compensation
val oldRot = adjustToReference(tracker.getRawRotation())
lastResetQuaternion = oldRot
// Adjust raw rotation to mountingOrientation
val mountingAdjustedRotation = tracker.getRawRotation() * mountingOrientation
if (tracker.needsMounting) {
gyroFix = fixGyroscope(mountingAdjustedRotation * tposeDownFix)
} else {
// Set mounting to the HMD's yaw so that the non-mounting-adjusted
// tracker goes forward.
// Gyrofix
if (tracker.needsMounting || (tracker.trackerPosition == TrackerPosition.HEAD && !tracker.isHmd)) {
gyroFix = if (tracker.isComputed) {
fixGyroscope(tracker.getRawRotation())
} else {
fixGyroscope(mountingAdjustedRotation * tposeDownFix)
}
}
// Mounting for computed trackers
if (tracker.isComputed && tracker.trackerPosition != TrackerPosition.HEAD) {
// Set mounting to the reference's yaw so that a computed
// tracker goes forward according to the head tracker.
mountRotFix = getYawQuaternion(reference)
}
attachmentFix = fixAttachment(mountingAdjustedRotation)
// Rotate attachmentFix by 180 degrees as a workaround for tpose (down)
// Attachment fix
attachmentFix = if (tracker.trackerPosition == TrackerPosition.HEAD && tracker.isHmd) {
if (resetHmdPitch) {
// Reset the HMD's pitch if it's assigned to head and resetHmdPitch is true
// Get rotation without yaw (make sure to use the raw rotation directly!)
val rotBuf = getYawQuaternion(tracker.getRawRotation()).inv() * tracker.getRawRotation()
// Isolate pitch
Quaternion(rotBuf.w, -rotBuf.x, 0f, 0f).unit()
} else {
// Don't reset the HMD at all
Quaternion.IDENTITY
}
} else {
fixAttachment(mountingAdjustedRotation)
}
// Rotate attachmentFix by 180 degrees as a workaround for t-pose (down)
if (tposeDownFix != Quaternion.IDENTITY && tracker.needsMounting) {
attachmentFix *= HalfHorizontal
}
makeIdentityAdjustmentQuatsFull()
yawFix = fixYaw(mountingAdjustedRotation, reference)
yawResetSmoothTimeRemain = 0.0f
// (don't adjust yaw if head and computed)
if (tracker.trackerPosition != TrackerPosition.HEAD || tracker.isComputed) {
yawFix = fixYaw(mountingAdjustedRotation, reference)
yawResetSmoothTimeRemain = 0.0f
}
calculateDrift(oldRot)
@@ -255,6 +287,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
* position should be corrected in the source.
*/
fun resetYaw(reference: Quaternion) {
// Old rot for drift compensation
val oldRot = adjustToReference(tracker.getRawRotation())
lastResetQuaternion = oldRot
@@ -286,9 +319,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
* and stores it in mountRotFix, and adjusts yawFix
*/
fun resetMounting(reference: Quaternion) {
if (!resetMountingFeet && isFootTracker()) {
return
}
if (!resetMountingFeet && isFootTracker()) return
// Get the current calibrated rotation
var rotBuf = adjustToDrift(tracker.getRawRotation() * mountingOrientation)
@@ -320,10 +351,8 @@ class TrackerResetsHandler(val tracker: Tracker) {
}
// Adjust for forward/back arms and thighs
val isLowerArmBack =
armsResetMode == ArmsResetModes.BACK && (isLeftLowerArmTracker() || isRightLowerArmTracker())
val isArmForward =
armsResetMode == ArmsResetModes.FORWARD && (isLeftArmTracker() || isRightArmTracker())
val isLowerArmBack = armsResetMode == ArmsResetModes.BACK && (isLeftLowerArmTracker() || isRightLowerArmTracker())
val isArmForward = armsResetMode == ArmsResetModes.FORWARD && (isLeftArmTracker() || isRightArmTracker())
if (!isThighTracker() && !isArmForward && !isLowerArmBack) {
// Tracker goes back
yawAngle -= FastMath.PI

View File

@@ -43,7 +43,6 @@ class MountingResetTests {
"test",
null,
hasRotation = true,
isInternal = true,
imuType = IMUType.UNKNOWN,
needsReset = true,
needsMounting = true,
@@ -128,7 +127,6 @@ class MountingResetTests {
"test",
null,
hasRotation = true,
isInternal = true,
imuType = IMUType.UNKNOWN,
needsReset = true,
needsMounting = true,

View File

@@ -92,7 +92,6 @@ class ReferenceAdjustmentsTests {
"test",
null,
hasRotation = true,
isInternal = true,
imuType = IMUType.UNKNOWN,
needsReset = true,
)
@@ -125,7 +124,6 @@ class ReferenceAdjustmentsTests {
"test",
null,
hasRotation = true,
isInternal = true,
imuType = IMUType.UNKNOWN,
needsReset = true,
)
@@ -154,7 +152,6 @@ class ReferenceAdjustmentsTests {
"test",
null,
hasRotation = true,
isInternal = true,
imuType = IMUType.UNKNOWN,
needsReset = true,
)

View File

@@ -153,6 +153,7 @@ abstract class SteamVRBridge(
"OpenVR", // TODO : We need the manufacturer
)
// Display name, needsReset and isHmd
val displayName: String
val (needsReset, isHmd) = if (trackerAdded.trackerId == 0) {
displayName = if (trackerAdded.trackerName == "HMD") {
@@ -160,19 +161,27 @@ abstract class SteamVRBridge(
} else {
"Feeder App HMD"
}
// TODO support needsReset = true for VTubing (GUI toggle?)
false to true
} else {
displayName = trackerAdded.trackerName
true to false
}
// trackerPosition
val role = getById(trackerAdded.trackerRole)
val trackerPosition = if (role != null) {
getByTrackerRole(role)
} else {
null
}
// Make the tracker
val tracker = Tracker(
device,
getNextLocalTrackerId(),
trackerAdded.trackerSerial,
displayName,
null,
trackerPosition,
trackerAdded.trackerId,
hasPosition = true,
hasRotation = true,
@@ -184,10 +193,6 @@ abstract class SteamVRBridge(
device.trackers[0] = tracker
instance.deviceManager.addDevice(device)
val role = getById(trackerAdded.trackerRole)
if (role != null) {
tracker.trackerPosition = getByTrackerRole(role)
}
return tracker
}