diff --git a/gui/src/components/settings/pages/GeneralSettings.tsx b/gui/src/components/settings/pages/GeneralSettings.tsx
index 5bcd58c2d..cd78a2527 100644
--- a/gui/src/components/settings/pages/GeneralSettings.tsx
+++ b/gui/src/components/settings/pages/GeneralSettings.tsx
@@ -539,7 +539,7 @@ export function GeneralSettings() {
variant="toggle"
outlined
control={control}
- name="toggles-viveEmulation"
+ name="toggles.viveEmulation"
label={l10n.getString(
'settings-general-fk_settings-vive_emulation-label'
)}
@@ -640,7 +640,7 @@ export function GeneralSettings() {
})
}
min={2}
- max={3}
+ max={10}
step={1}
/>
diff --git a/server/src/main/java/dev/slimevr/config/TapDetectionConfig.java b/server/src/main/java/dev/slimevr/config/TapDetectionConfig.java
index 74d2ad6f3..dc4a7dfcd 100644
--- a/server/src/main/java/dev/slimevr/config/TapDetectionConfig.java
+++ b/server/src/main/java/dev/slimevr/config/TapDetectionConfig.java
@@ -17,6 +17,7 @@ public class TapDetectionConfig {
private int quickResetTaps = 2;
private int resetTaps = 3;
private int mountingResetTaps = 3;
+ private int numberTrackersOverThreshold = 1;
public float getQuickResetDelay() {
return quickResetDelay;
@@ -72,8 +73,7 @@ public class TapDetectionConfig {
// clamp to 2-3 to prevent errors
public void setQuickResetTaps(int quickResetTaps) {
- if (quickResetTaps > 3 || quickResetTaps < 2)
- FastMath.clamp(quickResetTaps, 2, 3);
+ this.quickResetTaps = (int) FastMath.clamp(quickResetTaps, 2, 10);
this.quickResetTaps = quickResetTaps;
}
@@ -82,8 +82,7 @@ public class TapDetectionConfig {
}
public void setResetTaps(int resetTaps) {
- if (resetTaps > 3 || resetTaps < 2)
- FastMath.clamp(resetTaps, 2, 3);
+ this.resetTaps = (int) FastMath.clamp(resetTaps, 2, 10);
this.resetTaps = resetTaps;
}
@@ -92,8 +91,15 @@ public class TapDetectionConfig {
}
public void setMountingResetTaps(int mountingResetTaps) {
- if (mountingResetTaps > 3 || mountingResetTaps < 2)
- FastMath.clamp(mountingResetTaps, 2, 3);
+ this.mountingResetTaps = (int) FastMath.clamp(mountingResetTaps, 2, 10);
this.mountingResetTaps = mountingResetTaps;
}
+
+ public int getNumberTrackersOverThreshold() {
+ return numberTrackersOverThreshold;
+ }
+
+ public void setNumberTrackersOverThreshold(int numberTrackersOverThreshold) {
+ this.numberTrackersOverThreshold = numberTrackersOverThreshold;
+ }
}
diff --git a/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetection.java b/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetection.java
index 5d53420c9..b9ccd6455 100644
--- a/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetection.java
+++ b/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetection.java
@@ -19,19 +19,20 @@ public class TapDetection {
private LinkedList accelList = new LinkedList<>();
private LinkedList tapTimes = new LinkedList<>();
private Tracker trackerToWatch = null;
+ private int numberTrackersOverThreshold = 1;
// hyperparameters
private static final float NS_CONVERTER = 1.0e9f;
private static final float NEEDED_ACCEL_DELTA = 6.0f;
private static final float ALLOWED_BODY_ACCEL = 1.5f;
private static final float ALLOWED_BODY_ACCEL_SQUARED = ALLOWED_BODY_ACCEL * ALLOWED_BODY_ACCEL;
- private static final float CLUMP_TIME_NS = 0.03f * NS_CONVERTER;
- private static final float TIME_WINDOW_NS = 0.6f * NS_CONVERTER;
+ private static final float CLUMP_TIME_NS = 0.08f * NS_CONVERTER;
+ private float timeWindowNS = 0.6f * NS_CONVERTER;
// state
private float detectionTime = -1.0f;
- private boolean doubleTaped = false;
- private boolean tripleTaped = false;
+ private int taps = 0;
+ private boolean waitForLowAccel = false;
public TapDetection(HumanSkeleton skeleton) {
this.skeleton = skeleton;
@@ -58,24 +59,28 @@ public class TapDetection {
trackerToWatch = tracker;
}
- public boolean getDoubleTapped() {
- return doubleTaped;
- }
-
- public boolean getTripleTapped() {
- return tripleTaped;
+ public int getTaps() {
+ return taps;
}
public float getDetectionTime() {
return detectionTime;
}
+ public void setNumberTrackersOverThreshold(int numberTrackersOverThreshold) {
+ this.numberTrackersOverThreshold = numberTrackersOverThreshold;
+ }
+
// reset the lists for detecting taps
public void resetDetector() {
tapTimes.clear();
accelList.clear();
- doubleTaped = false;
- tripleTaped = false;
+ taps = 0;
+ }
+
+ // set the max taps this detector is configured to detect
+ public void setMaxTaps(int maxTaps) {
+ timeWindowNS = 0.3f * maxTaps * NS_CONVERTER;
}
// main function for tap detection
@@ -99,13 +104,21 @@ public class TapDetection {
}
// check for a tap
- if (getAccelDelta() > NEEDED_ACCEL_DELTA) {
+ float accelDelta = getAccelDelta();
+ if (accelDelta > NEEDED_ACCEL_DELTA && !waitForLowAccel) {
+ // after a tap is added to the list, a lower acceleration
+ // is needed before another tap can be added
tapTimes.add(time);
+ waitForLowAccel = true;
}
+ // if waiting for low accel
+ if (accelDelta < ALLOWED_BODY_ACCEL)
+ waitForLowAccel = false;
+
// remove old taps from the list (if they are too old)
if (!tapTimes.isEmpty()) {
- while (time - tapTimes.getFirst() > TIME_WINDOW_NS) {
+ while (time - tapTimes.getFirst() > timeWindowNS) {
tapTimes.removeFirst();
if (tapTimes.isEmpty())
return;
@@ -119,18 +132,13 @@ public class TapDetection {
}
// get the amount of taps in the list
- int tapEvents = getTapEvents();
-
- // if there are two tap events and the user is moving relatively slowly,
- // quick reset
- if (tapEvents == 2) {
- doubleTaped = true;
+ // and set the detection time
+ int newTaps = getTapEvents();
+ if (newTaps > taps) {
+ taps = newTaps;
detectionTime = time;
- } else if (tapEvents >= 3) {
- detectionTime = time;
- tripleTaped = true;
- doubleTaped = false;
}
+
}
private float getAccelDelta() {
@@ -151,7 +159,7 @@ public class TapDetection {
if (tapTimes.isEmpty())
return 0;
- int tapEvents = 0;
+ int tapEvents = 1;
float lastTapTime = tapTimes.getFirst();
for (int i = 0; i < tapTimes.size(); i++) {
if (tapTimes.get(i) - lastTapTime > CLUMP_TIME_NS) {
@@ -167,20 +175,21 @@ public class TapDetection {
// you need two or more trackers for this feature to be reliable)
private boolean isUserStatic(Tracker trackerToExclude) {
Vector3f accel = new Vector3f();
+ int num = 0;
if (skeleton.chestTracker != null && !skeleton.chestTracker.equals(trackerToExclude)) {
skeleton.chestTracker.getAcceleration(accel);
if (accel.lengthSquared() > ALLOWED_BODY_ACCEL_SQUARED)
- return false;
+ num++;
}
if (skeleton.hipTracker != null && !skeleton.hipTracker.equals(trackerToExclude)) {
skeleton.hipTracker.getAcceleration(accel);
if (accel.lengthSquared() > ALLOWED_BODY_ACCEL_SQUARED)
- return false;
+ num++;
}
if (skeleton.waistTracker != null && !skeleton.waistTracker.equals(trackerToExclude)) {
skeleton.waistTracker.getAcceleration(accel);
if (accel.lengthSquared() > ALLOWED_BODY_ACCEL_SQUARED)
- return false;
+ num++;
}
if (
skeleton.leftUpperLegTracker != null
@@ -188,7 +197,7 @@ public class TapDetection {
) {
skeleton.leftUpperLegTracker.getAcceleration(accel);
if (accel.lengthSquared() > ALLOWED_BODY_ACCEL_SQUARED)
- return false;
+ num++;
}
if (
skeleton.rightUpperLegTracker != null
@@ -196,22 +205,22 @@ public class TapDetection {
) {
skeleton.rightUpperLegTracker.getAcceleration(accel);
if (accel.lengthSquared() > ALLOWED_BODY_ACCEL_SQUARED)
- return false;
+ num++;
}
if (
skeleton.leftFootTracker != null && !skeleton.leftFootTracker.equals(trackerToExclude)
) {
skeleton.leftFootTracker.getAcceleration(accel);
if (accel.lengthSquared() > ALLOWED_BODY_ACCEL_SQUARED)
- return false;
+ num++;
}
if (
skeleton.rightFootTracker != null && !skeleton.rightFootTracker.equals(trackerToExclude)
) {
skeleton.rightFootTracker.getAcceleration(accel);
if (accel.lengthSquared() > ALLOWED_BODY_ACCEL_SQUARED)
- return false;
+ num++;
}
- return true;
+ return num < numberTrackersOverThreshold;
}
}
diff --git a/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetectionManager.java b/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetectionManager.java
index 2cfb597b2..846ba641a 100644
--- a/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetectionManager.java
+++ b/server/src/main/java/dev/slimevr/vr/processor/skeleton/TapDetectionManager.java
@@ -1,5 +1,6 @@
package dev.slimevr.vr.processor.skeleton;
+
import dev.slimevr.config.TapDetectionConfig;
import dev.slimevr.osc.VRCOSCHandler;
import dev.slimevr.vr.trackers.Tracker;
@@ -46,6 +47,21 @@ public class TapDetectionManager {
resetDetector = new TapDetection(skeleton, getTrackerToWatchReset());
mountingResetDetector = new TapDetection(skeleton, getTrackerToWatchMountingReset());
+ // since this config value is only modified by editing the config file,
+ // we can set it here
+ quickResetDetector
+ .setNumberTrackersOverThreshold(
+ config.getNumberTrackersOverThreshold()
+ );
+ resetDetector
+ .setNumberTrackersOverThreshold(
+ config.getNumberTrackersOverThreshold()
+ );
+ mountingResetDetector
+ .setNumberTrackersOverThreshold(
+ config.getNumberTrackersOverThreshold()
+ );
+
updateConfig();
}
@@ -59,6 +75,9 @@ public class TapDetectionManager {
quickResetTaps = config.getQuickResetTaps();
resetTaps = config.getResetTaps();
mountingResetTaps = config.getMountingResetTaps();
+ quickResetDetector.setMaxTaps(quickResetTaps);
+ resetDetector.setMaxTaps(resetTaps);
+ mountingResetDetector.setMaxTaps(mountingResetTaps);
}
public void update() {
@@ -76,9 +95,8 @@ public class TapDetectionManager {
}
private void checkQuickReset() {
- boolean tapped = (quickResetTaps == 2)
- ? quickResetDetector.getDoubleTapped()
- : quickResetDetector.getTripleTapped();
+ boolean tapped = (quickResetTaps <= quickResetDetector.getTaps());
+
if (
tapped && System.nanoTime() - quickResetDetector.getDetectionTime() > quickResetDelayNs
) {
@@ -90,9 +108,8 @@ public class TapDetectionManager {
}
private void checkReset() {
- boolean tapped = (resetTaps == 2)
- ? resetDetector.getDoubleTapped()
- : resetDetector.getTripleTapped();
+ boolean tapped = (resetTaps <= resetDetector.getTaps());
+
if (
tapped && System.nanoTime() - resetDetector.getDetectionTime() > resetDelayNs
) {
@@ -104,9 +121,8 @@ public class TapDetectionManager {
}
private void checkMountingReset() {
- boolean tapped = (mountingResetTaps == 2)
- ? mountingResetDetector.getDoubleTapped()
- : mountingResetDetector.getTripleTapped();
+ boolean tapped = (mountingResetTaps <= mountingResetDetector.getTaps());
+
if (
tapped
&& System.nanoTime() - mountingResetDetector.getDetectionTime()