Compare commits

...

49 Commits

Author SHA1 Message Date
Erimel
f30ee3bcce Update solarxr-protocol 2024-11-14 21:24:29 -05:00
Erimel
e3a99cdcf5 Merge branch 'main' into flex 2024-11-14 21:19:33 -05:00
Erimel
704f2157ee remove debug logs 2024-11-03 20:47:34 -05:00
Erimel
95acdaf8c2 use byte and uint 2024-10-11 15:24:33 -04:00
Erimel
09c51fb5df protocolVersion and move datatype in firmwareconstants 2024-10-11 14:43:53 -04:00
Erimel
d69399d471 don't add more stuff to handshake (legacy-only) 2024-10-11 11:45:53 -04:00
Erimel
eac4341201 Merge branch 'fingers' into flex 2024-10-09 21:54:02 -04:00
Erimel
8c0c49f57a fix rebase 2024-10-09 21:51:17 -04:00
Erimel
79fcdb3cd9 try to do what Eiren maybe meant to do 2024-10-09 21:45:46 -04:00
Eiren Rain
96651ddeb7 Update tracker position protocol 2024-10-09 21:45:24 -04:00
Erimel
ee0161575b support resistance going both ways 2024-10-09 21:45:24 -04:00
Erimel
82df10be7c show data support in tracker setting 2024-10-09 21:45:23 -04:00
Erimel
5489abd754 fix and improve thumb angle and direction 2024-10-09 21:45:21 -04:00
Erimel
9c0463cc86 fix merge 2024-10-09 21:45:21 -04:00
Erimel
25bcb84ade fix fingers mirror tracking 2024-10-09 21:45:21 -04:00
Erimel
c3b76ab242 Fix thumb direction and angle 2024-10-09 21:45:20 -04:00
Erimel
7ea2767227 Make direction dependent on the TrackerPosition 2024-10-09 21:45:20 -04:00
Erimel
5264e3908d fix bugs 2024-10-09 21:45:20 -04:00
Erimel
d4fb3847a9 Add basic reset calibration 2024-10-09 21:44:53 -04:00
Erimel
a0afa4ca94 fix udp and improve design 2024-10-09 21:43:20 -04:00
Erimel
c2ee25e9d1 wip better udp stuff 2024-10-09 21:43:05 -04:00
Erimel
d5997ffb99 move flex logic stuff into its own class 2024-10-09 21:42:44 -04:00
Erimel
6dca92387b Don't send fingers if we don't have any tracker for them 2024-10-09 21:42:44 -04:00
Erimel
0a18a1d8e6 move VRServer instance declaration back 2024-10-09 21:42:44 -04:00
Erimel
0b890c197b fix Unit tests 2024-10-09 21:42:44 -04:00
Erimel
81157f3c18 WIP flex resistance and angle udp support 2024-10-09 21:42:44 -04:00
Erimel
beaf83f097 add finger bones translations 2024-10-09 21:41:58 -04:00
Erimel
4e02562e05 Support receiving fingers via VMC 2024-10-09 21:41:57 -04:00
Erimel
708cab9ea8 Properly track rotations over 180 degrees 2024-10-09 21:41:57 -04:00
Erimel
d29109887b fine tune slerp values, use quaternion space, support tpose reset 2024-10-09 21:41:57 -04:00
Erimel
bbd63923dc add preview in GUI, fix vmc mirror and localRotation for fingers 2024-10-09 21:41:57 -04:00
Erimel
14293f4842 basic finger ik in skeleton 2024-10-09 21:41:57 -04:00
Erimel
0d4921e099 add fingers to UnityArmature 2024-10-09 21:41:57 -04:00
Erimel
c7aa6451f2 skeleton wip 2024-10-09 21:41:56 -04:00
Erimel
a80ce41079 Add finger bones variables 2024-10-09 21:41:48 -04:00
Erimel
6c289c91f4 Update solarxr-protocol 2024-10-09 21:26:49 -04:00
Erimel
79e7b19a12 fix fingers mirror tracking 2024-10-09 21:13:39 -04:00
Erimel
2f095aeb73 Don't send fingers if we don't have any tracker for them 2024-10-09 21:13:39 -04:00
Erimel
7909a1b312 move VRServer instance declaration back 2024-10-09 21:13:39 -04:00
Erimel
d404fb5a88 fix Unit tests 2024-10-09 21:13:39 -04:00
Erimel
938f708c89 add finger bones translations 2024-10-09 21:13:38 -04:00
Erimel
e1d9a2cd5b Support receiving fingers via VMC 2024-10-09 21:13:38 -04:00
Erimel
98c7db658f Properly track rotations over 180 degrees 2024-10-09 21:13:38 -04:00
Erimel
75cd829a5d fine tune slerp values, use quaternion space, support tpose reset 2024-10-09 21:13:38 -04:00
Erimel
c4a4016d45 add preview in GUI, fix vmc mirror and localRotation for fingers 2024-10-09 21:13:38 -04:00
Erimel
0191b421bb basic finger ik in skeleton 2024-10-09 21:13:37 -04:00
Erimel
1ec642f3b5 add fingers to UnityArmature 2024-10-09 21:13:37 -04:00
Erimel
99a2cee182 skeleton wip 2024-10-09 21:13:37 -04:00
Erimel
063a686efa Add finger bones variables 2024-10-09 21:13:19 -04:00
27 changed files with 1698 additions and 172 deletions

3
.gitignore vendored
View File

@@ -43,3 +43,6 @@ build/
# Ignore Android local properties
local.properties
# Ignore temporary config
vrconfig.yml.tmp

View File

@@ -44,6 +44,36 @@ body_part-LEFT_HAND = Left hand
body_part-LEFT_UPPER_LEG = Left thigh
body_part-LEFT_LOWER_LEG = Left ankle
body_part-LEFT_FOOT = Left foot
body_part-LEFT_THUMB_PROXIMAL = Left thumb proximal
body_part-LEFT_THUMB_INTERMEDIATE = Left thumb intermediate
body_part-LEFT_THUMB_DISTAL = Left thumb distal
body_part-LEFT_INDEX_PROXIMAL = Left index proximal
body_part-LEFT_INDEX_INTERMEDIATE = Left index intermediate
body_part-LEFT_INDEX_DISTAL = Left index distal
body_part-LEFT_MIDDLE_PROXIMAL = Left middle proximal
body_part-LEFT_MIDDLE_INTERMEDIATE = Left middle intermediate
body_part-LEFT_MIDDLE_DISTAL = Left middle distal
body_part-LEFT_RING_PROXIMAL = Left ring proximal
body_part-LEFT_RING_INTERMEDIATE = Left ring intermediate
body_part-LEFT_RING_DISTAL = Left ring distal
body_part-LEFT_LITTLE_PROXIMAL = Left little proximal
body_part-LEFT_LITTLE_INTERMEDIATE = Left little intermediate
body_part-LEFT_LITTLE_DISTAL = Left little distal
body_part-RIGHT_THUMB_PROXIMAL = Right thumb proximal
body_part-RIGHT_THUMB_INTERMEDIATE = Right thumb intermediate
body_part-RIGHT_THUMB_DISTAL = Right thumb distal
body_part-RIGHT_INDEX_PROXIMAL = Right index proximal
body_part-RIGHT_INDEX_INTERMEDIATE = Right index intermediate
body_part-RIGHT_INDEX_DISTAL = Right index distal
body_part-RIGHT_MIDDLE_PROXIMAL = Right middle proximal
body_part-RIGHT_MIDDLE_INTERMEDIATE = Right middle intermediate
body_part-RIGHT_MIDDLE_DISTAL = Right middle distal
body_part-RIGHT_RING_PROXIMAL = Right ring proximal
body_part-RIGHT_RING_INTERMEDIATE = Right ring intermediate
body_part-RIGHT_RING_DISTAL = Right ring distal
body_part-RIGHT_LITTLE_PROXIMAL = Right little proximal
body_part-RIGHT_LITTLE_INTERMEDIATE = Right little intermediate
body_part-RIGHT_LITTLE_DISTAL = Right little distal
## Proportions
skeleton_bone-NONE = None
@@ -184,6 +214,7 @@ tracker-infos-url = Tracker URL
tracker-infos-version = Firmware Version
tracker-infos-hardware_rev = Hardware Revision
tracker-infos-hardware_identifier = Hardware ID
tracker-infos-data_support = Data support
tracker-infos-imu = IMU Sensor
tracker-infos-board_type = Main board
tracker-infos-network_version = Protocol Version

View File

