Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b2cf2ec
Re-add bone references.
Sebane1 Jul 29, 2026
cb0e6bc
Restore toes back to the UI.
Sebane1 Jul 29, 2026
db58dde
Toe assigments and toe rotation sharing restored.
Sebane1 Jul 29, 2026
602e173
Add toe tracking plugin OSC output for toes.
Sebane1 Jul 29, 2026
6ccb273
Add tests for ToesImputeProcessor
Sebane1 Jul 29, 2026
1bcd01f
Adjust variable wording to be more precise in ToeImputeProcessor test.
Sebane1 Jul 29, 2026
f02b03c
Minor adjustments.
Sebane1 Jul 29, 2026
05eb030
Refactor naming for toe tracking mapping and associated test.
Sebane1 Jul 29, 2026
aec5ae6
Refactor toe joints, and add two additional toe joints.
Sebane1 Jul 29, 2026
674586e
Add extra toe bones to skeleton helper.
Sebane1 Jul 29, 2026
4e00b2d
Fix translations for toes.
Sebane1 Jul 30, 2026
6b638b1
Fix incorrect toe mappings.
Sebane1 Jul 30, 2026
ba7c002
Update test to cover all new toe entries.
Sebane1 Jul 30, 2026
8c8d9c9
Adjust toe entries
Sebane1 Jul 30, 2026
f374e70
Fix toe assignments again.
Sebane1 Jul 30, 2026
5a314e8
Update server/core/src/main/java/dev/slimevr/skeleton/bone-geometry.kt
Sebane1 Jul 30, 2026
4f308ee
Update server/core/src/main/java/dev/slimevr/skeleton/bone-geometry.kt
Sebane1 Jul 30, 2026
211222f
Update server/core/src/main/java/dev/slimevr/vrcosc/toe-plugin-output…
Sebane1 Jul 30, 2026
64620bb
Rename pinky toe to little toe.
Sebane1 Jul 30, 2026
1a3077e
Merge branch 'toe-tracking-server-rewrite' of https://github.com/Seba…
Sebane1 Jul 30, 2026
96f212f
Read in data from additional toes. Define toe calculation values with…
Sebane1 Jul 30, 2026
a5f3ac4
Apparently constants dont need classes.
Sebane1 Jul 30, 2026
43fa7c6
Update server/core/src/main/java/dev/slimevr/vrcosc/toe-plugin-output…
Sebane1 Jul 31, 2026
8966312
Adjust VMC mappings, correct casing of constants.
Sebane1 Jul 31, 2026
bc7da07
Merge branch 'toe-tracking-server-rewrite' of https://github.com/Seba…
Sebane1 Jul 31, 2026
78c06bd
Simplify splay direction syntax.
Sebane1 Jul 31, 2026
e41a3dc
Move osc name extensions back to the toe plugin output encoder. Add …
Sebane1 Jul 31, 2026
fd61681
Use add all for messages.
Sebane1 Jul 31, 2026
895b7c8
We can calculate the OSC toe number once.
Sebane1 Jul 31, 2026
66bffbd
Merge branch 'server-rewrite' into pr/1897
Erimelowo Jul 31, 2026
b2b3b02
Merge branch 'server-rewrite' into pr/1897
Erimelowo Jul 31, 2026
82e4995
spotless
Erimelowo Jul 31, 2026
6dbaa03
fix side.kt
Erimelowo Jul 31, 2026
f945f22
cleanup extra VMC_MIRROR_BONE_PAIRS entries
Erimelowo Jul 31, 2026
cd9bd76
Only the big toe needs to splay in the opposite direction.
Sebane1 Jul 31, 2026
5993b82
Merge remote-tracking branch 'upstream/server-rewrite' into toe-track…
Sebane1 Jul 31, 2026
4836218
Just to future proof. We should also send toe splay as a float for fu…
Sebane1 Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions gui/app/public/i18n/en/translation.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,17 @@ tracker_selection_menu-LEFT_HAND = { -tracker_selection-part } left hand?
tracker_selection_menu-LEFT_UPPER_LEG = { -tracker_selection-part } left thigh?
tracker_selection_menu-LEFT_LOWER_LEG = { -tracker_selection-part } left ankle?
tracker_selection_menu-LEFT_FOOT = { -tracker_selection-part } left foot?
tracker_selection_menu-LEFT_BIG_TOE = { -tracker_selection-part } left big toe?
tracker_selection_menu-LEFT_INDEX_TOE = { -tracker_selection-part } left index toe?
tracker_selection_menu-LEFT_MIDDLE_TOE = { -tracker_selection-part } left middle toe?
tracker_selection_menu-LEFT_RING_TOE = { -tracker_selection-part } left ring toe?
tracker_selection_menu-LEFT_LITTLE_TOE = { -tracker_selection-part } left little toe?
tracker_selection_menu-RIGHT_BIG_TOE = { -tracker_selection-part } right big toe?
tracker_selection_menu-RIGHT_INDEX_TOE = { -tracker_selection-part } right index toe?
tracker_selection_menu-RIGHT_MIDDLE_TOE = { -tracker_selection-part } right middle toe?
tracker_selection_menu-RIGHT_RING_TOE = { -tracker_selection-part } right ring toe?
tracker_selection_menu-RIGHT_LITTLE_TOE = { -tracker_selection-part } right little toe?


