Skip to content

Commit 0c4442b

Browse files
authored
feat: Add HTC face tracking support (#5)
1 parent cbed60a commit 0c4442b

File tree

3 files changed

+211
-5
lines changed

3 files changed

+211
-5
lines changed

ALVRModule/ALVRModule.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ public class ALVRModule : ExtTrackingModule
1919
{
2020
["EyesQuat"] = EyeTracking.SetEyesQuatParams,
2121
["CombQuat"] = EyeTracking.SetCombEyesQuatParams,
22-
["FaceFb\0\0"] = MetaFaceTracking.SetFace1FbParams,
23-
["Face2Fb\0"] = MetaFaceTracking.SetFace2FbParams,
22+
["FaceFb\0\0"] = FbFaceTracking.SetFace1FbParams,
23+
["Face2Fb\0"] = FbFaceTracking.SetFace2FbParams,
2424
["FacePico"] = PicoFaceTracking.SetFacePicoParams,
25+
["EyesHtc\0"] = HtcFaceTracking.SetEyesHtcParams,
26+
["LipHtc\0\0"] = HtcFaceTracking.SetLipHtcParams,
2527
};
2628

2729
public override (bool SupportsEye, bool SupportsExpression) Supported => (true, true);

ALVRModule/MetaFaceTracking.cs ALVRModule/FbFaceTracking.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@ public enum FaceFb
8181
Face2FbMax = 70,
8282
}
8383