@@ -15,6 +15,7 @@ import { UpperArmIcon } from './icon/UpperArmIcon';
import { UpperLegIcon } from './icon/UpperLegIcon';
import { WaistIcon } from './icon/WaistIcon';
import { UpperChestIcon } from './icon/UpperChestIcon';
import { FingersIcon } from './icon/FingersIcon';
// All body parts that are right or left, are by default left!
export const mapPart: Record<
@@ -86,6 +87,96 @@ export const mapPart: Record<
<UpperLegIcon width={width} flipped></UpperLegIcon>
),
[BodyPart.WAIST]: ({ width }) => <WaistIcon width={width}></WaistIcon>,
[BodyPart.LEFT_THUMB_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_THUMB_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_THUMB_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_INDEX_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_INDEX_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_INDEX_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_MIDDLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_MIDDLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_MIDDLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_RING_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_RING_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_RING_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_LITTLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_LITTLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_LITTLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_THUMB_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_THUMB_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_THUMB_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_INDEX_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_INDEX_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_INDEX_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_MIDDLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_MIDDLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_MIDDLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_RING_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_RING_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_RING_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_LITTLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_LITTLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_LITTLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
};
export function BodyPartIcon({

View File

@@ -0,0 +1,15 @@
export function FingersIcon({ width = 28 }: { width?: number }) {
return (
<svg
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
width={width}
viewBox="0 0 93.49 130"
>
<g>
<path d="M2.34,62.52l-0.26,0.27l-2.08-0.83V31.27c0-1.42,0.42-2.76,1.14-3.89l0,0c0.14-0.22,0.29-0.44,0.46-0.64 c0.17-0.22,0.35-0.42,0.53-0.6l0.02-0.02c0.54-0.54,1.18-1.01,1.89-1.36l0.03-0.01l0.35-0.17l0.04-0.02 c0.86-0.37,1.82-0.58,2.81-0.58l0,0h0.04v0c2.01,0,3.84,0.82,5.16,2.14c0.54,0.54,1.01,1.18,1.36,1.88l0.02,0.04l0.16,0.35 l0.01,0.03c0.37,0.86,0.58,1.82,0.58,2.81l0,0.01v0.04v24.96v1.13l-1.13,0.07c-3.08,0.19-5.92,1.18-8.32,2.77 c-0.48,0.32-0.94,0.66-1.38,1.02c-0.41,0.34-0.84,0.72-1.26,1.15L2.34,62.52L2.34,62.52L2.34,62.52z M65.62,83.35l1.23,0.46 l0.53,0.39c0.09,0.12,0.2,0.22,0.33,0.31l0,0l0.16,0.09l0,0.01c0.17,0.08,0.35,0.12,0.54,0.12v0h0.03c0.18,0,0.34-0.03,0.49-0.09 l0.12-0.06l0.12-0.07l0.04-0.02l0.04-0.02c0.54-0.31,1.26-0.85,2.05-1.5c0.8-0.67,1.71-1.49,2.61-2.33 c1.76-1.66,3.76-3.66,4.56-4.45l0.04-0.04c2.53-2.53,5.11-3.7,7.38-3.85c0.46-0.03,0.92-0.02,1.35,0.03 c0.44,0.05,0.87,0.14,1.28,0.27h0.01l0.05,0.02l0.01,0c0.81,0.26,1.56,0.67,2.22,1.2l0.03,0.03l0.31,0.27l0.06,0.05l0.29,0.29 l0.05,0.06l0.01,0.01l0,0l0.01,0.02l0,0c0.56,0.62,1.01,1.35,1.34,2.16l0.02,0.03l0.15,0.42l0.02,0.09l0.12,0.43l0.01,0.05 l0.01,0.06h0c0.57,2.38,0.1,5.27-1.88,8.17c-0.37,0.55-0.81,1.11-1.29,1.65c-0.48,0.54-1.02,1.09-1.62,1.62l0,0l-0.08,0.07 l-0.1,0.09l-0.07,0.07l-0.04,0.04L63.64,114.3l-0.85,0.93l-0.06-0.06c-1.35,1.23-2.67,2.29-4.01,3.2c-1.6,1.08-3.22,1.95-4.9,2.61 c-1.69,0.67-3.46,1.15-5.33,1.46c-1.87,0.3-3.84,0.45-5.94,0.45h-15.9c-5.3,0-10.23-1.56-14.36-4.23l0,0 c-0.79-0.51-1.57-1.08-2.32-1.69c-0.76-0.62-1.47-1.26-2.12-1.92l-0.02-0.02l0,0c-2.01-2.04-3.71-4.42-5-7.03 c-0.25-0.52-0.49-1.04-0.71-1.56C0.76,103.2,0.01,99.65,0,95.93h0V95.9V74.93c0-1.93,0.36-3.79,1-5.49l0-0.01 c0.12-0.31,0.26-0.64,0.41-0.97h0c0.15-0.32,0.31-0.64,0.48-0.95l0.01-0.02l0.03-0.05l0.02-0.04c0.62-0.97,1.36-1.88,2.19-2.69 l0.02-0.02l0.46-0.43l0.04-0.03l0.48-0.41l0.04-0.04l0.02-0.02l0,0c1.06-0.85,2.24-1.57,3.51-2.11h0c0.29-0.12,0.57-0.24,0.76-0.3 v0c1.56-0.57,3.25-0.88,5.01-0.88v0h0.04h0.64l0.29,0.04l0.27,0.07l0.21,0.02v0h17.27v0l0.11,0h0.08l0.11,0v0h17.27v0l0.05,0h0.07 l0.05,0v0h1.28c2.54,0,4.94,0.65,7.05,1.79l0,0c0.42,0.23,0.82,0.47,1.19,0.72v0l0.01,0c0.36,0.24,0.74,0.52,1.11,0.82l0.01,0.01 l0.02,0.02l0,0c1.82,1.49,3.3,3.41,4.25,5.6c0.2,0.45,0.37,0.89,0.5,1.31v0c0.15,0.45,0.27,0.91,0.38,1.37v0.01l0.01,0.07 l0.02,0.11c0.01,0.08,0.02,0.16,0.04,0.22h0l0.01,0.03h0l0.04,0.11h0l0.02,0.06L67,73.21l0.06,0.65l0,0.04l0.02,0.26v0.04 l0.02,0.46v0.03l0,0.25l0,0.01v4.43v1.66l-1.58-0.52c-2.46-0.81-4.81-1.36-7.03-1.66h0c-0.5-0.07-0.98-0.12-1.42-0.17 c-0.45-0.04-0.92-0.08-1.39-0.1l-1.02-0.03c-2.85-0.04-5.48,0.37-7.81,1.17c-0.51,0.18-0.99,0.36-1.42,0.55 c-0.45,0.2-0.9,0.41-1.32,0.64l-0.71,0.41c-2.23,1.34-4.08,3.14-5.49,5.34c-0.29,0.46-0.56,0.9-0.78,1.33 c-0.24,0.45-0.46,0.94-0.68,1.44v0l-0.01,0.03h0c-0.68,1.62-1.17,3.4-1.45,5.33c-0.06,0.44-0.12,0.87-0.15,1.28 c-0.03,0.34-0.07,0.7-0.08,1.06l2.66,0.03c0.08-1.35,0.28-2.64,0.57-3.84h0c0.09-0.37,0.18-0.72,0.27-1.03h0 c0.09-0.3,0.2-0.64,0.33-0.98v0l0.32-0.82l0,0c0.89-2.13,2.18-3.94,3.8-5.38c0.32-0.28,0.66-0.55,0.99-0.8 c0.37-0.27,0.72-0.51,1.06-0.71l0.02-0.01l0.03-0.02v0c1.7-1.02,3.68-1.73,5.9-2.09c0.45-0.07,0.94-0.14,1.44-0.18 c0.49-0.05,1-0.07,1.49-0.09h0.03l0.98,0h0.02c2.3,0.03,4.79,0.39,7.44,1.07v0c0.61,0.15,1.18,0.32,1.72,0.49 c0.62,0.19,1.21,0.39,1.77,0.58L65.62,83.35L65.62,83.35z M15.74,60.59L15.74,60.59L15.74,60.59L15.74,60.59L15.74,60.59z M48.24,57.4H36.05h-1.2v-1.2V7.3h0c0-2.01,0.82-3.84,2.14-5.16c0.54-0.54,1.18-1.01,1.88-1.36l0.03-0.01l0.35-0.17l0.04-0.02 c0.86-0.37,1.81-0.58,2.81-0.58l0-0.01h0.04v0.01c2.01,0,3.84,0.82,5.16,2.14c0.54,0.54,1,1.18,1.36,1.88l0.02,0.03l0.16,0.35 l0.02,0.04c0.37,0.86,0.58,1.81,0.58,2.81l0,0.01V7.3v48.89v1.2H48.24L48.24,57.4z M53.63,57.45l-0.22-0.02l-1.12-0.09v-1.11V19.01 h0c0-2.01,0.82-3.84,2.14-5.16c0.54-0.54,1.18-1,1.89-1.36l0.04-0.02l0.35-0.16l0.03-0.02c0.86-0.37,1.81-0.58,2.81-0.58l0,0h0.04 c1.42,0,2.76,0.42,3.89,1.14l0,0l0.01,0.01c0.22,0.13,0.43,0.29,0.63,0.45l0,0l0.01,0.01c0.21,0.16,0.41,0.34,0.59,0.52l0.02,0.02 c0.54,0.54,1.01,1.18,1.36,1.88l0.01,0.03l0.17,0.35l0.02,0.04c0.37,0.86,0.58,1.82,0.58,2.81l0,0v0.04v42.9l-2.07,0.84l-0.2-0.2 c-2.06-2.06-4.63-3.62-7.49-4.45c-0.57-0.17-1.16-0.31-1.73-0.41C54.84,57.58,54.24,57.5,53.63,57.45L53.63,57.45z M30.68,57.4 H18.49h-1.21v-1.2V31.27h0V18.89h0c0-1.42,0.42-2.77,1.14-3.9h0c0.14-0.23,0.3-0.45,0.46-0.65c0.17-0.22,0.35-0.42,0.52-0.59 l0.02-0.02c0.54-0.54,1.18-1,1.89-1.36l0.03-0.01l0.35-0.16l0.04-0.02c0.86-0.37,1.81-0.58,2.81-0.58l0,0h0.04v0 c2.01,0,3.84,0.82,5.16,2.14c0.54,0.54,1,1.18,1.36,1.88l0.01,0.03L31.28,16l0.02,0.04c0.37,0.86,0.58,1.82,0.58,2.81l0,0v0.04 v37.3v1.2H30.68L30.68,57.4z" />
</g>
</svg>
);
}

View File

@@ -10,6 +10,7 @@ import { SlimeUpIcon } from '@/components/commons/icon/SlimeUpIcon';
import { BodyPart } from 'solarxr-protocol';
import { PawIcon } from '@/components/commons/icon/PawIcon';
import { useLocaleConfig } from '@/i18n/config';
import { FingersIcon } from '@/components/commons/icon/FingersIcon';
// All body parts that are right or left, are by default left!
export const mapPart: Record<
@@ -97,6 +98,96 @@ export const mapPart: Record<
<FootIcon width={width} flipped></FootIcon>
),
[BodyPart.WAIST]: ({ width }) => <FootIcon width={width}></FootIcon>,
[BodyPart.LEFT_THUMB_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_THUMB_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_THUMB_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_INDEX_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_INDEX_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_INDEX_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_MIDDLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_MIDDLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_MIDDLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_RING_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_RING_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_RING_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_LITTLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_LITTLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.LEFT_LITTLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_THUMB_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_THUMB_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_THUMB_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_INDEX_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_INDEX_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_INDEX_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_MIDDLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_MIDDLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_MIDDLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_RING_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_RING_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_RING_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_LITTLE_PROXIMAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_LITTLE_INTERMEDIATE]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
[BodyPart.RIGHT_LITTLE_DISTAL]: ({ width }) => (
<FingersIcon width={width}></FingersIcon>
),
};
export function MountingBodyPartIcon({

View File

@@ -11,6 +11,7 @@ import {
ImuType,
MagnetometerStatus,
RpcMessage,
TrackerDataType,
} from 'solarxr-protocol';
import { useDebouncedEffect } from '@/hooks/timeout';
import { useTrackerFromId } from '@/hooks/tracker';
@@ -260,6 +261,16 @@ export function TrackerSettingsPage() {
{tracker?.device?.hardwareInfo?.hardwareIdentifier || '--'}
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-data_support')}
</Typography>
<Typography>
{tracker?.tracker.info?.dataSupport
? TrackerDataType[tracker?.tracker.info?.dataSupport]
: '--'}
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-imu')}

View File

@@ -223,6 +223,37 @@ export class BoneKind extends Bone {
case BodyPart.LEFT_HIP:
case BodyPart.RIGHT_HIP:
return new Color('pink');
case BodyPart.LEFT_THUMB_PROXIMAL:
case BodyPart.LEFT_THUMB_INTERMEDIATE:
case BodyPart.LEFT_THUMB_DISTAL:
case BodyPart.LEFT_INDEX_PROXIMAL:
case BodyPart.LEFT_INDEX_INTERMEDIATE:
case BodyPart.LEFT_INDEX_DISTAL:
case BodyPart.LEFT_MIDDLE_PROXIMAL:
case BodyPart.LEFT_MIDDLE_INTERMEDIATE:
case BodyPart.LEFT_MIDDLE_DISTAL:
case BodyPart.LEFT_RING_PROXIMAL:
case BodyPart.LEFT_RING_INTERMEDIATE:
case BodyPart.LEFT_RING_DISTAL:
case BodyPart.LEFT_LITTLE_PROXIMAL:
case BodyPart.LEFT_LITTLE_INTERMEDIATE:
case BodyPart.LEFT_LITTLE_DISTAL:
case BodyPart.RIGHT_THUMB_PROXIMAL:
case BodyPart.RIGHT_THUMB_INTERMEDIATE:
case BodyPart.RIGHT_THUMB_DISTAL:
case BodyPart.RIGHT_INDEX_PROXIMAL:
case BodyPart.RIGHT_INDEX_INTERMEDIATE:
case BodyPart.RIGHT_INDEX_DISTAL:
case BodyPart.RIGHT_MIDDLE_PROXIMAL:
case BodyPart.RIGHT_MIDDLE_INTERMEDIATE:
case BodyPart.RIGHT_MIDDLE_DISTAL:
case BodyPart.RIGHT_RING_PROXIMAL:
case BodyPart.RIGHT_RING_INTERMEDIATE:
case BodyPart.RIGHT_RING_DISTAL:
case BodyPart.RIGHT_LITTLE_PROXIMAL:
case BodyPart.RIGHT_LITTLE_INTERMEDIATE:
case BodyPart.RIGHT_LITTLE_DISTAL:
return new Color('pink');
}
}
@@ -275,8 +306,81 @@ export class BoneKind extends Bone {
case BodyPart.RIGHT_LOWER_ARM:
return [BodyPart.RIGHT_HAND];
case BodyPart.LEFT_HAND:
return [];
return [
BodyPart.LEFT_THUMB_PROXIMAL,
BodyPart.LEFT_INDEX_PROXIMAL,
BodyPart.LEFT_MIDDLE_PROXIMAL,
BodyPart.LEFT_RING_PROXIMAL,
BodyPart.LEFT_LITTLE_PROXIMAL,
];
case BodyPart.RIGHT_HAND:
return [
BodyPart.RIGHT_THUMB_PROXIMAL,
BodyPart.RIGHT_INDEX_PROXIMAL,
BodyPart.RIGHT_MIDDLE_PROXIMAL,
BodyPart.RIGHT_RING_PROXIMAL,
BodyPart.RIGHT_LITTLE_PROXIMAL,
];
case BodyPart.LEFT_THUMB_PROXIMAL:
return [BodyPart.LEFT_THUMB_INTERMEDIATE];
case BodyPart.LEFT_THUMB_INTERMEDIATE:
return [BodyPart.LEFT_THUMB_DISTAL];
case BodyPart.LEFT_THUMB_DISTAL:
return [];
case BodyPart.LEFT_INDEX_PROXIMAL:
return [BodyPart.LEFT_INDEX_INTERMEDIATE];
case BodyPart.LEFT_INDEX_INTERMEDIATE:
return [BodyPart.LEFT_INDEX_DISTAL];
case BodyPart.LEFT_INDEX_DISTAL:
return [];
case BodyPart.LEFT_MIDDLE_PROXIMAL:
return [BodyPart.LEFT_MIDDLE_INTERMEDIATE];
case BodyPart.LEFT_MIDDLE_INTERMEDIATE:
return [BodyPart.LEFT_MIDDLE_DISTAL];
case BodyPart.LEFT_MIDDLE_DISTAL:
return [];
case BodyPart.LEFT_RING_PROXIMAL:
return [BodyPart.LEFT_RING_INTERMEDIATE];
case BodyPart.LEFT_RING_INTERMEDIATE:
return [BodyPart.LEFT_RING_DISTAL];
case BodyPart.LEFT_RING_DISTAL:
return [];
case BodyPart.LEFT_LITTLE_PROXIMAL:
return [BodyPart.LEFT_LITTLE_INTERMEDIATE];
case BodyPart.LEFT_LITTLE_INTERMEDIATE:
return [BodyPart.LEFT_LITTLE_DISTAL];
case BodyPart.LEFT_LITTLE_DISTAL:
return [];
case BodyPart.RIGHT_THUMB_PROXIMAL:
return [BodyPart.RIGHT_THUMB_INTERMEDIATE];
case BodyPart.RIGHT_THUMB_INTERMEDIATE:
return [BodyPart.RIGHT_THUMB_DISTAL];
case BodyPart.RIGHT_THUMB_DISTAL:
return [];
case BodyPart.RIGHT_INDEX_PROXIMAL:
return [BodyPart.RIGHT_INDEX_INTERMEDIATE];
case BodyPart.RIGHT_INDEX_INTERMEDIATE:
return [BodyPart.RIGHT_INDEX_DISTAL];
case BodyPart.RIGHT_INDEX_DISTAL:
return [];
case BodyPart.RIGHT_MIDDLE_PROXIMAL:
return [BodyPart.RIGHT_MIDDLE_INTERMEDIATE];
case BodyPart.RIGHT_MIDDLE_INTERMEDIATE:
return [BodyPart.RIGHT_MIDDLE_DISTAL];
case BodyPart.RIGHT_MIDDLE_DISTAL:
return [];
case BodyPart.RIGHT_RING_PROXIMAL:
return [BodyPart.RIGHT_RING_INTERMEDIATE];
case BodyPart.RIGHT_RING_INTERMEDIATE:
return [BodyPart.RIGHT_RING_DISTAL];
case BodyPart.RIGHT_RING_DISTAL:
return [];
case BodyPart.RIGHT_LITTLE_PROXIMAL:
return [BodyPart.RIGHT_LITTLE_INTERMEDIATE];
case BodyPart.RIGHT_LITTLE_INTERMEDIATE:
return [BodyPart.RIGHT_LITTLE_DISTAL];
case BodyPart.RIGHT_LITTLE_DISTAL:
return [];
}
}
@@ -329,6 +433,67 @@ export class BoneKind extends Bone {
return BodyPart.LEFT_LOWER_ARM;
case BodyPart.RIGHT_HAND:
return BodyPart.RIGHT_LOWER_ARM;
case BodyPart.LEFT_THUMB_PROXIMAL:
return BodyPart.LEFT_HAND;
case BodyPart.LEFT_THUMB_INTERMEDIATE:
return BodyPart.LEFT_THUMB_PROXIMAL;
case BodyPart.LEFT_THUMB_DISTAL:
return BodyPart.LEFT_THUMB_INTERMEDIATE;
case BodyPart.LEFT_INDEX_PROXIMAL:
return BodyPart.LEFT_HAND;
case BodyPart.LEFT_INDEX_INTERMEDIATE:
return BodyPart.LEFT_INDEX_PROXIMAL;
case BodyPart.LEFT_INDEX_DISTAL:
return BodyPart.LEFT_INDEX_INTERMEDIATE;
case BodyPart.LEFT_MIDDLE_PROXIMAL:
return BodyPart.LEFT_HAND;
case BodyPart.LEFT_MIDDLE_INTERMEDIATE:
return BodyPart.LEFT_MIDDLE_PROXIMAL;
case BodyPart.LEFT_MIDDLE_DISTAL:
return BodyPart.LEFT_MIDDLE_INTERMEDIATE;
case BodyPart.LEFT_RING_PROXIMAL:
return BodyPart.LEFT_HAND;
case BodyPart.LEFT_RING_INTERMEDIATE:
return BodyPart.LEFT_RING_PROXIMAL;
case BodyPart.LEFT_RING_DISTAL:
return BodyPart.LEFT_RING_INTERMEDIATE;
case BodyPart.LEFT_LITTLE_PROXIMAL:
return BodyPart.LEFT_HAND;
case BodyPart.LEFT_LITTLE_INTERMEDIATE:
return BodyPart.LEFT_LITTLE_PROXIMAL;
case BodyPart.LEFT_LITTLE_DISTAL:
return BodyPart.LEFT_LITTLE_INTERMEDIATE;
case BodyPart.RIGHT_THUMB_PROXIMAL:
return BodyPart.RIGHT_HAND;
case BodyPart.RIGHT_THUMB_INTERMEDIATE:
return BodyPart.RIGHT_THUMB_PROXIMAL;
case BodyPart.RIGHT_THUMB_DISTAL:
return BodyPart.RIGHT_THUMB_INTERMEDIATE;
case BodyPart.RIGHT_INDEX_PROXIMAL:
return BodyPart.RIGHT_HAND;
case BodyPart.RIGHT_INDEX_INTERMEDIATE:
return BodyPart.RIGHT_INDEX_PROXIMAL;
case BodyPart.RIGHT_INDEX_DISTAL:
return BodyPart.RIGHT_INDEX_INTERMEDIATE;
case BodyPart.RIGHT_MIDDLE_PROXIMAL:
return BodyPart.RIGHT_HAND;
case BodyPart.RIGHT_MIDDLE_INTERMEDIATE:
return BodyPart.RIGHT_MIDDLE_PROXIMAL;
case BodyPart.RIGHT_MIDDLE_DISTAL:
return BodyPart.RIGHT_MIDDLE_INTERMEDIATE;
case BodyPart.RIGHT_RING_PROXIMAL:
return BodyPart.RIGHT_HAND;
case BodyPart.RIGHT_RING_INTERMEDIATE:
return BodyPart.RIGHT_RING_PROXIMAL;
case BodyPart.RIGHT_RING_DISTAL:
return BodyPart.RIGHT_RING_INTERMEDIATE;
case BodyPart.RIGHT_LITTLE_PROXIMAL:
return BodyPart.RIGHT_HAND;
case BodyPart.RIGHT_LITTLE_INTERMEDIATE:
return BodyPart.RIGHT_LITTLE_PROXIMAL;
case BodyPart.RIGHT_LITTLE_DISTAL:
return BodyPart.RIGHT_LITTLE_INTERMEDIATE;
}
}
}

View File

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

View File

@@ -1,5 +1,6 @@
package dev.slimevr.filtering
import com.jme3.system.NanoTimer
import dev.slimevr.VRServer
import io.github.axisangles.ktmath.Quaternion
import io.github.axisangles.ktmath.Quaternion.Companion.IDENTITY
@@ -17,18 +18,18 @@ private const val PREDICT_BUFFER = 6
class QuaternionMovingAverage(
val type: TrackerFilters,
var amount: Float,
initialRotation: Quaternion,
var amount: Float = 0f,
initialRotation: Quaternion = IDENTITY,
) {
var filteredQuaternion = IDENTITY
private var smoothFactor = 0f
private var predictFactor = 0f
private lateinit var rotBuffer: CircularArrayList<Quaternion>
private var latestQuaternion = IDENTITY
private var smoothingQuaternion = IDENTITY
private val fpsTimer = VRServer.instance.fpsTimer
private val fpsTimer = if (VRServer.instanceInitialized) VRServer.instance.fpsTimer else NanoTimer()
private var frameCounter = 0
private var lastAmt = 0f
var filteredQuaternion = IDENTITY
init {
// amount should range from 0 to 1.
@@ -48,9 +49,7 @@ class QuaternionMovingAverage(
predictFactor = PREDICT_MULTIPLIER * amount + PREDICT_MIN
rotBuffer = CircularArrayList(PREDICT_BUFFER)
}
filteredQuaternion = initialRotation
latestQuaternion = initialRotation
smoothingQuaternion = initialRotation
resetQuats(initialRotation)
}
// Runs at up to 1000hz. We use a timer to make it framerate-independent
@@ -70,7 +69,7 @@ class QuaternionMovingAverage(
// Slerps the target rotation to that predicted rotation by amt
filteredQuaternion = filteredQuaternion.interpR(quatBuf, amt)
}
} else { // Smoothing
} else if (type == TrackerFilters.SMOOTHING) {
// Increase every update for linear interpolation
frameCounter++
@@ -90,6 +89,9 @@ class QuaternionMovingAverage(
// Smooth towards the target rotation by the slerp factor
filteredQuaternion = smoothingQuaternion.interpR(latestQuaternion, amt)
} else {
// No filtering; just keep track of rotations (for going over 180 degrees)
filteredQuaternion = latestQuaternion.twinNearest(smoothingQuaternion)
}
}
@@ -102,12 +104,20 @@ class QuaternionMovingAverage(
// Gets and stores the rotation between the last 2 quaternions
rotBuffer.add(latestQuaternion.inv().times(q))
} else { // Smoothing
} else if (type == TrackerFilters.SMOOTHING) {
frameCounter = 0
lastAmt = 0f
smoothingQuaternion = filteredQuaternion
} else {
smoothingQuaternion = filteredQuaternion
}
latestQuaternion = q
}
fun resetQuats(q: Quaternion) {
filteredQuaternion = q
latestQuaternion = q
smoothingQuaternion = q
}
}

View File