tracker_selection_menu-unassigned = Unassigned Trackers
tracker_selection_menu-assigned = Assigned Trackers
Expand Down Expand Up @@ -1193,6 +1204,42 @@ onboarding-assign_trackers-warning-WAIST = Waist is assigned but you need { $una
*[unknown] Unknown unassigned body part
} to also be assigned!

onboarding-assign_trackers-warning-LEFT_BIG_TOE = Left Big Toe is assigned but you need { $unassigned ->
[0] the left foot
*[unknown] Unknown unassigned body part
} to also be assigned!
onboarding-assign_trackers-warning-LEFT_INDEX_TOE = Left Index Toe is assigned but you need { $unassigned ->
[0] the left foot
*[unknown] Unknown unassigned body part
} to also be assigned!
onboarding-assign_trackers-warning-LEFT_MIDDLE_TOE = Left Middle Toe is assigned but you need { $unassigned ->
[0] the left foot
*[unknown] Unknown unassigned body part} to also be assigned!
onboarding-assign_trackers-warning-LEFT_RING_TOE = Left Ring Toe is assigned but you need { $unassigned ->
[0] the left foot
*[unknown] Unknown unassigned body part} to also be assigned!
onboarding-assign_trackers-warning-LEFT_LITTLE_TOE = Left Little Toe is assigned but you need { $unassigned ->
[0] the left foot
*[unknown] Unknown unassigned body part} to also be assigned!
onboarding-assign_trackers-warning-RIGHT_BIG_TOE = Left Big Toe is assigned but you need { $unassigned ->
[0] the right foot
*[unknown] Unknown unassigned body part
} to also be assigned!
onboarding-assign_trackers-warning-RIGHT_INDEX_TOE = Right Index Toe is assigned but you need { $unassigned ->
[0] the right foot
*[unknown] Unknown unassigned body part
} to also be assigned!
onboarding-assign_trackers-warning-RIGHT_MIDDLE_TOE = Right Middle Toe is assigned but you need { $unassigned ->
[0] the right foot
*[unknown] Unknown unassigned body part} to also be assigned!
onboarding-assign_trackers-warning-RIGHT_RING_TOE = Right Ring Toe is assigned but you need { $unassigned ->
[0] the right foot
*[unknown] Unknown unassigned body part} to also be assigned!
onboarding-assign_trackers-warning-RIGHT_LITTLE_TOE = Right Little Toe is assigned but you need { $unassigned ->
[0] the right foot
*[unknown] Unknown unassigned body part} to also be assigned!


## Tracker mounting method choose
onboarding-choose_mounting = What mounting calibration method to use?
# Multiline text
Expand Down Expand Up @@ -1704,3 +1751,15 @@ toolbar-mounting_calibration-fingers = Fingers
toolbar-drift_reset = Drift Reset
toolbar-assigned_trackers = {$count} trackers assigned
toolbar-unassigned_trackers = {$count} trackers unassigned
body_part-LEFT_BIG_TOE = Left Big Toe
body_part-LEFT_INDEX_TOE = Left Index Toe
body_part-LEFT_MIDDLE_TOE = Left Middle Toe
body_part-LEFT_RING_TOE = Left Ring Toe
body_part-LEFT_LITTLE_TOE = Left Little Toe