84-
public class MetaFaceTracking
84+
public class FbFaceTracking
8585
{
8686
private static void SetFaceFbParams(FloatParams p, FloatWeightParams w, UnifiedEyeData eye)
8787
{
88-
eye.Right.Openness = 1.0f - Math.Max(0, Math.Min(1, p[EyesClosedR] + p[EyesClosedR] * p[LidTightenerR]));
89-
eye.Left.Openness = 1.0f - Math.Max(0, Math.Min(1, p[EyesClosedL] + p[EyesClosedL] * p[LidTightenerL]));
88+
eye.Right.Openness = 1.0f - Math.Clamp(p[EyesClosedR] + p[EyesClosedR] * p[LidTightenerR], 0.0f, 1.0f);
89+
eye.Left.Openness = 1.0f - Math.Clamp(p[EyesClosedL] + p[EyesClosedL] * p[LidTightenerL], 0.0f, 1.0f);
9090

9191
#region Eyelids
9292

ALVRModule/HtcFaceTracking.cs

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// Code inspired from https://github.com/VRCFaceTracking/SRanipalTrackingModule/blob/master/SRanipalExtTrackingModule/SRanipalTrackingInterface.cs
2+
3+
using static VRCFaceTracking.Core.Params.Expressions.UnifiedExpressions;
4+
using U = VRCFaceTracking.Core.Params.Expressions.UnifiedExpressions;
5+
using VRCFaceTracking.Core.Params.Data;
6+
7+
8+
namespace ALVRModule
9+
{
10+
using static EyesHtc;
11+
using static LipHtc;
12+
13+
public enum EyesHtc
14+
{
15+
LeftBlink = 0,
16+
LeftWide = 1,
17+
RightBlink = 2,
18+
RightWide = 3,
19+
LeftSqueeze = 4,
20+
RightSqueeze = 5,
21+
LeftDown = 6,
22+
RightDown = 7,
23+
LeftOut = 8,
24+
RightIn = 9,
25+
LeftIn = 10,
26+
RightOut = 11,
27+
LeftUp = 12,
28+
RightUp = 13,
29+
EyesMax = 14,
30+
};
31+
32+
public enum LipHtc
33+
{
34+
JawRight = 0,
35+
JawLeft = 1,
36+
JawForward = 2,
37+
JawOpen = 3,
38+
MouthApeShape = 4,
39+
MouthUpperRight = 5,
40+
MouthUpperLeft = 6,
41+
MouthLowerRight = 7,
42+
MouthLowerLeft = 8,
43+
MouthUpperOverturn = 9,
44+
MouthLowerOverturn = 10,
45+
MouthPout = 11,
46+
MouthSmileRight = 12,
47+
MouthSmileLeft = 13,
48+
MouthSadRight = 14,
49+
MouthSadLeft = 15,
50+
CheekPuffRight = 16,
51+
CheekPuffLeft = 17,
52+
CheekSuck = 18,
53+
MouthUpperUpright = 19,
54+
MouthUpperUpleft = 20,
55+
MouthLowerDownright = 21,
56+
MouthLowerDownleft = 22,
57+
MouthUpperInside = 23,
58+
MouthLowerInside = 24,
59+
MouthLowerOverlay = 25,
60+
TongueLongstep1 = 26,
61+
TongueLeft = 27,
62+
TongueRight = 28,
63+
TongueUp = 29,
64+
TongueDown = 30,
65+
TongueRoll = 31,
66+
TongueLongstep2 = 32,
67+
TongueUprightMorph = 33,
68+
TongueUpleftMorph = 34,
69+
TongueDownrightMorph = 35,
70+
TongueDownleftMorph = 36,
71+
LipMax = 37,
72+
}
73+
74+
public class HtcFaceTracking
75+
{
76+
public static void SetEyesHtcParams(FloatParams p, FloatWeightParams w, UnifiedEyeData eye)
77+
{
78+
p.Read((int)EyesMax);
79+
80+
eye.Right.Openness = 1.0f - Math.Clamp(p[RightBlink] + p[RightBlink] * p[RightSqueeze], 0.0f, 1.0f);
81+
eye.Left.Openness = 1.0f - Math.Clamp(p[LeftBlink] + p[LeftBlink] * p[LeftSqueeze], 0.0f, 1.0f);
82+
83+
eye.Right.Gaze.x = p[RightOut] - p[RightIn];
84+
eye.Right.Gaze.y = p[RightUp] - p[RightDown];
85+
eye.Left.Gaze.x = -p[LeftOut] + p[LeftIn];
86+
eye.Left.Gaze.y = p[LeftUp] - p[LeftDown];
87+
88+
eye.Left.PupilDiameter_MM = 5f;
89+
eye.Right.PupilDiameter_MM = 5f;
90+
91+
eye._minDilation = 0;
92+
eye._maxDilation = 10;
93+
94+
w[EyeWideLeft] = p[LeftWide];
95+
w[EyeWideRight] = p[RightWide];
96+
97+
w[EyeSquintLeft] = p[LeftSqueeze];
98+
w[EyeSquintRight] = p[RightSqueeze];
99+
100+
w[BrowInnerUpLeft] = p[LeftWide];
101+
w[BrowOuterUpLeft] = p[LeftWide];
102+
103+
w[BrowInnerUpRight] = p[RightWide];
104+
w[BrowOuterUpRight] = p[RightWide];
105+
106+
w[BrowPinchLeft] = p[LeftSqueeze];
107+
w[BrowLowererLeft] = p[LeftSqueeze];
108+
109+
w[BrowPinchRight] = p[RightSqueeze];
110+
w[BrowLowererRight] = p[RightSqueeze];
111+
}
112+
113+
public static void SetLipHtcParams(FloatParams p, FloatWeightParams w, UnifiedEyeData eye)
114+
{
115+
p.Read((int)LipMax);
116+
117+
#region Direct Jaw
118+
119+
w[U.JawOpen] = p[JawOpen] + p[MouthApeShape];
120+
w[U.JawLeft] = p[JawLeft];
121+
w[U.JawRight] = p[JawRight];
122+
w[U.JawForward] = p[JawForward];
123+
w[MouthClosed] = p[MouthApeShape];
124+
125+
#endregion
126+
127+
#region Direct Mouth and Lip
128+
129+
w[MouthUpperUpRight] = p[MouthUpperRight] - p[MouthUpperOverturn];
130+
w[MouthUpperDeepenRight] = p[MouthUpperRight] - p[MouthUpperOverturn];
131+
w[MouthUpperUpLeft] = p[MouthUpperLeft] - p[MouthUpperOverturn];
132+
w[MouthUpperDeepenLeft] = p[MouthUpperLeft] - p[MouthUpperOverturn];
133+
134+
w[MouthLowerDownLeft] = p[MouthLowerLeft] - p[MouthLowerOverturn];
135+
w[MouthLowerDownRight] = p[MouthLowerRight] - p[MouthLowerOverturn];
136+
137+
w[LipPuckerUpperLeft] = p[MouthPout];
138+
w[LipPuckerLowerLeft] = p[MouthPout];
139+
w[LipPuckerUpperRight] = p[MouthPout];
140+
w[LipPuckerLowerRight] = p[MouthPout];
141+
142+
w[LipFunnelUpperLeft] = p[MouthUpperOverturn];
143+
w[LipFunnelUpperRight] = p[MouthUpperOverturn];
144+
w[LipFunnelLowerLeft] = p[MouthUpperOverturn];
145+
w[LipFunnelLowerRight] = p[MouthUpperOverturn];
146+
147+
w[LipSuckUpperLeft] = p[MouthUpperInside];
148+
w[LipSuckUpperRight] = p[MouthUpperInside];
149+
w[LipSuckLowerLeft] = p[MouthLowerInside];
150+
w[LipSuckLowerRight] = p[MouthLowerInside];
151+
152+
w[U.MouthUpperLeft] = p[MouthUpperLeft];
153+
w[U.MouthUpperRight] = p[MouthUpperRight];
154+
w[U.MouthLowerLeft] = p[MouthLowerLeft];
155+
w[U.MouthLowerRight] = p[MouthLowerRight];
156+
157+
w[MouthCornerPullLeft] = p[MouthSmileLeft];
158+
w[MouthCornerPullRight] = p[MouthSmileRight];
159+
w[MouthCornerSlantLeft] = p[MouthSmileLeft];
160+
w[MouthCornerSlantRight] = p[MouthSmileRight];
161+
w[MouthFrownLeft] = p[MouthSadLeft];
162+
w[MouthFrownRight] = p[MouthSadRight];
163+
164+
w[MouthRaiserUpper] = p[MouthLowerOverlay] - p[MouthUpperInside];
165+
w[MouthRaiserLower] = p[MouthLowerOverlay];
166+
167+
#endregion
168+
169+
#region Direct Cheek
170+
171+
w[U.CheekPuffLeft] = p[CheekPuffLeft];
172+
w[U.CheekPuffRight] = p[CheekPuffRight];
173+
174+
w[CheekSuckLeft] = p[CheekSuck];
175+
w[CheekSuckRight] = p[CheekSuck];
176+
177+
#endregion
178+
179+
#region Direct Tongue
180+
181+
w[TongueOut] = (p[TongueLongstep1] + p[TongueLongstep2]) / 2.0f;
182+
w[U.TongueUp] = p[TongueUp];
183+
w[U.TongueDown] = p[TongueDown];
184+
w[U.TongueLeft] = p[TongueLeft];
185+
w[U.TongueRight] = p[TongueRight];
186+
w[U.TongueRoll] = p[TongueRoll];
187+
188+
#endregion
189+
190+
#region Emulated Unified Mapping
191+
192+
w[CheekSquintLeft] = p[MouthSmileLeft];
193+
w[CheekSquintRight] = p[MouthSmileRight];
194+
195+
w[MouthDimpleLeft] = p[MouthSmileLeft];
196+
w[MouthDimpleRight] = p[MouthSmileRight];
197+
198+
w[MouthStretchLeft] = p[MouthSadRight];
199+
w[MouthStretchRight] = p[MouthSadRight];
200+
201+
#endregion
202+
}
203+
}
204+
}

0 commit comments

Comments
 (0)