@@ -12,9 +12,12 @@ import io.github.axisangles.ktmath.Vector3
*/
class UnityArmature(localRot: Boolean) {
// Head
private val headNode = TransformNode(localRotation = localRot)
private val neckTailNode = TransformNode(localRotation = localRot)
private val neckHeadNode = TransformNode(localRotation = localRot)
// Spine
private val upperChestNode = TransformNode(localRotation = localRot)
private val chestNode = TransformNode(localRotation = localRot)
private val spineTailNode = TransformNode(localRotation = localRot)
@@ -22,12 +25,16 @@ class UnityArmature(localRot: Boolean) {
private val hipsNode = TransformNode(localRotation = localRot)
private val leftHipNode = TransformNode(localRotation = localRot)
private val rightHipNode = TransformNode(localRotation = localRot)
// Legs
private val leftKneeNode = TransformNode(localRotation = localRot)
private val leftAnkleNode = TransformNode(localRotation = localRot)
private val leftFootNode = TransformNode(localRotation = localRot)
private val rightKneeNode = TransformNode(localRotation = localRot)
private val rightAnkleNode = TransformNode(localRotation = localRot)
private val rightFootNode = TransformNode(localRotation = localRot)
// Arms
private val leftShoulderHeadNode = TransformNode(localRotation = localRot)
private val rightShoulderHeadNode = TransformNode(localRotation = localRot)
private val leftShoulderTailNode = TransformNode(localRotation = localRot)
@@ -36,8 +43,50 @@ class UnityArmature(localRot: Boolean) {
private val rightElbowNode = TransformNode(localRotation = localRot)
private val leftWristNode = TransformNode(localRotation = localRot)
private val rightWristNode = TransformNode(localRotation = localRot)
private val leftHandNode = TransformNode(localRotation = localRot)
private val rightHandNode = TransformNode(localRotation = localRot)
private val leftHandNode = TransformNode(localRotation = !localRot)
private val rightHandNode = TransformNode(localRotation = !localRot)
// Fingers
val leftThumbProximalHeadNode = TransformNode(localRotation = localRot)
val leftThumbProximalTailNode = TransformNode(localRotation = localRot)
val leftThumbIntermediateNode = TransformNode(localRotation = localRot)
val leftThumbDistalNode = TransformNode(localRotation = localRot)
val leftIndexProximalHeadNode = TransformNode(localRotation = localRot)
val leftIndexProximalTailNode = TransformNode(localRotation = localRot)
val leftIndexIntermediateNode = TransformNode(localRotation = localRot)
val leftIndexDistalNode = TransformNode(localRotation = localRot)
val leftMiddleProximalHeadNode = TransformNode(localRotation = localRot)
val leftMiddleProximalTailNode = TransformNode(localRotation = localRot)
val leftMiddleIntermediateNode = TransformNode(localRotation = localRot)
val leftMiddleDistalNode = TransformNode(localRotation = localRot)
val leftRingProximalHeadNode = TransformNode(localRotation = localRot)
val leftRingProximalTailNode = TransformNode(localRotation = localRot)
val leftRingIntermediateNode = TransformNode(localRotation = localRot)
val leftRingDistalNode = TransformNode(localRotation = localRot)
val leftLittleProximalHeadNode = TransformNode(localRotation = localRot)
val leftLittleProximalTailNode = TransformNode(localRotation = localRot)
val leftLittleIntermediateNode = TransformNode(localRotation = localRot)
val leftLittleDistalNode = TransformNode(localRotation = localRot)
val rightThumbProximalHeadNode = TransformNode(localRotation = localRot)
val rightThumbProximalTailNode = TransformNode(localRotation = localRot)
val rightThumbIntermediateNode = TransformNode(localRotation = localRot)
val rightThumbDistalNode = TransformNode(localRotation = localRot)
val rightIndexProximalHeadNode = TransformNode(localRotation = localRot)
val rightIndexProximalTailNode = TransformNode(localRotation = localRot)
val rightIndexIntermediateNode = TransformNode(localRotation = localRot)
val rightIndexDistalNode = TransformNode(localRotation = localRot)
val rightMiddleProximalHeadNode = TransformNode(localRotation = localRot)
val rightMiddleProximalTailNode = TransformNode(localRotation = localRot)
val rightMiddleIntermediateNode = TransformNode(localRotation = localRot)
val rightMiddleDistalNode = TransformNode(localRotation = localRot)
val rightRingProximalHeadNode = TransformNode(localRotation = localRot)
val rightRingProximalTailNode = TransformNode(localRotation = localRot)
val rightRingIntermediateNode = TransformNode(localRotation = localRot)
val rightRingDistalNode = TransformNode(localRotation = localRot)
val rightLittleProximalHeadNode = TransformNode(localRotation = localRot)
val rightLittleProximalTailNode = TransformNode(localRotation = localRot)
val rightLittleIntermediateNode = TransformNode(localRotation = localRot)
val rightLittleDistalNode = TransformNode(localRotation = localRot)
private var rootPosition = Vector3.NULL
private var rootRotation = Quaternion.IDENTITY
@@ -74,6 +123,48 @@ class UnityArmature(localRot: Boolean) {
rightElbowNode.attachChild(rightWristNode)
leftWristNode.attachChild(leftHandNode)
rightWristNode.attachChild(rightHandNode)
// Fingers
leftHandNode.attachChild(leftThumbProximalHeadNode)
leftThumbProximalHeadNode.attachChild(leftThumbProximalTailNode)
leftThumbProximalTailNode.attachChild(leftThumbIntermediateNode)
leftThumbIntermediateNode.attachChild(leftThumbDistalNode)
leftHandNode.attachChild(leftIndexProximalHeadNode)
leftIndexProximalHeadNode.attachChild(leftIndexProximalTailNode)
leftIndexProximalTailNode.attachChild(leftIndexIntermediateNode)
leftIndexIntermediateNode.attachChild(leftIndexDistalNode)
leftHandNode.attachChild(leftMiddleProximalHeadNode)
leftMiddleProximalHeadNode.attachChild(leftMiddleProximalTailNode)
leftMiddleProximalTailNode.attachChild(leftMiddleIntermediateNode)
leftMiddleIntermediateNode.attachChild(leftMiddleDistalNode)
leftHandNode.attachChild(leftRingProximalHeadNode)
leftRingProximalHeadNode.attachChild(leftRingProximalTailNode)
leftRingProximalTailNode.attachChild(leftRingIntermediateNode)
leftRingIntermediateNode.attachChild(leftRingDistalNode)
leftHandNode.attachChild(leftLittleProximalHeadNode)
leftLittleProximalHeadNode.attachChild(leftLittleProximalTailNode)
leftLittleProximalTailNode.attachChild(leftLittleIntermediateNode)
leftLittleIntermediateNode.attachChild(leftLittleDistalNode)
rightHandNode.attachChild(rightThumbProximalHeadNode)
rightThumbProximalHeadNode.attachChild(rightThumbProximalTailNode)
rightThumbProximalTailNode.attachChild(rightThumbIntermediateNode)
rightThumbIntermediateNode.attachChild(rightThumbDistalNode)
rightHandNode.attachChild(rightIndexProximalHeadNode)
rightIndexProximalHeadNode.attachChild(rightIndexProximalTailNode)
rightIndexProximalTailNode.attachChild(rightIndexIntermediateNode)
rightIndexIntermediateNode.attachChild(rightIndexDistalNode)
rightHandNode.attachChild(rightMiddleProximalHeadNode)
rightMiddleProximalHeadNode.attachChild(rightMiddleProximalTailNode)
rightMiddleProximalTailNode.attachChild(rightMiddleIntermediateNode)
rightMiddleIntermediateNode.attachChild(rightMiddleDistalNode)
rightHandNode.attachChild(rightRingProximalHeadNode)
rightRingProximalHeadNode.attachChild(rightRingProximalTailNode)
rightRingProximalTailNode.attachChild(rightRingIntermediateNode)
rightRingIntermediateNode.attachChild(rightRingDistalNode)
rightHandNode.attachChild(rightLittleProximalHeadNode)
rightLittleProximalHeadNode.attachChild(rightLittleProximalTailNode)
rightLittleProximalTailNode.attachChild(rightLittleIntermediateNode)
rightLittleIntermediateNode.attachChild(rightLittleDistalNode)
}
fun update() {
@@ -91,10 +182,12 @@ class UnityArmature(localRot: Boolean) {
fun setGlobalRotationForBone(unityBone: UnityBone, globalRot: Quaternion) {
val node = getHeadNodeOfBone(unityBone)
if (node != null) {
node.localTransform.rotation = when (unityBone) {
UnityBone.LEFT_UPPER_ARM, UnityBone.LEFT_LOWER_ARM, UnityBone.LEFT_HAND -> globalRot * LEFT_SHOULDER_OFFSET
UnityBone.RIGHT_UPPER_ARM, UnityBone.RIGHT_LOWER_ARM, UnityBone.RIGHT_HAND -> globalRot * RIGHT_SHOULDER_OFFSET
else -> globalRot
node.localTransform.rotation = if (UnityBone.isLeftArmBone(unityBone)) {
globalRot * LEFT_SHOULDER_OFFSET
} else if (UnityBone.isRightArmBone(unityBone)) {
globalRot * RIGHT_SHOULDER_OFFSET
} else {
globalRot
}
}
}
@@ -105,10 +198,12 @@ class UnityArmature(localRot: Boolean) {
if (unityBone == UnityBone.HIPS) {
node.worldTransform.rotation = localRot
} else {
node.localTransform.rotation = when (unityBone) {
UnityBone.LEFT_UPPER_ARM -> localRot * RIGHT_SHOULDER_OFFSET
UnityBone.RIGHT_UPPER_ARM -> localRot * LEFT_SHOULDER_OFFSET
else -> localRot
node.localTransform.rotation = if (UnityBone.isLeftStartOfArmOrFingerBone(unityBone)) {
localRot * RIGHT_SHOULDER_OFFSET
} else if (UnityBone.isRightStartOfArmOrFingerBone(unityBone)) {
localRot * LEFT_SHOULDER_OFFSET
} else {
localRot
}
}
}
@@ -194,6 +289,36 @@ class UnityArmature(localRot: Boolean) {
UnityBone.RIGHT_LOWER_ARM -> rightElbowNode
UnityBone.LEFT_HAND -> leftWristNode
UnityBone.RIGHT_HAND -> rightWristNode
UnityBone.LEFT_THUMB_PROXIMAL -> leftThumbProximalHeadNode
UnityBone.LEFT_THUMB_INTERMEDIATE -> leftThumbProximalTailNode
UnityBone.LEFT_THUMB_DISTAL -> leftThumbIntermediateNode
UnityBone.LEFT_INDEX_PROXIMAL -> leftIndexProximalHeadNode
UnityBone.LEFT_INDEX_INTERMEDIATE -> leftIndexProximalTailNode
UnityBone.LEFT_INDEX_DISTAL -> leftIndexIntermediateNode
UnityBone.LEFT_MIDDLE_PROXIMAL -> leftMiddleProximalHeadNode
UnityBone.LEFT_MIDDLE_INTERMEDIATE -> leftMiddleProximalTailNode
UnityBone.LEFT_MIDDLE_DISTAL -> leftMiddleIntermediateNode
UnityBone.LEFT_RING_PROXIMAL -> leftRingProximalHeadNode
UnityBone.LEFT_RING_INTERMEDIATE -> leftRingProximalTailNode
UnityBone.LEFT_RING_DISTAL -> leftRingIntermediateNode
UnityBone.LEFT_LITTLE_PROXIMAL -> leftLittleProximalHeadNode
UnityBone.LEFT_LITTLE_INTERMEDIATE -> leftLittleProximalTailNode
UnityBone.LEFT_LITTLE_DISTAL -> leftLittleIntermediateNode
UnityBone.RIGHT_THUMB_PROXIMAL -> rightThumbProximalHeadNode
UnityBone.RIGHT_THUMB_INTERMEDIATE -> rightThumbProximalTailNode
UnityBone.RIGHT_THUMB_DISTAL -> rightThumbIntermediateNode
UnityBone.RIGHT_INDEX_PROXIMAL -> rightIndexProximalHeadNode
UnityBone.RIGHT_INDEX_INTERMEDIATE -> rightIndexProximalTailNode
UnityBone.RIGHT_INDEX_DISTAL -> rightIndexIntermediateNode
UnityBone.RIGHT_MIDDLE_PROXIMAL -> rightMiddleProximalHeadNode
UnityBone.RIGHT_MIDDLE_INTERMEDIATE -> rightMiddleProximalTailNode
UnityBone.RIGHT_MIDDLE_DISTAL -> rightMiddleIntermediateNode
UnityBone.RIGHT_RING_PROXIMAL -> rightRingProximalHeadNode
UnityBone.RIGHT_RING_INTERMEDIATE -> rightRingProximalTailNode
UnityBone.RIGHT_RING_DISTAL -> rightRingIntermediateNode
UnityBone.RIGHT_LITTLE_PROXIMAL -> rightLittleProximalHeadNode
UnityBone.RIGHT_LITTLE_INTERMEDIATE -> rightLittleProximalTailNode
UnityBone.RIGHT_LITTLE_DISTAL -> rightLittleIntermediateNode
else -> null
}
}

View File

@@ -38,36 +38,36 @@ enum class UnityBone(
LEFT_EYE("LeftEye", null, null),
RIGHT_EYE("RightEye", null, null),
JAW("Jaw", null, null),
LEFT_THUMB_PROXIMAL("LeftThumbProximal", null, null),
LEFT_THUMB_INTERMEDIATE("LeftThumbIntermediate", null, null),
LEFT_THUMB_DISTAL("LeftThumbDistal", null, null),
LEFT_INDEX_PROXIMAL("LeftIndexProximal", null, null),
LEFT_INDEX_INTERMEDIATE("LeftIndexIntermediate", null, null),
LEFT_INDEX_DISTAL("LeftIndexDistal", null, null),
LEFT_MIDDLE_PROXIMAL("LeftMiddleProximal", null, null),
LEFT_MIDDLE_INTERMEDIATE("LeftMiddleIntermediate", null, null),
LEFT_MIDDLE_DISTAL("LeftMiddleDistal", null, null),
LEFT_RING_PROXIMAL("LeftRingProximal", null, null),
LEFT_RING_INTERMEDIATE("LeftRingIntermediate", null, null),
LEFT_RING_DISTAL("LeftRingDistal", null, null),
LEFT_LITTLE_PROXIMAL("LeftLittleProximal", null, null),
LEFT_LITTLE_INTERMEDIATE("LeftLittleIntermediate", null, null),
LEFT_LITTLE_DISTAL("LeftLittleDistal", null, null),
RIGHT_THUMB_PROXIMAL("RightThumbProximal", null, null),
RIGHT_THUMB_INTERMEDIATE("RightThumbIntermediate", null, null),
RIGHT_THUMB_DISTAL("RightThumbDistal", null, null),
RIGHT_INDEX_PROXIMAL("RightIndexProximal", null, null),
RIGHT_INDEX_INTERMEDIATE("RightIndexIntermediate", null, null),
RIGHT_INDEX_DISTAL("RightIndexDistal", null, null),
RIGHT_MIDDLE_PROXIMAL("RightMiddleProximal", null, null),
RIGHT_MIDDLE_INTERMEDIATE("RightMiddleIntermediate", null, null),
RIGHT_MIDDLE_DISTAL("RightMiddleDistal", null, null),
RIGHT_RING_PROXIMAL("RightRingProximal", null, null),
RIGHT_RING_INTERMEDIATE("RightRingIntermediate", null, null),
RIGHT_RING_DISTAL("RightRingDistal", null, null),
RIGHT_LITTLE_PROXIMAL("RightLittleProximal", null, null),
RIGHT_LITTLE_INTERMEDIATE("RightLittleIntermediate", null, null),
RIGHT_LITTLE_DISTAL("RightLittleDistal", null, null),
LEFT_THUMB_PROXIMAL("LeftThumbProximal", BoneType.LEFT_THUMB_PROXIMAL, TrackerPosition.LEFT_THUMB_PROXIMAL),
LEFT_THUMB_INTERMEDIATE("LeftThumbIntermediate", BoneType.LEFT_THUMB_INTERMEDIATE, TrackerPosition.LEFT_THUMB_INTERMEDIATE),
LEFT_THUMB_DISTAL("LeftThumbDistal", BoneType.LEFT_THUMB_DISTAL, TrackerPosition.LEFT_THUMB_DISTAL),
LEFT_INDEX_PROXIMAL("LeftIndexProximal", BoneType.LEFT_INDEX_PROXIMAL, TrackerPosition.LEFT_INDEX_PROXIMAL),
LEFT_INDEX_INTERMEDIATE("LeftIndexIntermediate", BoneType.LEFT_INDEX_INTERMEDIATE, TrackerPosition.LEFT_INDEX_INTERMEDIATE),
LEFT_INDEX_DISTAL("LeftIndexDistal", BoneType.LEFT_INDEX_DISTAL, TrackerPosition.LEFT_INDEX_DISTAL),
LEFT_MIDDLE_PROXIMAL("LeftMiddleProximal", BoneType.LEFT_MIDDLE_PROXIMAL, TrackerPosition.LEFT_MIDDLE_PROXIMAL),
LEFT_MIDDLE_INTERMEDIATE("LeftMiddleIntermediate", BoneType.LEFT_MIDDLE_INTERMEDIATE, TrackerPosition.LEFT_MIDDLE_INTERMEDIATE),
LEFT_MIDDLE_DISTAL("LeftMiddleDistal", BoneType.LEFT_MIDDLE_DISTAL, TrackerPosition.LEFT_MIDDLE_DISTAL),
LEFT_RING_PROXIMAL("LeftRingProximal", BoneType.LEFT_RING_PROXIMAL, TrackerPosition.LEFT_RING_PROXIMAL),
LEFT_RING_INTERMEDIATE("LeftRingIntermediate", BoneType.LEFT_RING_INTERMEDIATE, TrackerPosition.LEFT_RING_INTERMEDIATE),
LEFT_RING_DISTAL("LeftRingDistal", BoneType.LEFT_RING_DISTAL, TrackerPosition.LEFT_RING_DISTAL),
LEFT_LITTLE_PROXIMAL("LeftLittleProximal", BoneType.LEFT_LITTLE_PROXIMAL, TrackerPosition.LEFT_LITTLE_PROXIMAL),
LEFT_LITTLE_INTERMEDIATE("LeftLittleIntermediate", BoneType.LEFT_LITTLE_INTERMEDIATE, TrackerPosition.LEFT_LITTLE_INTERMEDIATE),
LEFT_LITTLE_DISTAL("LeftLittleDistal", BoneType.LEFT_LITTLE_DISTAL, TrackerPosition.LEFT_LITTLE_DISTAL),
RIGHT_THUMB_PROXIMAL("RightThumbProximal", BoneType.RIGHT_THUMB_PROXIMAL, TrackerPosition.RIGHT_THUMB_PROXIMAL),
RIGHT_THUMB_INTERMEDIATE("RightThumbIntermediate", BoneType.RIGHT_THUMB_INTERMEDIATE, TrackerPosition.RIGHT_THUMB_INTERMEDIATE),
RIGHT_THUMB_DISTAL("RightThumbDistal", BoneType.RIGHT_THUMB_DISTAL, TrackerPosition.RIGHT_THUMB_DISTAL),
RIGHT_INDEX_PROXIMAL("RightIndexProximal", BoneType.RIGHT_INDEX_PROXIMAL, TrackerPosition.RIGHT_INDEX_PROXIMAL),
RIGHT_INDEX_INTERMEDIATE("RightIndexIntermediate", BoneType.RIGHT_INDEX_INTERMEDIATE, TrackerPosition.RIGHT_INDEX_INTERMEDIATE),
RIGHT_INDEX_DISTAL("RightIndexDistal", BoneType.RIGHT_INDEX_DISTAL, TrackerPosition.RIGHT_INDEX_DISTAL),
RIGHT_MIDDLE_PROXIMAL("RightMiddleProximal", BoneType.RIGHT_MIDDLE_PROXIMAL, TrackerPosition.RIGHT_MIDDLE_PROXIMAL),
RIGHT_MIDDLE_INTERMEDIATE("RightMiddleIntermediate", BoneType.RIGHT_MIDDLE_INTERMEDIATE, TrackerPosition.RIGHT_MIDDLE_INTERMEDIATE),
RIGHT_MIDDLE_DISTAL("RightMiddleDistal", BoneType.RIGHT_MIDDLE_DISTAL, TrackerPosition.RIGHT_MIDDLE_DISTAL),
RIGHT_RING_PROXIMAL("RightRingProximal", BoneType.RIGHT_RING_PROXIMAL, TrackerPosition.RIGHT_RING_PROXIMAL),
RIGHT_RING_INTERMEDIATE("RightRingIntermediate", BoneType.RIGHT_RING_INTERMEDIATE, TrackerPosition.RIGHT_RING_INTERMEDIATE),
RIGHT_RING_DISTAL("RightRingDistal", BoneType.RIGHT_RING_DISTAL, TrackerPosition.RIGHT_RING_DISTAL),
RIGHT_LITTLE_PROXIMAL("RightLittleProximal", BoneType.RIGHT_LITTLE_PROXIMAL, TrackerPosition.RIGHT_LITTLE_PROXIMAL),
RIGHT_LITTLE_INTERMEDIATE("RightLittleIntermediate", BoneType.RIGHT_LITTLE_INTERMEDIATE, TrackerPosition.RIGHT_LITTLE_INTERMEDIATE),
RIGHT_LITTLE_DISTAL("RightLittleDistal", BoneType.RIGHT_LITTLE_DISTAL, TrackerPosition.RIGHT_LITTLE_DISTAL),
LAST_BONE("LastBone", null, null),
;
@@ -76,5 +76,107 @@ enum class UnityBone(
@JvmStatic
fun getByStringVal(stringVal: String): UnityBone? = byStringVal[stringVal.lowercase()]
/**
* Returns the bone on the opposite limb, or the original bone if
* it not a limb bone.
*/
fun tryGetOppositeArmBone(bone: UnityBone): UnityBone = when (bone) {
LEFT_SHOULDER -> RIGHT_SHOULDER
LEFT_UPPER_ARM -> RIGHT_UPPER_ARM
LEFT_LOWER_ARM -> RIGHT_LOWER_ARM
LEFT_HAND -> RIGHT_HAND
RIGHT_SHOULDER -> LEFT_SHOULDER
RIGHT_UPPER_ARM -> LEFT_UPPER_ARM
RIGHT_LOWER_ARM -> LEFT_LOWER_ARM
RIGHT_HAND -> LEFT_HAND
LEFT_UPPER_LEG -> RIGHT_UPPER_LEG
LEFT_LOWER_LEG -> RIGHT_LOWER_LEG
LEFT_FOOT -> RIGHT_FOOT
RIGHT_UPPER_LEG -> LEFT_UPPER_LEG
RIGHT_LOWER_LEG -> LEFT_LOWER_LEG
RIGHT_FOOT -> LEFT_FOOT
LEFT_THUMB_PROXIMAL -> RIGHT_THUMB_PROXIMAL
LEFT_THUMB_INTERMEDIATE -> RIGHT_THUMB_INTERMEDIATE
LEFT_THUMB_DISTAL -> RIGHT_THUMB_DISTAL
LEFT_INDEX_PROXIMAL -> RIGHT_INDEX_PROXIMAL
LEFT_INDEX_INTERMEDIATE -> RIGHT_INDEX_INTERMEDIATE
LEFT_INDEX_DISTAL -> RIGHT_INDEX_DISTAL
LEFT_MIDDLE_PROXIMAL -> RIGHT_MIDDLE_PROXIMAL
LEFT_MIDDLE_INTERMEDIATE -> RIGHT_MIDDLE_INTERMEDIATE
LEFT_MIDDLE_DISTAL -> RIGHT_MIDDLE_DISTAL
LEFT_RING_PROXIMAL -> RIGHT_RING_PROXIMAL
LEFT_RING_INTERMEDIATE -> RIGHT_RING_INTERMEDIATE
LEFT_RING_DISTAL -> RIGHT_RING_DISTAL
LEFT_LITTLE_PROXIMAL -> RIGHT_LITTLE_PROXIMAL
LEFT_LITTLE_INTERMEDIATE -> RIGHT_LITTLE_INTERMEDIATE
LEFT_LITTLE_DISTAL -> RIGHT_LITTLE_DISTAL
RIGHT_THUMB_PROXIMAL -> LEFT_THUMB_PROXIMAL
RIGHT_THUMB_INTERMEDIATE -> LEFT_THUMB_INTERMEDIATE
RIGHT_THUMB_DISTAL -> LEFT_THUMB_DISTAL
RIGHT_INDEX_PROXIMAL -> LEFT_INDEX_PROXIMAL
RIGHT_INDEX_INTERMEDIATE -> LEFT_INDEX_INTERMEDIATE
RIGHT_INDEX_DISTAL -> LEFT_INDEX_DISTAL
RIGHT_MIDDLE_PROXIMAL -> LEFT_MIDDLE_PROXIMAL
RIGHT_MIDDLE_INTERMEDIATE -> LEFT_MIDDLE_INTERMEDIATE
RIGHT_MIDDLE_DISTAL -> LEFT_MIDDLE_DISTAL
RIGHT_RING_PROXIMAL -> LEFT_RING_PROXIMAL
RIGHT_RING_INTERMEDIATE -> LEFT_RING_INTERMEDIATE
RIGHT_RING_DISTAL -> LEFT_RING_DISTAL
RIGHT_LITTLE_PROXIMAL -> LEFT_LITTLE_PROXIMAL
RIGHT_LITTLE_INTERMEDIATE -> LEFT_LITTLE_INTERMEDIATE
RIGHT_LITTLE_DISTAL -> LEFT_LITTLE_DISTAL
else -> bone
}
/**
* Returns true if the bone is part of the left arm (incl. fingers, excl. shoulder)
*/
fun isLeftArmBone(bone: UnityBone): Boolean = bone == LEFT_UPPER_ARM || bone == LEFT_LOWER_ARM || bone == LEFT_HAND ||
bone == LEFT_THUMB_PROXIMAL || bone == LEFT_THUMB_INTERMEDIATE || bone == LEFT_THUMB_DISTAL ||
bone == LEFT_INDEX_PROXIMAL || bone == LEFT_INDEX_INTERMEDIATE || bone == LEFT_INDEX_DISTAL ||
bone == LEFT_MIDDLE_PROXIMAL || bone == LEFT_MIDDLE_INTERMEDIATE || bone == LEFT_MIDDLE_DISTAL ||
bone == LEFT_RING_PROXIMAL || bone == LEFT_RING_INTERMEDIATE || bone == LEFT_RING_DISTAL ||
bone == LEFT_LITTLE_PROXIMAL || bone == LEFT_LITTLE_INTERMEDIATE || bone == LEFT_LITTLE_DISTAL
/**
* Returns true if the bone is part of the right arm (incl. fingers, excl. shoulder)
*/
fun isRightArmBone(bone: UnityBone): Boolean = bone == RIGHT_UPPER_ARM || bone == RIGHT_LOWER_ARM || bone == RIGHT_HAND ||
bone == RIGHT_THUMB_PROXIMAL || bone == RIGHT_THUMB_INTERMEDIATE || bone == RIGHT_THUMB_DISTAL ||
bone == RIGHT_INDEX_PROXIMAL || bone == RIGHT_INDEX_INTERMEDIATE || bone == RIGHT_INDEX_DISTAL ||
bone == RIGHT_MIDDLE_PROXIMAL || bone == RIGHT_MIDDLE_INTERMEDIATE || bone == RIGHT_MIDDLE_DISTAL ||
bone == RIGHT_RING_PROXIMAL || bone == RIGHT_RING_INTERMEDIATE || bone == RIGHT_RING_DISTAL ||
bone == RIGHT_LITTLE_PROXIMAL || bone == RIGHT_LITTLE_INTERMEDIATE || bone == RIGHT_LITTLE_DISTAL
/**
* Returns true if the bone is the left upper arm or proximal left finger bone
*/
fun isLeftStartOfArmOrFingerBone(bone: UnityBone): Boolean = bone == LEFT_UPPER_ARM || bone == LEFT_THUMB_PROXIMAL ||
bone == LEFT_INDEX_PROXIMAL || bone == LEFT_MIDDLE_PROXIMAL || bone == LEFT_RING_PROXIMAL || bone == LEFT_LITTLE_PROXIMAL
/**
* Returns true if the bone is the right upper arm or proximal right finger bone
*/
fun isRightStartOfArmOrFingerBone(bone: UnityBone): Boolean = bone == RIGHT_UPPER_ARM || bone == RIGHT_THUMB_PROXIMAL ||
bone == RIGHT_INDEX_PROXIMAL || bone == RIGHT_MIDDLE_PROXIMAL || bone == RIGHT_RING_PROXIMAL || bone == RIGHT_LITTLE_PROXIMAL
/**
* Returns true if the bone is part of the left fingers
*/
fun isLeftFingerBone(bone: UnityBone): Boolean = bone == LEFT_THUMB_PROXIMAL || bone == LEFT_THUMB_INTERMEDIATE || bone == LEFT_THUMB_DISTAL ||
bone == LEFT_INDEX_PROXIMAL || bone == LEFT_INDEX_INTERMEDIATE || bone == LEFT_INDEX_DISTAL ||
bone == LEFT_MIDDLE_PROXIMAL || bone == LEFT_MIDDLE_INTERMEDIATE || bone == LEFT_MIDDLE_DISTAL ||
bone == LEFT_RING_PROXIMAL || bone == LEFT_RING_INTERMEDIATE || bone == LEFT_RING_DISTAL ||
bone == LEFT_LITTLE_PROXIMAL || bone == LEFT_LITTLE_INTERMEDIATE || bone == LEFT_LITTLE_DISTAL
/**
* Returns true if the bone part of the right fingers
*/
fun isRightFingerBone(bone: UnityBone): Boolean = bone == RIGHT_THUMB_PROXIMAL || bone == RIGHT_THUMB_INTERMEDIATE || bone == RIGHT_THUMB_DISTAL ||
bone == RIGHT_INDEX_PROXIMAL || bone == RIGHT_INDEX_INTERMEDIATE || bone == RIGHT_INDEX_DISTAL ||
bone == RIGHT_MIDDLE_PROXIMAL || bone == RIGHT_MIDDLE_INTERMEDIATE || bone == RIGHT_MIDDLE_DISTAL ||
bone == RIGHT_RING_PROXIMAL || bone == RIGHT_RING_INTERMEDIATE || bone == RIGHT_RING_DISTAL ||
bone == RIGHT_LITTLE_PROXIMAL || bone == RIGHT_LITTLE_INTERMEDIATE || bone == RIGHT_LITTLE_DISTAL
}
}

View File

@@ -345,13 +345,13 @@ class VMCHandler(
oscBundle.addPacket(OSCMessage("/VMC/Ext/Root/Pos", oscArgs.clone()))
for (unityBone in UnityBone.entries) {
if (unityBone.boneType == null) continue
// Get opposite bone if tracking must be mirrored
val boneType = (if (mirrorTracking) tryGetOppositeArmBone(unityBone) else unityBone).boneType
val boneType = (if (mirrorTracking) UnityBone.tryGetOppositeArmBone(unityBone) else unityBone).boneType
if (boneType == null) continue
// Get SlimeVR bone
val bone = humanPoseManager.getBone(boneType!!)
val bone = humanPoseManager.getBone(boneType)
// Update unity hierarchy from bone's global rotation
val boneRotation = if (mirrorTracking) {
@@ -384,20 +384,28 @@ class VMCHandler(
}
// Update Unity skeleton
outputUnityArmature!!.update()
outputUnityArmature?.update()
// Add Unity humanoid bones transforms
for (bone in UnityBone.entries) {
if (bone.boneType != null &&
!(humanPoseManager.isTrackingLeftArmFromController && isLeftArmUnityBone(bone)) &&
!(humanPoseManager.isTrackingRightArmFromController && isRightArmUnityBone(bone))
for (unityBone in UnityBone.entries) {
// Don't send bones for which we don't have an equivalent
// Don't send fingers if we don't have any tracker for them
// Don't send arm bones if we're tracking from the controller
if (unityBone.boneType != null &&
(!UnityBone.isLeftFingerBone(unityBone) || humanPoseManager.skeleton.hasLeftFingerTracker || (mirrorTracking && humanPoseManager.skeleton.hasRightFingerTracker)) &&
(!UnityBone.isRightFingerBone(unityBone) || humanPoseManager.skeleton.hasRightFingerTracker || (mirrorTracking && humanPoseManager.skeleton.hasLeftFingerTracker)) &&
!(humanPoseManager.isTrackingLeftArmFromController && (UnityBone.isLeftArmBone(unityBone) || unityBone == UnityBone.LEFT_SHOULDER)) &&
!(humanPoseManager.isTrackingRightArmFromController && (UnityBone.isRightArmBone(unityBone) || unityBone == UnityBone.RIGHT_SHOULDER))
) {
oscArgs.clear()
oscArgs.add(bone.stringVal)
addTransformToArgs(
outputUnityArmature!!.getLocalTranslationForBone(bone),
outputUnityArmature!!.getLocalRotationForBone(bone),
)
oscArgs.add(unityBone.stringVal)
outputUnityArmature?.let {
addTransformToArgs(
it.getLocalTranslationForBone(unityBone),
it.getLocalRotationForBone(unityBone),
)
}
oscBundle.addPacket(OSCMessage("/VMC/Ext/Bone/Pos", oscArgs.clone()))
}
}
@@ -491,32 +499,6 @@ class VMCHandler(
oscArgs.add(-rot.w)
}
/**
* Returns the bone on the opposite limb, or the original bone if
* it not a limb bone.
*/
private fun tryGetOppositeArmBone(bone: UnityBone): UnityBone = when (bone) {
UnityBone.LEFT_SHOULDER -> UnityBone.RIGHT_SHOULDER
UnityBone.LEFT_UPPER_ARM -> UnityBone.RIGHT_UPPER_ARM
UnityBone.LEFT_LOWER_ARM -> UnityBone.RIGHT_LOWER_ARM
UnityBone.LEFT_HAND -> UnityBone.RIGHT_HAND
UnityBone.RIGHT_SHOULDER -> UnityBone.LEFT_SHOULDER
UnityBone.RIGHT_UPPER_ARM -> UnityBone.LEFT_UPPER_ARM
UnityBone.RIGHT_LOWER_ARM -> UnityBone.LEFT_LOWER_ARM
UnityBone.RIGHT_HAND -> UnityBone.LEFT_HAND
UnityBone.LEFT_UPPER_LEG -> UnityBone.RIGHT_UPPER_LEG
UnityBone.LEFT_LOWER_LEG -> UnityBone.RIGHT_LOWER_LEG
UnityBone.LEFT_FOOT -> UnityBone.RIGHT_FOOT
UnityBone.RIGHT_UPPER_LEG -> UnityBone.LEFT_UPPER_LEG
UnityBone.RIGHT_LOWER_LEG -> UnityBone.LEFT_LOWER_LEG
UnityBone.RIGHT_FOOT -> UnityBone.LEFT_FOOT
else -> bone
}
private fun isLeftArmUnityBone(bone: UnityBone): Boolean = bone == UnityBone.LEFT_SHOULDER || bone == UnityBone.LEFT_UPPER_ARM || bone == UnityBone.LEFT_LOWER_ARM || bone == UnityBone.LEFT_HAND
private fun isRightArmUnityBone(bone: UnityBone): Boolean = bone == UnityBone.RIGHT_SHOULDER || bone == UnityBone.RIGHT_UPPER_ARM || bone == UnityBone.RIGHT_LOWER_ARM || bone == UnityBone.RIGHT_HAND
override fun getOscSender(): OSCPortOut = oscSender!!
override fun getPortOut(): Int = lastPortOut

View File

@@ -59,7 +59,7 @@ public class DataFeedBuilder {
)
);
HardwareInfo.addNetworkProtocolVersion(fbb, udpDevice.firmwareBuild);
HardwareInfo.addNetworkProtocolVersion(fbb, udpDevice.protocolVersion);
}
// BRUH MOMENT
@@ -142,6 +142,8 @@ public class DataFeedBuilder {
TrackerInfo.addMagnetometer(fbb, tracker.getMagStatus().getSolarType());
TrackerInfo.addIsHmd(fbb, tracker.isHmd());
TrackerInfo.addDataSupport(fbb, tracker.getTrackerDataType().getSolarType());
return TrackerInfo.endTrackerInfo(fbb);
}

View File

@@ -39,7 +39,37 @@ public enum BoneType {
LEFT_HAND(BodyPart.LEFT_HAND),
RIGHT_HAND(BodyPart.RIGHT_HAND),
LEFT_HAND_TRACKER,
RIGHT_HAND_TRACKER;
RIGHT_HAND_TRACKER,
LEFT_THUMB_PROXIMAL(BodyPart.LEFT_THUMB_PROXIMAL),
LEFT_THUMB_INTERMEDIATE(BodyPart.LEFT_THUMB_INTERMEDIATE),
LEFT_THUMB_DISTAL(BodyPart.LEFT_THUMB_DISTAL),
LEFT_INDEX_PROXIMAL(BodyPart.LEFT_INDEX_PROXIMAL),
LEFT_INDEX_INTERMEDIATE(BodyPart.LEFT_INDEX_INTERMEDIATE),
LEFT_INDEX_DISTAL(BodyPart.LEFT_INDEX_DISTAL),
LEFT_MIDDLE_PROXIMAL(BodyPart.LEFT_MIDDLE_PROXIMAL),
LEFT_MIDDLE_INTERMEDIATE(BodyPart.LEFT_MIDDLE_INTERMEDIATE),
LEFT_MIDDLE_DISTAL(BodyPart.LEFT_MIDDLE_DISTAL),
LEFT_RING_PROXIMAL(BodyPart.LEFT_RING_PROXIMAL),
LEFT_RING_INTERMEDIATE(BodyPart.LEFT_RING_INTERMEDIATE),
LEFT_RING_DISTAL(BodyPart.LEFT_RING_DISTAL),
LEFT_LITTLE_PROXIMAL(BodyPart.LEFT_LITTLE_PROXIMAL),
LEFT_LITTLE_INTERMEDIATE(BodyPart.LEFT_LITTLE_INTERMEDIATE),
LEFT_LITTLE_DISTAL(BodyPart.LEFT_LITTLE_DISTAL),
RIGHT_THUMB_PROXIMAL(BodyPart.RIGHT_THUMB_PROXIMAL),
RIGHT_THUMB_INTERMEDIATE(BodyPart.RIGHT_THUMB_INTERMEDIATE),
RIGHT_THUMB_DISTAL(BodyPart.RIGHT_THUMB_DISTAL),
RIGHT_INDEX_PROXIMAL(BodyPart.RIGHT_INDEX_PROXIMAL),
RIGHT_INDEX_INTERMEDIATE(BodyPart.RIGHT_INDEX_INTERMEDIATE),
RIGHT_INDEX_DISTAL(BodyPart.RIGHT_INDEX_DISTAL),
RIGHT_MIDDLE_PROXIMAL(BodyPart.RIGHT_MIDDLE_PROXIMAL),
RIGHT_MIDDLE_INTERMEDIATE(BodyPart.RIGHT_MIDDLE_INTERMEDIATE),
RIGHT_MIDDLE_DISTAL(BodyPart.RIGHT_MIDDLE_DISTAL),
RIGHT_RING_PROXIMAL(BodyPart.RIGHT_RING_PROXIMAL),
RIGHT_RING_INTERMEDIATE(BodyPart.RIGHT_RING_INTERMEDIATE),
RIGHT_RING_DISTAL(BodyPart.RIGHT_RING_DISTAL),
RIGHT_LITTLE_PROXIMAL(BodyPart.RIGHT_LITTLE_PROXIMAL),
RIGHT_LITTLE_INTERMEDIATE(BodyPart.RIGHT_LITTLE_INTERMEDIATE),
RIGHT_LITTLE_DISTAL(BodyPart.RIGHT_LITTLE_DISTAL);
public static final BoneType[] values = values();

View File

@@ -287,6 +287,51 @@ class SkeletonConfigManager(
0f,
)
BoneType.LEFT_THUMB_PROXIMAL, BoneType.LEFT_THUMB_INTERMEDIATE, BoneType.LEFT_THUMB_DISTAL,
BoneType.RIGHT_THUMB_PROXIMAL, BoneType.RIGHT_THUMB_INTERMEDIATE, BoneType.RIGHT_THUMB_DISTAL,
-> setNodeOffset(
nodeOffset,
0f,
-getOffset(SkeletonConfigOffsets.HAND_Y) * 0.2f,
-getOffset(SkeletonConfigOffsets.HAND_Y) * 0.1f,
)
BoneType.LEFT_INDEX_PROXIMAL, BoneType.LEFT_INDEX_INTERMEDIATE, BoneType.LEFT_INDEX_DISTAL,
BoneType.RIGHT_INDEX_PROXIMAL, BoneType.RIGHT_INDEX_INTERMEDIATE, BoneType.RIGHT_INDEX_DISTAL,
-> setNodeOffset(
nodeOffset,
0f,
-getOffset(SkeletonConfigOffsets.HAND_Y) * 0.25f,
0f,
)
BoneType.LEFT_MIDDLE_PROXIMAL, BoneType.LEFT_MIDDLE_INTERMEDIATE, BoneType.LEFT_MIDDLE_DISTAL,
BoneType.RIGHT_MIDDLE_PROXIMAL, BoneType.RIGHT_MIDDLE_INTERMEDIATE, BoneType.RIGHT_MIDDLE_DISTAL,
-> setNodeOffset(
nodeOffset,
0f,
-getOffset(SkeletonConfigOffsets.HAND_Y) * 0.3f,
0f,
)
BoneType.LEFT_RING_PROXIMAL, BoneType.LEFT_RING_INTERMEDIATE, BoneType.LEFT_RING_DISTAL,
BoneType.RIGHT_RING_PROXIMAL, BoneType.RIGHT_RING_INTERMEDIATE, BoneType.RIGHT_RING_DISTAL,
-> setNodeOffset(
nodeOffset,
0f,
-getOffset(SkeletonConfigOffsets.HAND_Y) * 0.28f,
0f,
)
BoneType.LEFT_LITTLE_PROXIMAL, BoneType.LEFT_LITTLE_INTERMEDIATE, BoneType.LEFT_LITTLE_DISTAL,
BoneType.RIGHT_LITTLE_PROXIMAL, BoneType.RIGHT_LITTLE_INTERMEDIATE, BoneType.RIGHT_LITTLE_DISTAL,
-> setNodeOffset(
nodeOffset,
0f,
-getOffset(SkeletonConfigOffsets.HAND_Y) * 0.2f,
0f,
)
else -> {}
}
}

View File

@@ -12,6 +12,7 @@ import dev.slimevr.tracking.trackers.TrackerPosition
import dev.slimevr.tracking.trackers.TrackerRole
import dev.slimevr.tracking.trackers.TrackerUtils.getFirstAvailableTracker
import dev.slimevr.tracking.trackers.TrackerUtils.getTrackerForSkeleton
import dev.slimevr.tracking.trackers.udp.TrackerDataType
import dev.slimevr.util.ann.VRServerThread
import io.eiren.util.ann.ThreadSafe
import io.eiren.util.collections.FastList
@@ -61,6 +62,38 @@ class HumanSkeleton(
val leftHandBone = Bone(BoneType.LEFT_HAND)
val rightHandBone = Bone(BoneType.RIGHT_HAND)
// Finger bones
val leftThumbProximalBone = Bone(BoneType.LEFT_THUMB_PROXIMAL)
val leftThumbIntermediateBone = Bone(BoneType.LEFT_THUMB_INTERMEDIATE)
val leftThumbDistalBone = Bone(BoneType.LEFT_THUMB_DISTAL)
val leftIndexProximalBone = Bone(BoneType.LEFT_INDEX_PROXIMAL)
val leftIndexIntermediateBone = Bone(BoneType.LEFT_INDEX_INTERMEDIATE)
val leftIndexDistalBone = Bone(BoneType.LEFT_INDEX_DISTAL)
val leftMiddleProximalBone = Bone(BoneType.LEFT_MIDDLE_PROXIMAL)
val leftMiddleIntermediateBone = Bone(BoneType.LEFT_MIDDLE_INTERMEDIATE)
val leftMiddleDistalBone = Bone(BoneType.LEFT_MIDDLE_DISTAL)
val leftRingProximalBone = Bone(BoneType.LEFT_RING_PROXIMAL)
val leftRingIntermediateBone = Bone(BoneType.LEFT_RING_INTERMEDIATE)
val leftRingDistalBone = Bone(BoneType.LEFT_RING_DISTAL)
val leftLittleProximalBone = Bone(BoneType.LEFT_LITTLE_PROXIMAL)
val leftLittleIntermediateBone = Bone(BoneType.LEFT_LITTLE_INTERMEDIATE)
val leftLittleDistalBone = Bone(BoneType.LEFT_LITTLE_DISTAL)
val rightThumbProximalBone = Bone(BoneType.RIGHT_THUMB_PROXIMAL)
val rightThumbIntermediateBone = Bone(BoneType.RIGHT_THUMB_INTERMEDIATE)
val rightThumbDistalBone = Bone(BoneType.RIGHT_THUMB_DISTAL)
val rightIndexProximalBone = Bone(BoneType.RIGHT_INDEX_PROXIMAL)
val rightIndexIntermediateBone = Bone(BoneType.RIGHT_INDEX_INTERMEDIATE)
val rightIndexDistalBone = Bone(BoneType.RIGHT_INDEX_DISTAL)
val rightMiddleProximalBone = Bone(BoneType.RIGHT_MIDDLE_PROXIMAL)
val rightMiddleIntermediateBone = Bone(BoneType.RIGHT_MIDDLE_INTERMEDIATE)
val rightMiddleDistalBone = Bone(BoneType.RIGHT_MIDDLE_DISTAL)
val rightRingProximalBone = Bone(BoneType.RIGHT_RING_PROXIMAL)
val rightRingIntermediateBone = Bone(BoneType.RIGHT_RING_INTERMEDIATE)
val rightRingDistalBone = Bone(BoneType.RIGHT_RING_DISTAL)
val rightLittleProximalBone = Bone(BoneType.RIGHT_LITTLE_PROXIMAL)
val rightLittleIntermediateBone = Bone(BoneType.RIGHT_LITTLE_INTERMEDIATE)
val rightLittleDistalBone = Bone(BoneType.RIGHT_LITTLE_DISTAL)
// Tracker bones
val headTrackerBone = Bone(BoneType.HEAD_TRACKER)
val chestTrackerBone = Bone(BoneType.CHEST_TRACKER)
@@ -77,12 +110,10 @@ class HumanSkeleton(
// Buffers
var hasSpineTracker = false
var hasKneeTrackers = false
var hasLeftLegTracker = false
var hasRightLegTracker = false
var hasLeftFootTracker = false
var hasRightFootTracker = false
var hasLeftArmTracker = false
var hasRightArmTracker = false
var hasLeftFingerTracker = false
var hasRightFingerTracker = false
// Input trackers
var headTracker: Tracker? by Delegates.observable(null) { _, old, new ->
@@ -110,6 +141,36 @@ class HumanSkeleton(
var rightHandTracker: Tracker? = null
var leftShoulderTracker: Tracker? = null
var rightShoulderTracker: Tracker? = null
var leftThumbProximalTracker: Tracker? = null
var leftThumbIntermediateTracker: Tracker? = null
var leftThumbDistalTracker: Tracker? = null
var leftIndexProximalTracker: Tracker? = null
var leftIndexIntermediateTracker: Tracker? = null
var leftIndexDistalTracker: Tracker? = null
var leftMiddleProximalTracker: Tracker? = null
var leftMiddleIntermediateTracker: Tracker? = null
var leftMiddleDistalTracker: Tracker? = null
var leftRingProximalTracker: Tracker? = null
var leftRingIntermediateTracker: Tracker? = null
var leftRingDistalTracker: Tracker? = null
var leftLittleProximalTracker: Tracker? = null
var leftLittleIntermediateTracker: Tracker? = null
var leftLittleDistalTracker: Tracker? = null
var rightThumbProximalTracker: Tracker? = null
var rightThumbIntermediateTracker: Tracker? = null
var rightThumbDistalTracker: Tracker? = null
var rightIndexProximalTracker: Tracker? = null
var rightIndexIntermediateTracker: Tracker? = null
var rightIndexDistalTracker: Tracker? = null
var rightMiddleProximalTracker: Tracker? = null
var rightMiddleIntermediateTracker: Tracker? = null
var rightMiddleDistalTracker: Tracker? = null
var rightRingProximalTracker: Tracker? = null
var rightRingIntermediateTracker: Tracker? = null
var rightRingDistalTracker: Tracker? = null
var rightLittleProximalTracker: Tracker? = null
var rightLittleIntermediateTracker: Tracker? = null
var rightLittleDistalTracker: Tracker? = null
// Output trackers
var computedHeadTracker: Tracker? = null
@@ -259,24 +320,63 @@ class HumanSkeleton(
rightLowerArmBone.attachChild(rightHandBone)
rightHandBone.attachChild(rightHandTrackerBone)
}
// Fingers
leftHandBone.attachChild(leftThumbProximalBone)
leftThumbProximalBone.attachChild(leftThumbIntermediateBone)
leftThumbIntermediateBone.attachChild(leftThumbDistalBone)
leftHandBone.attachChild(leftIndexProximalBone)
leftIndexProximalBone.attachChild(leftIndexIntermediateBone)
leftIndexIntermediateBone.attachChild(leftIndexDistalBone)
leftHandBone.attachChild(leftMiddleProximalBone)
leftMiddleProximalBone.attachChild(leftMiddleIntermediateBone)
leftMiddleIntermediateBone.attachChild(leftMiddleDistalBone)
leftHandBone.attachChild(leftRingProximalBone)
leftRingProximalBone.attachChild(leftRingIntermediateBone)
leftRingIntermediateBone.attachChild(leftRingDistalBone)
leftHandBone.attachChild(leftLittleProximalBone)
leftLittleProximalBone.attachChild(leftLittleIntermediateBone)
leftLittleIntermediateBone.attachChild(leftLittleDistalBone)
rightHandBone.attachChild(rightThumbProximalBone)
rightThumbProximalBone.attachChild(rightThumbIntermediateBone)
rightThumbIntermediateBone.attachChild(rightThumbDistalBone)
rightHandBone.attachChild(rightIndexProximalBone)
rightIndexProximalBone.attachChild(rightIndexIntermediateBone)
rightIndexIntermediateBone.attachChild(rightIndexDistalBone)
rightHandBone.attachChild(rightMiddleProximalBone)
rightMiddleProximalBone.attachChild(rightMiddleIntermediateBone)
rightMiddleIntermediateBone.attachChild(rightMiddleDistalBone)
rightHandBone.attachChild(rightRingProximalBone)
rightRingProximalBone.attachChild(rightRingIntermediateBone)
rightRingIntermediateBone.attachChild(rightRingDistalBone)
rightHandBone.attachChild(rightLittleProximalBone)
rightLittleProximalBone.attachChild(rightLittleIntermediateBone)
rightLittleIntermediateBone.attachChild(rightLittleDistalBone)
}
/**
* Set input trackers from a list
*/
fun setTrackersFromList(trackers: List<Tracker>) {
// Head
headTracker = getTrackerForSkeleton(trackers, TrackerPosition.HEAD)
neckTracker = getTrackerForSkeleton(trackers, TrackerPosition.NECK)
// Spine
upperChestTracker = getTrackerForSkeleton(trackers, TrackerPosition.UPPER_CHEST)
chestTracker = getTrackerForSkeleton(trackers, TrackerPosition.CHEST)
waistTracker = getTrackerForSkeleton(trackers, TrackerPosition.WAIST)
hipTracker = getTrackerForSkeleton(trackers, TrackerPosition.HIP)
// Legs
leftUpperLegTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_UPPER_LEG)
leftLowerLegTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_LOWER_LEG)
leftFootTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_FOOT)
rightUpperLegTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_UPPER_LEG)
rightLowerLegTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_LOWER_LEG)
rightFootTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_FOOT)
// Arms
leftLowerArmTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_LOWER_ARM)
rightLowerArmTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_LOWER_ARM)
leftUpperArmTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_UPPER_ARM)
@@ -286,15 +386,53 @@ class HumanSkeleton(
leftShoulderTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_SHOULDER)
rightShoulderTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_SHOULDER)
// Check for specific conditions and store them in booleans.
// Fingers
leftThumbProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_THUMB_PROXIMAL)
leftThumbIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_THUMB_INTERMEDIATE)
leftThumbDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_THUMB_DISTAL)
leftIndexProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_INDEX_PROXIMAL)
leftIndexIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_INDEX_INTERMEDIATE)
leftIndexDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_INDEX_DISTAL)
leftMiddleProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_MIDDLE_PROXIMAL)
leftMiddleIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_MIDDLE_INTERMEDIATE)
leftMiddleDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_MIDDLE_DISTAL)
leftRingProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_RING_PROXIMAL)
leftRingIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_RING_INTERMEDIATE)
leftRingDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_RING_DISTAL)
leftLittleProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_LITTLE_PROXIMAL)
leftLittleIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_LITTLE_INTERMEDIATE)
leftLittleDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.LEFT_LITTLE_DISTAL)
rightThumbProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_THUMB_PROXIMAL)
rightThumbIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_THUMB_INTERMEDIATE)
rightThumbDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_THUMB_DISTAL)
rightIndexProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_INDEX_PROXIMAL)
rightIndexIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_INDEX_INTERMEDIATE)
rightIndexDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_INDEX_DISTAL)
rightMiddleProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_MIDDLE_PROXIMAL)
rightMiddleIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_MIDDLE_INTERMEDIATE)
rightMiddleDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_MIDDLE_DISTAL)
rightRingProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_RING_PROXIMAL)
rightRingIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_RING_INTERMEDIATE)
rightRingDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_RING_DISTAL)
rightLittleProximalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_LITTLE_PROXIMAL)
rightLittleIntermediateTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_LITTLE_INTERMEDIATE)
rightLittleDistalTracker = getTrackerForSkeleton(trackers, TrackerPosition.RIGHT_LITTLE_DISTAL)
// Check for specific conditions and cache them
hasSpineTracker = upperChestTracker != null || chestTracker != null || waistTracker != null || hipTracker != null
hasKneeTrackers = leftUpperLegTracker != null && rightUpperLegTracker != null
hasLeftLegTracker = leftUpperLegTracker != null || leftLowerLegTracker != null || leftFootTracker != null
hasRightLegTracker = rightUpperLegTracker != null || rightLowerLegTracker != null || rightFootTracker != null
hasLeftFootTracker = leftFootTracker != null
hasRightFootTracker = rightFootTracker != null
hasLeftArmTracker = leftLowerArmTracker != null || leftUpperArmTracker != null
hasRightArmTracker = rightLowerArmTracker != null || rightUpperArmTracker != null
hasLeftFingerTracker = leftThumbProximalTracker != null || leftThumbIntermediateTracker != null || leftThumbDistalTracker != null ||
leftIndexProximalTracker != null || leftIndexIntermediateTracker != null || leftIndexDistalTracker != null ||
leftMiddleProximalTracker != null || leftMiddleIntermediateTracker != null || leftMiddleDistalTracker != null ||
leftRingProximalTracker != null || leftRingIntermediateTracker != null || leftRingDistalTracker != null ||
leftLittleProximalTracker != null || leftLittleIntermediateTracker != null || leftLittleDistalTracker != null
hasRightFingerTracker = rightThumbProximalTracker != null || rightThumbIntermediateTracker != null || rightThumbDistalTracker != null ||
rightIndexProximalTracker != null || rightIndexIntermediateTracker != null || rightIndexDistalTracker != null ||
rightMiddleProximalTracker != null || rightMiddleIntermediateTracker != null || rightMiddleDistalTracker != null ||
rightRingProximalTracker != null || rightRingIntermediateTracker != null || rightRingDistalTracker != null ||
rightLittleProximalTracker != null || rightLittleIntermediateTracker != null || rightLittleDistalTracker != null
// Rebuilds the arm skeleton nodes attachments
assembleSkeletonArms(true)
@@ -394,6 +532,7 @@ class HumanSkeleton(
// Spine
updateSpineTransforms()
// Left leg
updateLegTransforms(
leftUpperLegBone,
@@ -405,6 +544,7 @@ class HumanSkeleton(
leftLowerLegTracker,
leftFootTracker,
)
// Right leg
updateLegTransforms(
rightUpperLegBone,
@@ -416,6 +556,7 @@ class HumanSkeleton(
rightLowerLegTracker,
rightFootTracker,
)
// Left arm
updateArmTransforms(
isTrackingLeftArmFromController,
@@ -430,6 +571,7 @@ class HumanSkeleton(
leftLowerArmTracker,
leftHandTracker,
)
// Right arm
updateArmTransforms(
isTrackingRightArmFromController,
@@ -444,6 +586,116 @@ class HumanSkeleton(
rightLowerArmTracker,
rightHandTracker,
)
// Left thumb
updateFingerTransforms(
leftHandTrackerBone.getGlobalRotation(),
leftThumbProximalBone,
leftThumbIntermediateBone,
leftThumbDistalBone,
leftThumbProximalTracker,
leftThumbIntermediateTracker,
leftThumbDistalTracker,
)
// Left index
updateFingerTransforms(
leftHandTrackerBone.getGlobalRotation(),
leftIndexProximalBone,
leftIndexIntermediateBone,
leftIndexDistalBone,
leftIndexProximalTracker,
leftIndexIntermediateTracker,
leftIndexDistalTracker,
)
// Left middle
updateFingerTransforms(
leftHandTrackerBone.getGlobalRotation(),
leftMiddleProximalBone,
leftMiddleIntermediateBone,
leftMiddleDistalBone,
leftMiddleProximalTracker,
leftMiddleIntermediateTracker,
leftMiddleDistalTracker,
)
// Left ring
updateFingerTransforms(
leftHandTrackerBone.getGlobalRotation(),
leftRingProximalBone,
leftRingIntermediateBone,
leftRingDistalBone,
leftRingProximalTracker,
leftRingIntermediateTracker,
leftRingDistalTracker,
)
// Left little
updateFingerTransforms(
leftHandTrackerBone.getGlobalRotation(),
leftLittleProximalBone,
leftLittleIntermediateBone,
leftLittleDistalBone,
leftLittleProximalTracker,
leftLittleIntermediateTracker,
leftLittleDistalTracker,
)
// Right thumb
updateFingerTransforms(
rightHandTrackerBone.getGlobalRotation(),
rightThumbProximalBone,
rightThumbIntermediateBone,
rightThumbDistalBone,
rightThumbProximalTracker,
rightThumbIntermediateTracker,
rightThumbDistalTracker,
)
// Right index
updateFingerTransforms(
rightHandTrackerBone.getGlobalRotation(),
rightIndexProximalBone,
rightIndexIntermediateBone,
rightIndexDistalBone,
rightIndexProximalTracker,
rightIndexIntermediateTracker,
rightIndexDistalTracker,
)
// Right middle
updateFingerTransforms(
rightHandTrackerBone.getGlobalRotation(),
rightMiddleProximalBone,
rightMiddleIntermediateBone,
rightMiddleDistalBone,
rightMiddleProximalTracker,
rightMiddleIntermediateTracker,
rightMiddleDistalTracker,
)
// Right ring
updateFingerTransforms(
rightHandTrackerBone.getGlobalRotation(),
rightRingProximalBone,
rightRingIntermediateBone,
rightRingDistalBone,
rightRingProximalTracker,
rightRingIntermediateTracker,
rightRingDistalTracker,
)
// Right little
updateFingerTransforms(
rightHandTrackerBone.getGlobalRotation(),
rightLittleProximalBone,
rightLittleIntermediateBone,
rightLittleDistalBone,
rightLittleProximalTracker,
rightLittleIntermediateTracker,
rightLittleDistalTracker,
)
}
/**
@@ -767,6 +1019,70 @@ class HumanSkeleton(
}
}
/**
* Update a finger's 3 bones' transforms
*/
private fun updateFingerTransforms(
handRotation: Quaternion,
proximalBone: Bone,
intermediateBone: Bone,
distalBone: Bone,
proximalTracker: Tracker?,
intermediateTracker: Tracker?,
distalTracker: Tracker?,
) {
if (distalTracker == null && intermediateTracker == null && proximalTracker == null) {
// Set fingers' rotations to the hand's if no finger tracker
proximalBone.setRotation(handRotation)
intermediateBone.setRotation(handRotation)
distalBone.setRotation(handRotation)
}
// Note: we use interpQ instead of interpR in order to slerp over 180 degrees.
// Start of finger
proximalTracker?.let {
val fingerRot = if (it.trackerDataType == TrackerDataType.FLEX_RESISTANCE ||
it.trackerDataType == TrackerDataType.FLEX_ANGLE
) {
handRotation * it.getRotation()
} else {
it.getRotation()
}
proximalBone.setRotation(fingerRot)
if (intermediateTracker == null) intermediateBone.setRotation(handRotation.interpQ(fingerRot, 2.12f))
if (distalTracker == null) distalBone.setRotation(handRotation.interpQ(fingerRot, 3.03f))
}
// Middle of finger
intermediateTracker?.let {
val fingerRot = if (it.trackerDataType == TrackerDataType.FLEX_RESISTANCE ||
it.trackerDataType == TrackerDataType.FLEX_ANGLE
) {
handRotation * it.getRotation()
} else {
it.getRotation()
}
if (proximalTracker == null) proximalBone.setRotation(handRotation.interpQ(fingerRot, 0.47f))
intermediateBone.setRotation(fingerRot)
if (distalTracker == null) distalBone.setRotation(handRotation.interpQ(fingerRot, 1.43f))
}
// Tip of finger
distalTracker?.let {
val fingerRot = if (it.trackerDataType == TrackerDataType.FLEX_RESISTANCE ||
it.trackerDataType == TrackerDataType.FLEX_ANGLE
) {
handRotation * it.getRotation()
} else {
it.getRotation()
}
if (proximalTracker == null && intermediateTracker == null) proximalBone.setRotation(handRotation.interpQ(fingerRot, 0.33f))
if (intermediateTracker == null) intermediateBone.setRotation(handRotation.interpQ(fingerRot, 0.7f))
distalBone.setRotation(fingerRot)
}
}
/**
* Rotates the first Quaternion to match its yaw and roll to the rotation of
* the second Quaternion
@@ -959,6 +1275,36 @@ class HumanSkeleton(
BoneType.RIGHT_HAND -> rightHandBone
BoneType.LEFT_HAND_TRACKER -> leftHandTrackerBone
BoneType.RIGHT_HAND_TRACKER -> rightHandTrackerBone
BoneType.LEFT_THUMB_PROXIMAL -> leftThumbProximalBone
BoneType.LEFT_THUMB_INTERMEDIATE -> leftThumbIntermediateBone
BoneType.LEFT_THUMB_DISTAL -> leftThumbDistalBone
BoneType.LEFT_INDEX_PROXIMAL -> leftIndexProximalBone
BoneType.LEFT_INDEX_INTERMEDIATE -> leftIndexIntermediateBone
BoneType.LEFT_INDEX_DISTAL -> leftIndexDistalBone
BoneType.LEFT_MIDDLE_PROXIMAL -> leftMiddleProximalBone
BoneType.LEFT_MIDDLE_INTERMEDIATE -> leftMiddleIntermediateBone
BoneType.LEFT_MIDDLE_DISTAL -> leftMiddleDistalBone
BoneType.LEFT_RING_PROXIMAL -> leftRingProximalBone
BoneType.LEFT_RING_INTERMEDIATE -> leftRingIntermediateBone
BoneType.LEFT_RING_DISTAL -> leftRingDistalBone
BoneType.LEFT_LITTLE_PROXIMAL -> leftLittleProximalBone
BoneType.LEFT_LITTLE_INTERMEDIATE -> leftLittleIntermediateBone
BoneType.LEFT_LITTLE_DISTAL -> leftLittleDistalBone
BoneType.RIGHT_THUMB_PROXIMAL -> rightThumbProximalBone
BoneType.RIGHT_THUMB_INTERMEDIATE -> rightThumbIntermediateBone
BoneType.RIGHT_THUMB_DISTAL -> rightThumbDistalBone
BoneType.RIGHT_INDEX_PROXIMAL -> rightIndexProximalBone
BoneType.RIGHT_INDEX_INTERMEDIATE -> rightIndexIntermediateBone
BoneType.RIGHT_INDEX_DISTAL -> rightIndexDistalBone
BoneType.RIGHT_MIDDLE_PROXIMAL -> rightMiddleProximalBone
BoneType.RIGHT_MIDDLE_INTERMEDIATE -> rightMiddleIntermediateBone
BoneType.RIGHT_MIDDLE_DISTAL -> rightMiddleDistalBone
BoneType.RIGHT_RING_PROXIMAL -> rightRingProximalBone
BoneType.RIGHT_RING_INTERMEDIATE -> rightRingIntermediateBone
BoneType.RIGHT_RING_DISTAL -> rightRingDistalBone
BoneType.RIGHT_LITTLE_PROXIMAL -> rightLittleProximalBone
BoneType.RIGHT_LITTLE_INTERMEDIATE -> rightLittleIntermediateBone
BoneType.RIGHT_LITTLE_DISTAL -> rightLittleDistalBone
}
/**
@@ -988,6 +1334,36 @@ class HumanSkeleton(
rightLowerArmBone,
leftHandBone,
rightHandBone,
leftThumbProximalBone,
leftThumbIntermediateBone,
leftThumbDistalBone,
leftIndexProximalBone,
leftIndexIntermediateBone,
leftIndexDistalBone,
leftMiddleProximalBone,
leftMiddleIntermediateBone,
leftMiddleDistalBone,
leftRingProximalBone,
leftRingIntermediateBone,
leftRingDistalBone,
leftLittleProximalBone,
leftLittleIntermediateBone,
leftLittleDistalBone,
rightThumbProximalBone,
rightThumbIntermediateBone,
rightThumbDistalBone,
rightIndexProximalBone,
rightIndexIntermediateBone,
rightIndexDistalBone,
rightMiddleProximalBone,
rightMiddleIntermediateBone,
rightMiddleDistalBone,
rightRingProximalBone,
rightRingIntermediateBone,
rightRingDistalBone,
rightLittleProximalBone,
rightLittleIntermediateBone,
rightLittleDistalBone,
)
/**
@@ -1007,6 +1383,36 @@ class HumanSkeleton(
rightHandBone,
leftHandTrackerBone,
rightHandTrackerBone,
leftThumbProximalBone,
leftThumbIntermediateBone,
leftThumbDistalBone,
leftIndexProximalBone,
leftIndexIntermediateBone,
leftIndexDistalBone,
leftMiddleProximalBone,
leftMiddleIntermediateBone,
leftMiddleDistalBone,
leftRingProximalBone,
leftRingIntermediateBone,
leftRingDistalBone,
leftLittleProximalBone,
leftLittleIntermediateBone,
leftLittleDistalBone,
rightThumbProximalBone,
rightThumbIntermediateBone,
rightThumbDistalBone,
rightIndexProximalBone,
rightIndexIntermediateBone,
rightIndexDistalBone,
rightMiddleProximalBone,
rightMiddleIntermediateBone,
rightMiddleDistalBone,
rightRingProximalBone,
rightRingIntermediateBone,
rightRingDistalBone,
rightLittleProximalBone,
rightLittleIntermediateBone,
rightLittleDistalBone,
)
val hmdHeight: Float
@@ -1053,6 +1459,36 @@ class HumanSkeleton(
rightHandTracker,
leftShoulderTracker,
rightShoulderTracker,
leftThumbProximalTracker,
leftThumbIntermediateTracker,
leftThumbDistalTracker,
leftIndexProximalTracker,
leftIndexIntermediateTracker,
leftIndexDistalTracker,
leftMiddleProximalTracker,
leftMiddleIntermediateTracker,
leftMiddleDistalTracker,
leftRingProximalTracker,
leftRingIntermediateTracker,
leftRingDistalTracker,
leftLittleProximalTracker,
leftLittleIntermediateTracker,
leftLittleDistalTracker,
rightThumbProximalTracker,
rightThumbIntermediateTracker,
rightThumbDistalTracker,
rightIndexProximalTracker,
rightIndexIntermediateTracker,
rightIndexDistalTracker,
rightMiddleProximalTracker,
rightMiddleIntermediateTracker,
rightMiddleDistalTracker,
rightRingProximalTracker,
rightRingIntermediateTracker,
rightRingDistalTracker,
rightLittleProximalTracker,
rightLittleIntermediateTracker,
rightLittleDistalTracker,
)
fun resetTrackersFull(resetSourceName: String?) {

View File

@@ -5,6 +5,7 @@ import dev.slimevr.config.TrackerConfig
import dev.slimevr.tracking.trackers.TrackerPosition.Companion.getByDesignation
import dev.slimevr.tracking.trackers.udp.IMUType
import dev.slimevr.tracking.trackers.udp.MagnetometerStatus
import dev.slimevr.tracking.trackers.udp.TrackerDataType
import io.eiren.util.BufferedTimer
import io.github.axisangles.ktmath.Quaternion
import io.github.axisangles.ktmath.Vector3
@@ -67,6 +68,11 @@ class Tracker @JvmOverloads constructor(
val needsMounting: Boolean = false,
val isHmd: Boolean = false,
magStatus: MagnetometerStatus = MagnetometerStatus.NOT_SUPPORTED,
/**
* Rotation by default.
* NOT the same as hasRotation (other data types emulate rotation)
*/
val trackerDataType: TrackerDataType = TrackerDataType.ROTATION,
) {
private val timer = BufferedTimer(1f)
private var timeAtLastUpdate: Long = System.currentTimeMillis()
@@ -75,6 +81,7 @@ class Tracker @JvmOverloads constructor(
var position = Vector3.NULL
val resetsHandler: TrackerResetsHandler = TrackerResetsHandler(this)
val filteringHandler: TrackerFilteringHandler = TrackerFilteringHandler()
val trackerFlexHandler: TrackerFlexHandler = TrackerFlexHandler(this)
var batteryVoltage: Float? = null
var batteryLevel: Float? = null
var ping: Int? = null
@@ -150,7 +157,7 @@ class Tracker @JvmOverloads constructor(
fun checkReportRequireReset() {
if (needsReset && trackerPosition != null && lastResetStatus == 0u &&
!status.reset && (isImu() || !statusResetRecently)
!status.reset && (isImu() || !statusResetRecently && trackerDataType != TrackerDataType.FLEX_ANGLE)
) {
reportRequireReset()
} else if (lastResetStatus != 0u && (trackerPosition == null || status.reset)) {
@@ -319,16 +326,16 @@ class Tracker @JvmOverloads constructor(
* it too much should be avoided for performance reasons.
*/
fun getRotation(): Quaternion {
var rot = if (allowFiltering && filteringHandler.enabled) {
var rot = if (allowFiltering && filteringHandler.filteringEnabled) {
// Get filtered rotation
filteringHandler.getFilteredRotation()
} else {
// Get unfiltered rotation
_rotation
filteringHandler.getTrackedRotation()
}
// Reset if needed and is not computed and internal
if (needsReset && !(isComputed && isInternal)) {
if (needsReset && !(isComputed && isInternal) && trackerDataType == TrackerDataType.ROTATION) {
// Adjust to reset, mounting and drift compensation
rot = resetsHandler.getReferenceAdjustedDriftRotationFrom(rot)
}
@@ -351,16 +358,16 @@ class Tracker @JvmOverloads constructor(
* This is used for debugging/visualizing tracker data
*/
fun getIdentityAdjustedRotation(): Quaternion {
var rot = if (filteringHandler.enabled) {
var rot = if (filteringHandler.filteringEnabled) {
// Get filtered rotation
filteringHandler.getFilteredRotation()
} else {
// Get unfiltered rotation
_rotation
filteringHandler.getTrackedRotation()
}
// Reset if needed or is a computed tracker besides head
if (needsReset && !(isComputed && trackerPosition != TrackerPosition.HEAD)) {
if (needsReset && !(isComputed && trackerPosition != TrackerPosition.HEAD) && trackerDataType == TrackerDataType.ROTATION) {
// Adjust to reset and mounting
rot = resetsHandler.getIdentityAdjustedDriftRotationFrom(rot)
}
@@ -372,7 +379,7 @@ class Tracker @JvmOverloads constructor(
* Gets the raw (unadjusted) rotation of the tracker.
* If this is an IMU, this will be the raw sensor rotation.
*/
fun getRawRotation(): Quaternion = _rotation
fun getRawRotation() = _rotation
/**
* Sets the raw (unadjusted) rotation of the tracker.
@@ -388,7 +395,11 @@ class Tracker @JvmOverloads constructor(
this._acceleration = vec
}
fun isImu(): Boolean = imuType != null
/**
* True if the raw rotation is coming directly from an IMU (no cameras or lighthouses)
* For example, flex sensor trackers are not considered as IMU trackers (see TrackerDataType)
*/
fun isImu(): Boolean = imuType != null && trackerDataType == TrackerDataType.ROTATION
/**
* Please don't use this and instead set it via [Device.setMag]
@@ -406,4 +417,11 @@ class Tracker @JvmOverloads constructor(
*/
val tps: Float
get() = timer.averageFPS
/**
* Call when doing a full reset to reset the tracking of rotations >180 degrees
*/
fun resetFilteringQuats() {
filteringHandler.resetQuats(_rotation)
}
}

View File

@@ -12,8 +12,9 @@ import io.github.axisangles.ktmath.Quaternion
*/
class TrackerFilteringHandler {
private var movingAverage: QuaternionMovingAverage? = null
var enabled = false
private var filteringMovingAverage: QuaternionMovingAverage? = null
private var trackingMovingAverage = QuaternionMovingAverage(TrackerFilters.NONE)
var filteringEnabled = false
/**
* Reads/loads filtering settings from given config
@@ -21,15 +22,15 @@ class TrackerFilteringHandler {
fun readFilteringConfig(config: FiltersConfig, currentRawRotation: Quaternion) {
val type = TrackerFilters.getByConfigkey(config.type)
if (type == TrackerFilters.SMOOTHING || type == TrackerFilters.PREDICTION) {
movingAverage = QuaternionMovingAverage(
filteringMovingAverage = QuaternionMovingAverage(
type,
config.amount,
currentRawRotation,
)
enabled = true
filteringEnabled = true
} else {
movingAverage = null
enabled = false
filteringMovingAverage = null
filteringEnabled = false
}
}
@@ -37,18 +38,33 @@ class TrackerFilteringHandler {
* Update the moving average to make it smooth
*/
fun update() {
movingAverage?.update()
trackingMovingAverage.update()
filteringMovingAverage?.update()
}
/**
* Updates the latest rotation
*/
fun dataTick(currentRawRotation: Quaternion) {
movingAverage?.addQuaternion(currentRawRotation)
trackingMovingAverage.addQuaternion(currentRawRotation)
filteringMovingAverage?.addQuaternion(currentRawRotation)
}
/**
* Call when doing a full reset to reset the tracking of rotations >180 degrees
*/
fun resetQuats(currentRawRotation: Quaternion) {
trackingMovingAverage.resetQuats(currentRawRotation)
filteringMovingAverage?.resetQuats(currentRawRotation)
}
/**
* Gets the tracked rotation from the moving average (allows >180 degrees)
*/
fun getTrackedRotation() = trackingMovingAverage.filteredQuaternion
/**
* Get the filtered rotation from the moving average
*/
fun getFilteredRotation(): Quaternion = movingAverage?.filteredQuaternion ?: Quaternion.IDENTITY
fun getFilteredRotation() = filteringMovingAverage?.filteredQuaternion ?: Quaternion.IDENTITY
}

View File

@@ -0,0 +1,176 @@
package dev.slimevr.tracking.trackers
import com.jme3.math.FastMath
import io.github.axisangles.ktmath.EulerAngles
import io.github.axisangles.ktmath.EulerOrder
import kotlin.math.*
/**
* Class handling flex sensor data (angle and resistance)
* Resistance is expected to go up with bend by default, but a mounting reset allows the contrary
*/
class TrackerFlexHandler(val tracker: Tracker) {
private var minResistance = Float.MIN_VALUE
private var maxResistance = Float.MAX_VALUE
// Used to support resistance going both ways.
// Default is higher = more bend, but can change after a full and mounting reset.
private var lastMinResetResistance = Float.MIN_VALUE
private var resistanceReversed = false
private var lastResistance = 0f
private val thumbInitialOffset = FastMath.PI / 8 // 22.5 deg
/**
* Resets the min resistance from the last resistance value received.
* Triggered from full reset
*/
fun resetMin() {
minResistance = lastResistance
lastMinResetResistance = lastResistance
setFlexResistance(lastResistance)
tracker.dataTick()
}
/**
* Resets the max resistance from the last resistance value received.
* Triggered from mounting reset
*/
fun resetMax() {
// Account for the resistance being able to be reversed
if (resistanceReversed != lastResistance < lastMinResetResistance) {
// Switching
resistanceReversed = lastResistance < lastMinResetResistance
minResistance = maxResistance
maxResistance = lastMinResetResistance
} else {
// Not switching
maxResistance = lastResistance
}
setFlexResistance(lastResistance)
tracker.dataTick()
}
/**
* Sets the flex resistance which is then calculated into an angle
*/
fun setFlexResistance(resistance: Float) {
// Dynamically calibrate the minimum resistance
minResistance = if (minResistance == Float.MIN_VALUE) {
resistance
} else if (!resistanceReversed) {
min(minResistance, resistance)
} else {
max(minResistance, resistance)
}
// Dynamically calibrate the maximum resistance
maxResistance = if (maxResistance == Float.MAX_VALUE) {
resistance
} else if (!resistanceReversed) {
max(maxResistance, resistance)
} else {
min(maxResistance, resistance)
}
// Get max angle
val maxBend = getMaxAngleForTrackerPosition(tracker.trackerPosition)
// Get angle and set it
val angle = if (minResistance == maxResistance) {
// Avoid division by 0
0f
} else {
maxBend * (resistance - minResistance) / (maxResistance - minResistance)
}
setFlexAngle(angle)
lastResistance = resistance
}
/**
* Sets an angle (rad) about the X axis
*/
fun setFlexAngle(angle: Float) {
// Sets the rotation of the tracker by the angle about a given axis depending on
// the tracker's TrackerPosition
when (tracker.trackerPosition) {
TrackerPosition.LEFT_INDEX_PROXIMAL, TrackerPosition.LEFT_INDEX_INTERMEDIATE,
TrackerPosition.LEFT_INDEX_DISTAL, TrackerPosition.LEFT_MIDDLE_PROXIMAL,
TrackerPosition.LEFT_MIDDLE_INTERMEDIATE, TrackerPosition.LEFT_MIDDLE_DISTAL,
TrackerPosition.LEFT_RING_PROXIMAL, TrackerPosition.LEFT_RING_INTERMEDIATE,
TrackerPosition.LEFT_RING_DISTAL, TrackerPosition.LEFT_LITTLE_PROXIMAL,
TrackerPosition.LEFT_LITTLE_INTERMEDIATE, TrackerPosition.LEFT_LITTLE_DISTAL,
TrackerPosition.RIGHT_SHOULDER,
-> tracker.setRotation(EulerAngles(EulerOrder.YZX, 0f, 0f, angle).toQuaternion())
TrackerPosition.RIGHT_INDEX_PROXIMAL, TrackerPosition.RIGHT_INDEX_INTERMEDIATE,
TrackerPosition.RIGHT_INDEX_DISTAL, TrackerPosition.RIGHT_MIDDLE_PROXIMAL,
TrackerPosition.RIGHT_MIDDLE_INTERMEDIATE, TrackerPosition.RIGHT_MIDDLE_DISTAL,
TrackerPosition.RIGHT_RING_PROXIMAL, TrackerPosition.RIGHT_RING_INTERMEDIATE,
TrackerPosition.RIGHT_RING_DISTAL, TrackerPosition.RIGHT_LITTLE_PROXIMAL,
TrackerPosition.RIGHT_LITTLE_INTERMEDIATE, TrackerPosition.RIGHT_LITTLE_DISTAL,
TrackerPosition.LEFT_SHOULDER,
-> tracker.setRotation(EulerAngles(EulerOrder.YZX, 0f, 0f, -angle).toQuaternion())
TrackerPosition.LEFT_THUMB_PROXIMAL, TrackerPosition.LEFT_THUMB_INTERMEDIATE, TrackerPosition.LEFT_THUMB_DISTAL,
-> tracker.setRotation(EulerAngles(EulerOrder.YZX, thumbInitialOffset - angle, -angle * 0.05f, angle * 0.1f).toQuaternion())
TrackerPosition.RIGHT_THUMB_PROXIMAL, TrackerPosition.RIGHT_THUMB_INTERMEDIATE, TrackerPosition.RIGHT_THUMB_DISTAL,
-> tracker.setRotation(EulerAngles(EulerOrder.YZX, thumbInitialOffset - angle, angle * 0.05f, -angle * 0.1f).toQuaternion())
// Default to X axis (pitch)
else -> tracker.setRotation(EulerAngles(EulerOrder.YZX, angle, 0f, 0f).toQuaternion())
}
}
/**
* Gets the max angle for a TrackerPosition
*/
private fun getMaxAngleForTrackerPosition(trackerPosition: TrackerPosition?): Float {
if (trackerPosition == null) return FastMath.PI // 180 degrees
return when (trackerPosition) {
// 270 degrees
TrackerPosition.LEFT_INDEX_DISTAL, TrackerPosition.LEFT_MIDDLE_DISTAL,
TrackerPosition.LEFT_RING_DISTAL, TrackerPosition.LEFT_LITTLE_DISTAL,
TrackerPosition.RIGHT_INDEX_DISTAL, TrackerPosition.RIGHT_MIDDLE_DISTAL,
TrackerPosition.RIGHT_RING_DISTAL, TrackerPosition.RIGHT_LITTLE_DISTAL,
-> FastMath.PI + FastMath.HALF_PI
// 202.5 degrees
TrackerPosition.LEFT_THUMB_DISTAL, TrackerPosition.RIGHT_THUMB_DISTAL,
-> FastMath.PI + thumbInitialOffset
// 180 degrees
TrackerPosition.LEFT_INDEX_INTERMEDIATE, TrackerPosition.LEFT_MIDDLE_INTERMEDIATE,
TrackerPosition.LEFT_RING_INTERMEDIATE, TrackerPosition.LEFT_LITTLE_INTERMEDIATE,
TrackerPosition.RIGHT_INDEX_INTERMEDIATE, TrackerPosition.RIGHT_MIDDLE_INTERMEDIATE,
TrackerPosition.RIGHT_RING_INTERMEDIATE, TrackerPosition.RIGHT_LITTLE_INTERMEDIATE,
-> FastMath.PI
// 112.5 degrees
TrackerPosition.LEFT_THUMB_INTERMEDIATE, TrackerPosition.RIGHT_THUMB_INTERMEDIATE,
-> FastMath.HALF_PI + thumbInitialOffset
// 90 degrees
TrackerPosition.LEFT_INDEX_PROXIMAL, TrackerPosition.LEFT_MIDDLE_PROXIMAL,
TrackerPosition.LEFT_RING_PROXIMAL, TrackerPosition.LEFT_LITTLE_PROXIMAL,
TrackerPosition.RIGHT_INDEX_PROXIMAL, TrackerPosition.RIGHT_MIDDLE_PROXIMAL,
TrackerPosition.RIGHT_RING_PROXIMAL, TrackerPosition.RIGHT_LITTLE_PROXIMAL,
-> FastMath.HALF_PI
// 67.5 degrees
TrackerPosition.LEFT_THUMB_PROXIMAL, TrackerPosition.RIGHT_THUMB_PROXIMAL,
-> FastMath.QUARTER_PI + thumbInitialOffset
// 45 degrees
TrackerPosition.LEFT_SHOULDER, TrackerPosition.RIGHT_SHOULDER,
-> FastMath.QUARTER_PI
// 135 degrees
else -> FastMath.HALF_PI + FastMath.QUARTER_PI
}
}
}

View File

@@ -13,39 +13,88 @@ enum class TrackerPosition(
val designation: String,
val trackerRole: TrackerRole?,
val bodyPart: Int,
val id: Int,
) {
// If updating BodyPart of a TrackerRole,
// please also update SteamVRBridge#updateShareSettingsAutomatically()
HEAD("body:head", TrackerRole.HMD, BodyPart.HEAD),
NECK("body:neck", TrackerRole.NECK, BodyPart.NECK),
UPPER_CHEST("body:upper_chest", TrackerRole.CHEST, BodyPart.UPPER_CHEST),
CHEST("body:chest", null, BodyPart.CHEST),
WAIST("body:waist", null, BodyPart.WAIST),
HIP("body:hip", TrackerRole.WAIST, BodyPart.HIP),
LEFT_UPPER_LEG("body:left_upper_leg", TrackerRole.LEFT_KNEE, BodyPart.LEFT_UPPER_LEG),
RIGHT_UPPER_LEG("body:right_upper_leg", TrackerRole.RIGHT_KNEE, BodyPart.RIGHT_UPPER_LEG),
LEFT_LOWER_LEG("body:left_lower_leg", null, BodyPart.LEFT_LOWER_LEG),
RIGHT_LOWER_LEG("body:right_lower_leg", null, BodyPart.RIGHT_LOWER_LEG),
LEFT_FOOT("body:left_foot", TrackerRole.LEFT_FOOT, BodyPart.LEFT_FOOT),
RIGHT_FOOT("body:right_foot", TrackerRole.RIGHT_FOOT, BodyPart.RIGHT_FOOT),
LEFT_LOWER_ARM("body:left_lower_arm", null, BodyPart.LEFT_LOWER_ARM),
RIGHT_LOWER_ARM("body:right_lower_arm", null, BodyPart.RIGHT_LOWER_ARM),
LEFT_UPPER_ARM("body:left_upper_arm", TrackerRole.LEFT_ELBOW, BodyPart.LEFT_UPPER_ARM),
RIGHT_UPPER_ARM("body:right_upper_arm", TrackerRole.RIGHT_ELBOW, BodyPart.RIGHT_UPPER_ARM),
LEFT_HAND("body:left_hand", TrackerRole.LEFT_HAND, BodyPart.LEFT_HAND),
RIGHT_HAND("body:right_hand", TrackerRole.RIGHT_HAND, BodyPart.RIGHT_HAND),
LEFT_SHOULDER("body:left_shoulder", TrackerRole.LEFT_SHOULDER, BodyPart.LEFT_SHOULDER),
RIGHT_SHOULDER("body:right_shoulder", TrackerRole.RIGHT_SHOULDER, BodyPart.RIGHT_SHOULDER),
HEAD("body:head", TrackerRole.HMD, BodyPart.HEAD, 1),
NECK("body:neck", TrackerRole.NECK, BodyPart.NECK, 2),
UPPER_CHEST("body:upper_chest", TrackerRole.CHEST, BodyPart.UPPER_CHEST, 3),
CHEST("body:chest", null, BodyPart.CHEST, 4),
WAIST("body:waist", null, BodyPart.WAIST, 5),
HIP("body:hip", TrackerRole.WAIST, BodyPart.HIP, 6),
LEFT_UPPER_LEG("body:left_upper_leg", TrackerRole.LEFT_KNEE, BodyPart.LEFT_UPPER_LEG, 7),
RIGHT_UPPER_LEG("body:right_upper_leg", TrackerRole.RIGHT_KNEE, BodyPart.RIGHT_UPPER_LEG, 8),
LEFT_LOWER_LEG("body:left_lower_leg", null, BodyPart.LEFT_LOWER_LEG, 9),
RIGHT_LOWER_LEG("body:right_lower_leg", null, BodyPart.RIGHT_LOWER_LEG, 10),
LEFT_FOOT("body:left_foot", TrackerRole.LEFT_FOOT, BodyPart.LEFT_FOOT, 11),
RIGHT_FOOT("body:right_foot", TrackerRole.RIGHT_FOOT, BodyPart.RIGHT_FOOT, 12),
LEFT_LOWER_ARM("body:left_lower_arm", null, BodyPart.LEFT_LOWER_ARM, 13),
RIGHT_LOWER_ARM("body:right_lower_arm", null, BodyPart.RIGHT_LOWER_ARM, 14),
LEFT_UPPER_ARM("body:left_upper_arm", TrackerRole.LEFT_ELBOW, BodyPart.LEFT_UPPER_ARM, 15),
RIGHT_UPPER_ARM("body:right_upper_arm", TrackerRole.RIGHT_ELBOW, BodyPart.RIGHT_UPPER_ARM, 16),
LEFT_HAND("body:left_hand", TrackerRole.LEFT_HAND, BodyPart.LEFT_HAND, 17),
RIGHT_HAND("body:right_hand", TrackerRole.RIGHT_HAND, BodyPart.RIGHT_HAND, 18),
LEFT_SHOULDER("body:left_shoulder", TrackerRole.LEFT_SHOULDER, BodyPart.LEFT_SHOULDER, 19),
RIGHT_SHOULDER("body:right_shoulder", TrackerRole.RIGHT_SHOULDER, BodyPart.RIGHT_SHOULDER, 20),
LEFT_THUMB_PROXIMAL("body:left_thumb_proximal", null, BodyPart.LEFT_THUMB_PROXIMAL, 21),
LEFT_THUMB_INTERMEDIATE("body:left_thumb_intermediate", null, BodyPart.LEFT_THUMB_INTERMEDIATE, 22),
LEFT_THUMB_DISTAL("body:left_thumb_distal", null, BodyPart.LEFT_THUMB_DISTAL, 23),
LEFT_INDEX_PROXIMAL("body:left_index_proximal", null, BodyPart.LEFT_INDEX_PROXIMAL, 24),
LEFT_INDEX_INTERMEDIATE("body:left_index_intermediate", null, BodyPart.LEFT_INDEX_INTERMEDIATE, 25),
LEFT_INDEX_DISTAL("body:left_index_distal", null, BodyPart.LEFT_INDEX_DISTAL, 26),
LEFT_MIDDLE_PROXIMAL("body:left_middle_proximal", null, BodyPart.LEFT_MIDDLE_PROXIMAL, 27),
LEFT_MIDDLE_INTERMEDIATE("body:left_middle_intermediate", null, BodyPart.LEFT_MIDDLE_INTERMEDIATE, 28),
LEFT_MIDDLE_DISTAL("body:left_middle_distal", null, BodyPart.LEFT_MIDDLE_DISTAL, 29),
LEFT_RING_PROXIMAL("body:left_ring_proximal", null, BodyPart.LEFT_RING_PROXIMAL, 30),
LEFT_RING_INTERMEDIATE("body:left_ring_intermediate", null, BodyPart.LEFT_RING_INTERMEDIATE, 31),
LEFT_RING_DISTAL("body:left_ring_distal", null, BodyPart.LEFT_RING_DISTAL, 32),
LEFT_LITTLE_PROXIMAL("body:left_little_proximal", null, BodyPart.LEFT_LITTLE_PROXIMAL, 33),
LEFT_LITTLE_INTERMEDIATE("body:left_little_intermediate", null, BodyPart.LEFT_LITTLE_INTERMEDIATE, 34),
LEFT_LITTLE_DISTAL("body:left_little_distal", null, BodyPart.LEFT_LITTLE_DISTAL, 35),
RIGHT_THUMB_PROXIMAL("body:right_thumb_proximal", null, BodyPart.RIGHT_THUMB_PROXIMAL, 36),
RIGHT_THUMB_INTERMEDIATE("body:right_thumb_intermediate", null, BodyPart.RIGHT_THUMB_INTERMEDIATE, 37),
RIGHT_THUMB_DISTAL("body:right_thumb_distal", null, BodyPart.RIGHT_THUMB_DISTAL, 38),
RIGHT_INDEX_PROXIMAL("body:right_index_proximal", null, BodyPart.RIGHT_INDEX_PROXIMAL, 39),
RIGHT_INDEX_INTERMEDIATE("body:right_index_intermediate", null, BodyPart.RIGHT_INDEX_INTERMEDIATE, 40),
RIGHT_INDEX_DISTAL("body:right_index_distal", null, BodyPart.RIGHT_INDEX_DISTAL, 41),
RIGHT_MIDDLE_PROXIMAL("body:right_middle_proximal", null, BodyPart.RIGHT_MIDDLE_PROXIMAL, 42),
RIGHT_MIDDLE_INTERMEDIATE("body:right_middle_intermediate", null, BodyPart.RIGHT_MIDDLE_INTERMEDIATE, 43),
RIGHT_MIDDLE_DISTAL("body:right_middle_distal", null, BodyPart.RIGHT_MIDDLE_DISTAL, 44),
RIGHT_RING_PROXIMAL("body:right_ring_proximal", null, BodyPart.RIGHT_RING_PROXIMAL, 45),
RIGHT_RING_INTERMEDIATE("body:right_ring_intermediate", null, BodyPart.RIGHT_RING_INTERMEDIATE, 46),
RIGHT_RING_DISTAL("body:right_ring_distal", null, BodyPart.RIGHT_RING_DISTAL, 47),
RIGHT_LITTLE_PROXIMAL("body:right_little_proximal", null, BodyPart.RIGHT_LITTLE_PROXIMAL, 48),
RIGHT_LITTLE_INTERMEDIATE("body:right_little_intermediate", null, BodyPart.RIGHT_LITTLE_INTERMEDIATE, 49),
RIGHT_LITTLE_DISTAL("body:right_little_distal", null, BodyPart.RIGHT_LITTLE_DISTAL, 50),
;
/**
* Returns the default mounting orientation for the body part
*/
fun defaultMounting(): Quaternion = when (this) {
LEFT_LOWER_ARM, LEFT_HAND -> Quaternion.SLIMEVR.LEFT
RIGHT_LOWER_ARM, RIGHT_HAND -> Quaternion.SLIMEVR.RIGHT
LEFT_LOWER_ARM, LEFT_HAND,
LEFT_INDEX_PROXIMAL, LEFT_INDEX_INTERMEDIATE,
LEFT_INDEX_DISTAL, LEFT_MIDDLE_PROXIMAL,
LEFT_MIDDLE_INTERMEDIATE, LEFT_MIDDLE_DISTAL,
LEFT_RING_PROXIMAL, LEFT_RING_INTERMEDIATE,
LEFT_RING_DISTAL, LEFT_LITTLE_PROXIMAL,
LEFT_LITTLE_INTERMEDIATE, LEFT_LITTLE_DISTAL,
-> Quaternion.SLIMEVR.LEFT
RIGHT_LOWER_ARM, RIGHT_HAND,
RIGHT_INDEX_PROXIMAL, RIGHT_INDEX_INTERMEDIATE,
RIGHT_INDEX_DISTAL, RIGHT_MIDDLE_PROXIMAL,
RIGHT_MIDDLE_INTERMEDIATE, RIGHT_MIDDLE_DISTAL,
RIGHT_RING_PROXIMAL, RIGHT_RING_INTERMEDIATE,
RIGHT_RING_DISTAL, RIGHT_LITTLE_PROXIMAL,
RIGHT_LITTLE_INTERMEDIATE, RIGHT_LITTLE_DISTAL,
-> Quaternion.SLIMEVR.RIGHT
LEFT_UPPER_ARM, LEFT_LOWER_LEG -> Quaternion.SLIMEVR.FRONT_LEFT
RIGHT_UPPER_ARM, RIGHT_LOWER_LEG -> Quaternion.SLIMEVR.FRONT_RIGHT
else -> Quaternion.SLIMEVR.FRONT
}
@@ -56,6 +105,7 @@ enum class TrackerPosition(
this[position.bodyPart] = position
}
}
private val byId = entries.associateBy { it.id }
private val byDesignation = entries.associateBy { it.designation.lowercase() }
private val byTrackerRole = entries.filter { it.trackerRole != null }.associateBy { it.trackerRole!! }
@@ -82,5 +132,8 @@ enum class TrackerPosition(
@JvmStatic
fun getByBodyPart(bodyPart: Int): TrackerPosition? = byBodyPart[bodyPart]
@JvmStatic
fun getById(id: Int): TrackerPosition? = byId[id]
}
}

View File

@@ -7,6 +7,7 @@ import dev.slimevr.config.ArmsResetModes
import dev.slimevr.config.DriftCompensationConfig
import dev.slimevr.config.ResetsConfig
import dev.slimevr.filtering.CircularArrayList
import dev.slimevr.tracking.trackers.udp.TrackerDataType
import io.eiren.math.FloatMath.animateEase
import io.github.axisangles.ktmath.EulerAngles
import io.github.axisangles.ktmath.EulerOrder
@@ -213,10 +214,19 @@ class TrackerResetsHandler(val tracker: Tracker) {
* 0). This allows the tracker to be strapped to body at any pitch and roll.
*/
fun resetFull(reference: Quaternion) {
if (tracker.trackerDataType == TrackerDataType.FLEX_RESISTANCE) {
tracker.trackerFlexHandler.resetMin()
postProcessResetFull()
return
} else if (tracker.trackerDataType == TrackerDataType.FLEX_ANGLE) {
postProcessResetFull()
return
}
// Adjust for T-Pose (down)
tposeDownFix = if ((isLeftArmTracker() && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
tposeDownFix = if (((isLeftArmTracker() || isLeftFingerTracker()) && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
EulerAngles(EulerOrder.YZX, 0f, 0f, -FastMath.HALF_PI).toQuaternion()
} else if ((isRightArmTracker() && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
} else if (((isRightArmTracker() || isRightFingerTracker()) && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
EulerAngles(EulerOrder.YZX, 0f, 0f, FastMath.HALF_PI).toQuaternion()
} else {
Quaternion.IDENTITY
@@ -276,10 +286,16 @@ class TrackerResetsHandler(val tracker: Tracker) {
calculateDrift(oldRot)
postProcessResetFull()
}
private fun postProcessResetFull() {
if (this.tracker.lastResetStatus != 0u) {
VRServer.instance.statusSystem.removeStatus(this.tracker.lastResetStatus)
this.tracker.lastResetStatus = 0u
}
tracker.resetFilteringQuats()
}
/**
@@ -289,6 +305,12 @@ class TrackerResetsHandler(val tracker: Tracker) {
* position should be corrected in the source.
*/
fun resetYaw(reference: Quaternion) {
if (tracker.trackerDataType == TrackerDataType.FLEX_RESISTANCE ||
tracker.trackerDataType == TrackerDataType.FLEX_ANGLE
) {
return
}
// Old rot for drift compensation
val oldRot = adjustToReference(tracker.getRawRotation())
lastResetQuaternion = oldRot
@@ -314,6 +336,8 @@ class TrackerResetsHandler(val tracker: Tracker) {
this.tracker.statusResetRecently = false
this.tracker.lastResetStatus = 0u
}
tracker.resetFilteringQuats()
}
/**
@@ -321,7 +345,15 @@ class TrackerResetsHandler(val tracker: Tracker) {
* and stores it in mountRotFix, and adjusts yawFix
*/
fun resetMounting(reference: Quaternion) {
if (!resetMountingFeet && isFootTracker()) return
if (tracker.trackerDataType == TrackerDataType.FLEX_RESISTANCE) {
tracker.trackerFlexHandler.resetMax()
tracker.resetFilteringQuats()
return
} else if (tracker.trackerDataType == TrackerDataType.FLEX_ANGLE) {
return
} else if (!resetMountingFeet && isFootTracker()) {
return
}
// Get the current calibrated rotation
var rotBuf = adjustToDrift(tracker.getRawRotation() * mountingOrientation)
@@ -338,15 +370,17 @@ class TrackerResetsHandler(val tracker: Tracker) {
// Calculate the yaw angle using tan
var yawAngle = atan2(rotVector.x, rotVector.z)
// Adjust for T-Pose
// Adjust for T-Pose and fingers
if ((isLeftArmTracker() && armsResetMode == ArmsResetModes.TPOSE_DOWN) ||
(isRightArmTracker() && armsResetMode == ArmsResetModes.TPOSE_UP)
(isRightArmTracker() && armsResetMode == ArmsResetModes.TPOSE_UP) ||
isLeftFingerTracker()
) {
// Tracker goes right
yawAngle -= FastMath.HALF_PI
}
if ((isLeftArmTracker() && armsResetMode == ArmsResetModes.TPOSE_UP) ||
(isRightArmTracker() && armsResetMode == ArmsResetModes.TPOSE_DOWN)
(isRightArmTracker() && armsResetMode == ArmsResetModes.TPOSE_DOWN) ||
isRightFingerTracker()
) {
// Tracker goes left
yawAngle += FastMath.HALF_PI
@@ -365,6 +399,8 @@ class TrackerResetsHandler(val tracker: Tracker) {
// save mounting reset
if (saveMountingReset) tracker.saveMountingResetOrientation(mountRotFix)
tracker.resetFilteringQuats()
}
fun clearMounting() {
@@ -573,4 +609,46 @@ class TrackerResetsHandler(val tracker: Tracker) {
}
return false
}
private fun isLeftFingerTracker(): Boolean {
tracker.trackerPosition?.let {
return it == TrackerPosition.LEFT_THUMB_PROXIMAL ||
it == TrackerPosition.LEFT_THUMB_INTERMEDIATE ||
it == TrackerPosition.LEFT_THUMB_DISTAL ||
it == TrackerPosition.LEFT_INDEX_PROXIMAL ||
it == TrackerPosition.LEFT_INDEX_INTERMEDIATE ||
it == TrackerPosition.LEFT_INDEX_DISTAL ||
it == TrackerPosition.LEFT_MIDDLE_PROXIMAL ||
it == TrackerPosition.LEFT_MIDDLE_INTERMEDIATE ||
it == TrackerPosition.LEFT_MIDDLE_DISTAL ||
it == TrackerPosition.LEFT_RING_PROXIMAL ||
it == TrackerPosition.LEFT_RING_INTERMEDIATE ||
it == TrackerPosition.LEFT_RING_DISTAL ||
it == TrackerPosition.LEFT_LITTLE_PROXIMAL ||
it == TrackerPosition.LEFT_LITTLE_INTERMEDIATE ||
it == TrackerPosition.LEFT_LITTLE_DISTAL
}
return false
}
private fun isRightFingerTracker(): Boolean {
tracker.trackerPosition?.let {
return it == TrackerPosition.RIGHT_THUMB_PROXIMAL ||
it == TrackerPosition.RIGHT_THUMB_INTERMEDIATE ||
it == TrackerPosition.RIGHT_THUMB_DISTAL ||
it == TrackerPosition.RIGHT_INDEX_PROXIMAL ||
it == TrackerPosition.RIGHT_INDEX_INTERMEDIATE ||
it == TrackerPosition.RIGHT_INDEX_DISTAL ||
it == TrackerPosition.RIGHT_MIDDLE_PROXIMAL ||
it == TrackerPosition.RIGHT_MIDDLE_INTERMEDIATE ||
it == TrackerPosition.RIGHT_MIDDLE_DISTAL ||
it == TrackerPosition.RIGHT_RING_PROXIMAL ||
it == TrackerPosition.RIGHT_RING_INTERMEDIATE ||
it == TrackerPosition.RIGHT_RING_DISTAL ||
it == TrackerPosition.RIGHT_LITTLE_PROXIMAL ||
it == TrackerPosition.RIGHT_LITTLE_INTERMEDIATE ||
it == TrackerPosition.RIGHT_LITTLE_DISTAL
}
return false
}
}

View File

@@ -107,6 +107,22 @@ enum class MCUType(val id: UInt) {
}
}
enum class TrackerDataType(val id: UInt) {
ROTATION(0u),
FLEX_RESISTANCE(1u),
FLEX_ANGLE(2u),
;
fun getSolarType(): Int = this.id.toInt()
companion object {
private val byId = entries.associateBy { it.id }
@JvmStatic
fun getById(id: UInt): TrackerDataType? = byId[id]
}
}
@JvmInline
value class ConfigTypeId(val v: UShort)

View File

@@ -5,8 +5,7 @@ import dev.slimevr.NetworkProtocol
import dev.slimevr.VRServer
import dev.slimevr.config.config
import dev.slimevr.protocol.rpc.MAG_TIMEOUT
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerStatus
import dev.slimevr.tracking.trackers.*
import io.eiren.util.Util
import io.eiren.util.collections.FastList
import io.eiren.util.logging.LogManager
@@ -87,7 +86,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
ipAddress = addr
name = handshake.macString?.let { "udp://$it" }
descriptiveName = "udp:/$addr"
firmwareBuild = handshake.firmwareBuild
protocolVersion = handshake.protocolVersion
firmwareVersion = handshake.firmware
connectionsByAddress[address] = this
@@ -97,8 +96,8 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
"""
[TrackerServer] Tracker $i handed over to address $socketAddr.
Board type: ${handshake.boardType},
imu type: ${handshake.imuType},
firmware: ${handshake.firmware} ($firmwareBuild),
firmware name: ${handshake.firmware},
protocol version: $protocolVersion,
mac: ${handshake.macString},
name: $name
""".trimIndent(),
@@ -111,7 +110,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
name = handshake.macString?.let { "udp://$it" }
?: "udp:/$addr"
descriptiveName = "udp:/$addr"
firmwareBuild = handshake.firmwareBuild
protocolVersion = handshake.protocolVersion
firmwareVersion = handshake.firmware
val i = connections.indexOf(this)
LogManager
@@ -119,8 +118,8 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
"""
[TrackerServer] Tracker $i reconnected from address $socketAddr.
Board type: ${handshake.boardType},
imu type: ${handshake.imuType},
firmware: ${handshake.firmware} ($firmwareBuild),
firmware name: ${handshake.firmware},
protocol version: $protocolVersion,
mac: ${handshake.macString},
name: $name
""".trimIndent(),
@@ -137,7 +136,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
handshake.mcuType,
)
VRServer.instance.deviceManager.addDevice(connection)
connection.firmwareBuild = handshake.firmwareBuild
connection.protocolVersion = handshake.protocolVersion
connection.protocol = if (handshake.firmware?.isEmpty() == true) {
// Only old owoTrack doesn't report firmware and have different packet IDs with SlimeVR
NetworkProtocol.OWO_LEGACY
@@ -165,18 +164,18 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
"""
[TrackerServer] Tracker $i connected from address $socketAddr.
Board type: ${handshake.boardType},
imu type: ${handshake.imuType},
firmware: ${handshake.firmware} (${connection.firmwareBuild}),
firmware name: ${handshake.firmware},
protocol version: ${connection.protocolVersion},
mac: ${handshake.macString},
name: ${connection.name}
""".trimIndent(),
)
}
if (connection.protocol == NetworkProtocol.OWO_LEGACY || connection.firmwareBuild < 9) {
if (connection.protocol == NetworkProtocol.OWO_LEGACY || connection.protocolVersion < 9) {
// Set up new sensor for older firmware.
// Firmware after 7 should send sensor status packet and sensor
// will be created when it's received
setUpSensor(connection, 0, handshake.imuType, 1, MagnetometerStatus.NOT_SUPPORTED)
setUpSensor(connection, 0, handshake.imuType, 1, MagnetometerStatus.NOT_SUPPORTED, null, TrackerDataType.ROTATION)
}
connection
}
@@ -188,7 +187,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
}
private val mainScope = CoroutineScope(SupervisorJob())
private fun setUpSensor(connection: UDPDevice, trackerId: Int, sensorType: IMUType, sensorStatus: Int, magStatus: MagnetometerStatus) {
private fun setUpSensor(connection: UDPDevice, trackerId: Int, sensorType: IMUType, sensorStatus: Int, magStatus: MagnetometerStatus, trackerPosition: TrackerPosition?, trackerDataType: TrackerDataType) {
LogManager.info("[TrackerServer] Sensor $trackerId for ${connection.name} status: $sensorStatus")
var imuTracker = connection.getTracker(trackerId)
if (imuTracker == null) {
@@ -202,21 +201,22 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
VRServer.getNextLocalTrackerId(),
connection.name + "/" + trackerId,
"IMU Tracker $formattedHWID",
null,
trackerPosition,
trackerNum = trackerId,
hasRotation = true,
hasAcceleration = true,
userEditable = true,
imuType = sensorType,
imuType = if (trackerDataType == TrackerDataType.ROTATION) sensorType else null,
allowFiltering = true,
needsReset = true,
needsMounting = true,
usesTimeout = true,
magStatus = magStatus,
trackerDataType = trackerDataType,
)
connection.trackers[trackerId] = imuTracker
trackersConsumer.accept(imuTracker)
LogManager.info("[TrackerServer] Added sensor $trackerId for ${connection.name}, type $sensorType")
LogManager.info("[TrackerServer] Added sensor $trackerId for ${connection.name}, ImuType $sensorType, DataType $trackerDataType, default TrackerPosition $trackerPosition")
}
val status = UDPPacket15SensorInfo.getStatus(sensorStatus)
if (status != null) imuTracker.status = status
@@ -479,6 +479,8 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
packet.sensorType,
packet.sensorStatus,
magStatus,
packet.trackerPosition,
packet.trackerDataType,
)
// Send ack
bb.limit(bb.capacity())
@@ -561,6 +563,17 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
LogManager.info("[TrackerServer] Acknowledged config change on ${connection.descriptiveName} (${trackers.map { it.trackerNum }.joinToString()}). Config changed on ${packet.configType}")
}
is UDPPacket26FlexData -> {
tracker = connection?.getTracker(packet.sensorId)
if (tracker == null) return
if (tracker.trackerDataType == TrackerDataType.FLEX_RESISTANCE) {
tracker.trackerFlexHandler.setFlexResistance(packet.flexData)
} else if (tracker.trackerDataType == TrackerDataType.FLEX_ANGLE) {
tracker.trackerFlexHandler.setFlexAngle(packet.flexData)
}
tracker.dataTick()
}
is UDPPacket200ProtocolChange -> {}
}
}

View File

@@ -48,7 +48,7 @@ class UDPDevice(
var protocol: NetworkProtocol? = null
@JvmField
var firmwareBuild = 0
var protocolVersion = 0
@JvmField
var timedOut = false

View File

@@ -1,5 +1,6 @@
package dev.slimevr.tracking.trackers.udp
import dev.slimevr.tracking.trackers.TrackerPosition
import dev.slimevr.tracking.trackers.TrackerStatus
import io.github.axisangles.ktmath.Quaternion
import io.github.axisangles.ktmath.Vector3
@@ -95,7 +96,7 @@ data class UDPPacket3Handshake(
var boardType: BoardType = BoardType.UNKNOWN,
var imuType: IMUType = IMUType.UNKNOWN,
var mcuType: MCUType = MCUType.UNKNOWN,
var firmwareBuild: Int = 0,
var protocolVersion: Int = 0,
var firmware: String? = null,
var macString: String? = null,
) : UDPPacket(3) {
@@ -115,7 +116,7 @@ data class UDPPacket3Handshake(
buf.int
buf.int // IMU info
}
if (buf.remaining() > 3) firmwareBuild = buf.int
if (buf.remaining() > 3) protocolVersion = buf.int
var length = 0
if (buf.remaining() > 0) length = buf.get().toInt()
// firmware version length is 1 longer than
@@ -224,6 +225,8 @@ data class UDPPacket15SensorInfo(
var sensorStatus: Int = 0,
var sensorType: IMUType = IMUType.UNKNOWN,
var sensorConfig: SensorConfig? = null,
var trackerDataType: TrackerDataType = TrackerDataType.ROTATION,
var trackerPosition: TrackerPosition? = null,
) : UDPPacket(15),
SensorSpecificPacket {
override var sensorId = 0
@@ -231,12 +234,13 @@ data class UDPPacket15SensorInfo(
sensorId = buf.get().toInt() and 0xFF
sensorStatus = buf.get().toInt() and 0xFF
if (buf.remaining() > 0) {
sensorType =
IMUType.getById(buf.get().toUInt() and 0xFFu) ?: IMUType.UNKNOWN
sensorType = IMUType.getById(buf.get().toUInt() and 0xFFu) ?: IMUType.UNKNOWN
}
if (buf.remaining() > 1) {
sensorConfig = SensorConfig(buf.getShort().toUShort())
}
if (buf.remaining() > 0) trackerPosition = TrackerPosition.getById(buf.get().toInt() and 0xFF)
if (buf.remaining() > 0) trackerDataType = TrackerDataType.getById(buf.get().toUInt() and 0xFFu) ?: TrackerDataType.ROTATION
}
companion object {
@@ -378,6 +382,18 @@ data class UDPPacket25SetConfigFlag(
}
}
data class UDPPacket26FlexData(
var flexData: Float = 0f,
) : UDPPacket(26),
SensorSpecificPacket {
override var sensorId = 0
override fun readData(buf: ByteBuffer) {
sensorId = buf.get().toInt() and 0xFF
flexData = UDPUtils.getSafeBufferFloat(buf)
}
}
data class UDPPacket200ProtocolChange(
var targetProtocol: Int = 0,
var targetProtocolVersion: Int = 0,

View File

@@ -115,6 +115,7 @@ class UDPProtocolParser {
PACKET_USER_ACTION -> UDPPacket21UserAction()
PACKET_FEATURE_FLAGS -> UDPPacket22FeatureFlags()
PACKET_ACK_CONFIG_CHANGE -> UDPPacket24AckConfigChange()
PACKET_FLEX_DATA -> UDPPacket26FlexData()
PACKET_PROTOCOL_CHANGE -> UDPPacket200ProtocolChange()
else -> null
}
@@ -150,6 +151,7 @@ class UDPProtocolParser {
const val PACKET_ROTATION_AND_ACCELERATION = 23
const val PACKET_ACK_CONFIG_CHANGE = 24
const val PACKET_SET_CONFIG_FLAG = 25
const val PACKET_FLEX_DATA = 26
const val PACKET_BUNDLE = 100
const val PACKET_BUNDLE_COMPACT = 101
const val PACKET_PROTOCOL_CHANGE = 200