body_part-RIGHT_TOES = Right Toes
body_part-RIGHT_BIG_TOE = Right Big Toe
body_part-RIGHT_INDEX_TOE = Right Index Toe
body_part-RIGHT_MIDDLE_TOE = Right Middle Toe
body_part-RIGHT_RING_TOE = Right Ring Toe
body_part-RIGHT_LITTLE_TOE = Right Little Toe
9 changes: 8 additions & 1 deletion gui/app/src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function Toolbar() {

const { visibleGroups, groupVisibility } = useMemo(() => {
const groupVisibility = Object.keys(BODY_PARTS_GROUPS)
.filter((k) => ['fingers'].includes(k))
.filter((k) => ['fingers', 'toes'].includes(k))
.reduce(
(curr, key) => {
const group = key as MountingResetGroup;
Expand Down Expand Up @@ -200,6 +200,13 @@ export function Toolbar() {
group={'feet'}
customName="toolbar-mounting_calibration-feet"
/>
{groupVisibility['toes'] && (
<BasicResetButton
type={ResetType.MOUNTING}
group={'toes'}
customName="toolbar-mounting_calibration-toes"
/>
)}
{groupVisibility['fingers'] && (
<BasicResetButton
type={ResetType.MOUNTING}
Expand Down
17 changes: 14 additions & 3 deletions gui/app/src/components/commons/BodyInteractions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function BodyInteractions({
variant = 'tracker-select',
mirror,
onSelectRole,
onToesSelected,
}: {
leftControls?: ReactNode;
rightControls?: ReactNode;
Expand All @@ -21,6 +22,7 @@ export function BodyInteractions({
variant?: 'dots' | 'tracker-select';
assignedRoles: BodyPart[];
onSelectRole: (role: BodyPart) => void;
onToesSelected?: (side: 'left' | 'right') => void;
highlightedRoles: BodyPart[];
mirror: boolean;
}) {
Expand Down Expand Up @@ -114,7 +116,10 @@ export function BodyInteractions({
...slotPosition,
id: slot.id,
hidden:
variant === 'tracker-select' && !controlsPosIds.includes(slot.id),
variant === 'tracker-select' &&
!controlsPosIds.includes(slot.id) &&
slot.id !== 'left-toes' &&
slot.id !== 'right-toes',
buttonOffset: {
left: canvasBox.left - personBox.left,
top: canvasBox.top - personBox.top,
Expand Down Expand Up @@ -211,8 +216,14 @@ export function BodyInteractions({
({ top, left, height, width, id, hidden, buttonOffset }) => (
<div
key={id}
className={classNames('absolute z-10')}
onClick={() => onSelectRole((BodyPart as any)[id])}
className={classNames('absolute z-10', 'cursor-pointer')}
onClick={() => {
if (id === 'left-toes' || id === 'right-toes') {
onToesSelected?.(id === 'left-toes' ? 'left' : 'right');
return;
}
onSelectRole((BodyPart as any)[id]);
}}
style={{
top: top + height / 2 - dotsSize / 2 + buttonOffset.top,
left: left + width / 2 - dotsSize / 2 + buttonOffset.left,
Expand Down
32 changes: 31 additions & 1 deletion gui/app/src/components/commons/BodyPartIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,38 @@ export const mapPart: Record<
<FingersIcon width={width} />
),
[BodyPart.RIGHT_LITTLE_DISTAL]: ({ width }) => <FingersIcon width={width} />,
[BodyPart.LEFT_BIG_TOE]: renderFootLeft,
[BodyPart.LEFT_INDEX_TOE]: renderFootLeft,
[BodyPart.LEFT_LITTLE_TOE]: renderFootLeft,
[BodyPart.RIGHT_BIG_TOE]: renderFootRight,
[BodyPart.RIGHT_INDEX_TOE]: renderFootRight,
[BodyPart.RIGHT_LITTLE_TOE]: renderFootRight,
};
function renderFootLeft({
width,
currentLocales,
}: {
width?: number;
currentLocales: string[];
}) {
if (currentLocales.includes('en-x-owo')) {
return <PawIcon />;
}
return <FootIcon width={width} />;
}

function renderFootRight({
width,
currentLocales,
}: {
width?: number;
currentLocales: string[];
}) {
if (currentLocales.includes('en-x-owo')) {
return <PawIcon />;
}
return <FootIcon width={width} flipped />;
}
export function BodyPartIcon({
bodyPart = BodyPart.NONE,
width = 24,
Expand All @@ -138,7 +168,7 @@ export function BodyPartIcon({
return (
<svg width={width} height={width}>
<rect width={width} height={width} rx="2" fill="#56407B" />
{mapPart[bodyPart]({ width, currentLocales })}
{mapPart[bodyPart]?.({ width, currentLocales })}
</svg>
);
}
21 changes: 21 additions & 0 deletions gui/app/src/components/commons/PersonFrontIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export const SIDES = [
upperLeg: BodyPart.LEFT_UPPER_LEG,
lowerLeg: BodyPart.LEFT_LOWER_LEG,
foot: BodyPart.LEFT_FOOT,
bigToe: BodyPart.LEFT_BIG_TOE,
indexToe: BodyPart.LEFT_INDEX_TOE,
littleToe: BodyPart.LEFT_LITTLE_TOE,
},
{
shoulder: BodyPart.RIGHT_SHOULDER,
Expand All @@ -18,6 +21,9 @@ export const SIDES = [
upperLeg: BodyPart.RIGHT_UPPER_LEG,
lowerLeg: BodyPart.RIGHT_LOWER_LEG,
foot: BodyPart.RIGHT_FOOT,
bigToe: BodyPart.RIGHT_BIG_TOE,
indexToe: BodyPart.RIGHT_INDEX_TOE,
littleToe: BodyPart.RIGHT_LITTLE_TOE,
},
];

Expand Down Expand Up @@ -121,6 +127,14 @@ export function PersonFrontIcon({ mirror = true }: { mirror?: boolean }) {
id={BodyPart[SIDES[right].foot]}
/>

<circle
className="body-part-circle"
cx="102"
cy="393"
r={CIRCLE_RADIUS}
id="right-toes"
/>

<circle
className="body-part-circle"
cx="15"
Expand Down Expand Up @@ -172,6 +186,13 @@ export function PersonFrontIcon({ mirror = true }: { mirror?: boolean }) {
r={CIRCLE_RADIUS}
id={BodyPart[SIDES[left].foot]}
/>
<circle
className="body-part-circle"
cx="62"
cy="393"
r={CIRCLE_RADIUS}
id="left-toes"
/>
</svg>
);
}
1 change: 1 addition & 0 deletions gui/app/src/components/home/ResetButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function ResetButtonIcon(options: UseResetOptions) {
if (options.type === ResetType.MOUNTING) {
if (options.group === 'default') return <SkiIcon />;
if (options.group === 'feet') return <FootIcon />;
if (options.group === 'toes') return <FootIcon />;
if (options.group === 'fingers') return <FingersIcon width={16} />;
}
}
Expand Down
20 changes: 20 additions & 0 deletions gui/app/src/components/onboarding/BodyAssignment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ export const LOWER_BODY = new Set([
BodyPart.RIGHT_FOOT,
...LEGS_PARTS,
]);

export const TOES_PARTS = new Set([
BodyPart.LEFT_BIG_TOE,
BodyPart.RIGHT_BIG_TOE,
BodyPart.LEFT_INDEX_TOE,
BodyPart.RIGHT_INDEX_TOE,
BodyPart.LEFT_LITTLE_TOE,
BodyPart.RIGHT_LITTLE_TOE,
]);

export const SPINE_PARTS = [
BodyPart.UPPER_CHEST,
BodyPart.CHEST,
Expand All @@ -35,6 +45,12 @@ export const SPINE_PARTS = [
export const ASSIGNMENT_RULES: Partial<
Record<BodyPart, (BodyPart | BodyPart[])[]>
> = {
[BodyPart.LEFT_BIG_TOE]: [BodyPart.LEFT_FOOT],
[BodyPart.LEFT_INDEX_TOE]: [BodyPart.LEFT_FOOT],
[BodyPart.LEFT_LITTLE_TOE]: [BodyPart.LEFT_FOOT],
[BodyPart.RIGHT_BIG_TOE]: [BodyPart.RIGHT_FOOT],
[BodyPart.RIGHT_INDEX_TOE]: [BodyPart.RIGHT_FOOT],
[BodyPart.RIGHT_LITTLE_TOE]: [BodyPart.RIGHT_FOOT],
[BodyPart.LEFT_FOOT]: [
BodyPart.LEFT_LOWER_LEG,
BodyPart.LEFT_UPPER_LEG,
Expand Down Expand Up @@ -93,13 +109,15 @@ export const ASSIGNMENT_MODES: Record<AssignMode, BodyPart[]> = {
...SPINE_PARTS,
...ARMS_PARTS,
...LEGS_PARTS,
...TOES_PARTS,
],
};

export function BodyAssignment({
assignMode,
mirror,
onRoleSelected,
onToesSelected,
rolesWithErrors = {},
highlightedRoles = [],
onlyAssigned = false,
Expand All @@ -111,6 +129,7 @@ export function BodyAssignment({
rolesWithErrors?: Partial<Record<BodyPart, BodyPartError>>;
highlightedRoles?: BodyPart[];
onRoleSelected: (role: BodyPart) => void;
onToesSelected?: (side: 'left' | 'right') => void;
width?: number;
dotSize?: number;
}) {
Expand Down Expand Up @@ -159,6 +178,7 @@ export function BodyAssignment({
assignedRoles={assignedRoles}
highlightedRoles={highlightedRoles}
onSelectRole={onRoleSelected}
onToesSelected={onToesSelected}
leftControls={
<div className="flex flex-col justify-between h-full text-right">
<div className="flex flex-col gap-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,60 @@ export const mapPart: Record<
<FingersIcon width={width} />
),
[BodyPart.RIGHT_LITTLE_DISTAL]: ({ width }) => <FingersIcon width={width} />,
[BodyPart.LEFT_BIG_TOE]: ({ width, currentLocales }) =>
currentLocales.includes('en-x-owo') ? (
<PawIcon
width={width ? width * 0.75 : undefined}
transform="translate(40, -50)"
/>
) : (
<FootIcon width={width} />
),
[BodyPart.LEFT_INDEX_TOE]: ({ width, currentLocales }) =>
currentLocales.includes('en-x-owo') ? (
<PawIcon
width={width ? width * 0.75 : undefined}
transform="translate(40, -50)"
/>
) : (
<FootIcon width={width} />
),
[BodyPart.LEFT_LITTLE_TOE]: ({ width, currentLocales }) =>
currentLocales.includes('en-x-owo') ? (
<PawIcon
width={width ? width * 0.75 : undefined}
transform="translate(40, -50)"
/>
) : (
<FootIcon width={width} />
),
[BodyPart.RIGHT_BIG_TOE]: ({ width, currentLocales }) =>
currentLocales.includes('en-x-owo') ? (
<PawIcon
width={width ? width * 0.75 : undefined}
transform="translate(40, -50)"
/>
) : (
<FootIcon width={width} flipped />
),
[BodyPart.RIGHT_INDEX_TOE]: ({ width, currentLocales }) =>
currentLocales.includes('en-x-owo') ? (
<PawIcon
width={width ? width * 0.75 : undefined}
transform="translate(40, -50)"
/>
) : (
<FootIcon width={width} flipped />
),
[BodyPart.RIGHT_LITTLE_TOE]: ({ width, currentLocales }) =>
currentLocales.includes('en-x-owo') ? (
<PawIcon
width={width ? width * 0.75 : undefined}
transform="translate(40, -50)"
/>
) : (
<FootIcon width={width} flipped />
),
};

export function MountingBodyPartIcon({
Expand All @@ -158,7 +212,9 @@ export function MountingBodyPartIcon({
width?: number;
}) {
const { currentLocales } = useLocaleConfig();
return mapPart[bodyPart]({ width, currentLocales });
return (
mapPart[bodyPart]?.({ width, currentLocales }) || <FootIcon width={width} />
);
}

function PieSliceOfFeet({
Expand Down
Loading