Skip to content

Commit 67f63e9

Browse files
authored
Merge pull request lonelyicer#8 from lonelyicer/dev
feat: compatible with other facetracking accessories
2 parents 8f60901 + fbaa47c commit 67f63e9

7 files changed

Lines changed: 100 additions & 71 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ riderModule.iml
55
/_ReSharper.Caches/
66
/.vs
77
/output
8+
/.idea

VRCFTPicoModule/Data/BlendShapeIndex.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
namespace VRCFTPicoModule.Data;
1+
// ReSharper disable InconsistentNaming
2+
namespace VRCFTPicoModule.Data;
23

3-
public class BlendShape
4+
public abstract class BlendShape
45
{
56
public enum Index
67
{

VRCFTPicoModule/Data/DataPacket.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace VRCFTPicoModule.Data
44
{
5-
public class DataPacket
5+
public abstract class DataPacket
66
{
77
[StructLayout(LayoutKind.Sequential, Pack = 1)]
88
public struct DataPackHeader

VRCFTPicoModule/Data/LegacyDataPacket.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace VRCFTPicoModule.Data
44
{
5-
public class LegacyDataPacket
5+
public abstract class LegacyDataPacket
66
{
77
[StructLayout(LayoutKind.Sequential, Pack = 1)]
88
public struct DataPackBody

VRCFTPicoModule/Utils/Updater.cs

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,66 @@
66
using VRCFaceTracking.Core.Library;
77
using VRCFaceTracking.Core.Params.Expressions;
88
using VRCFTPicoModule.Data;
9-
using VRCFTPicoModule.Utils;
109

11-
namespace VRCFTPicoModule
10+
namespace VRCFTPicoModule.Utils
1211
{
13-
public class Updater
12+
public class Updater()
1413
{
15-
private readonly UdpClient udpClient;
16-
private readonly ILogger logger;
17-
private int timeOut = 0;
18-
private float lastMouthLeft = 0f;
19-
private float lastMouthRight = 0f;
20-
private const float smoothingFactor = 0.5f;
21-
private bool isLegecy = false;
22-
public ModuleState moduleState;
23-
24-
public Updater(UdpClient udpClient, ILogger logger, bool isLegecy)
14+
private readonly UdpClient? _udpClient;
15+
private readonly ILogger? _logger;
16+
private readonly bool _isLegacy;
17+
private readonly (bool, bool) _trackingAvailable;
18+
19+
public Updater(UdpClient udpClient, ILogger logger, bool isLegacy, (bool, bool) trackingAvailable) : this()
2520
{
26-
this.udpClient = udpClient;
27-
this.logger = logger;
28-
this.isLegecy = isLegecy;
21+
_udpClient = udpClient;
22+
_logger = logger;
23+
_isLegacy = isLegacy;
24+
_trackingAvailable = trackingAvailable;
2925
}
30-
31-
public void Update()
26+
27+
private int _timeOut;
28+
private float _lastMouthLeft;
29+
private float _lastMouthRight;
30+
private const float SmoothingFactor = 0.5f;
31+
private ModuleState _moduleState;
32+
33+
public void Update(ModuleState state)
3234
{
33-
if (moduleState != ModuleState.Active) return;
35+
if (_udpClient == null)
36+
return;
37+
38+
if (_logger == null)
39+
return;
40+
41+
_udpClient.Client.ReceiveTimeout = 100;
42+
_moduleState = state;
43+
44+
if (_moduleState != ModuleState.Active) return;
3445

3546
try
3647
{
3748
var endPoint = new IPEndPoint(IPAddress.Any, 0);
38-
var data = udpClient.Receive(ref endPoint);
39-
var pShape = ParseData(data, isLegecy);
40-
41-
UpdateEye(pShape);
42-
UpdateExpression(pShape);
49+
var data = _udpClient.Receive(ref endPoint);
50+
var pShape = ParseData(data, _isLegacy);
51+
52+
if (_trackingAvailable.Item1)
53+
UpdateEye(pShape);
54+
55+
if (_trackingAvailable.Item2)
56+
UpdateExpression(pShape);
4357
}
4458
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
4559
{
46-
if (++timeOut > 600)
60+
if (++_timeOut > 600)
4761
{
48-
logger.LogWarning("Receive data timed out.");
49-
timeOut = 0;
62+
_logger.LogWarning("Receive data timed out.");
63+
_timeOut = 0;
5064
}
5165
}
5266
catch (Exception ex)
5367
{
54-
logger.LogWarning("Update failed with exception: {0}", ex);
68+
_logger.LogWarning("Update failed with exception: {0}", ex);
5569
}
5670
}
5771

@@ -62,11 +76,11 @@ private static float[] ParseData(byte[] data, bool isLegacy)
6276

6377
if (data.Length >= Marshal.SizeOf<DataPacket.DataPackHeader>() + Marshal.SizeOf<DataPacket.DataPackBody>())
6478
{
65-
var header = DataPacketHelpers.ByteArrayToStructure<DataPacket.DataPackHeader>(data, 0);
79+
var header = DataPacketHelpers.ByteArrayToStructure<DataPacket.DataPackHeader>(data);
6680
if (header.trackingType == 2)
6781
return DataPacketHelpers.ByteArrayToStructure<DataPacket.DataPackBody>(data, Marshal.SizeOf<DataPacket.DataPackHeader>()).blendShapeWeight;
6882
}
69-
return Array.Empty<float>();
83+
return [];
7084
}
7185

7286
private static void UpdateEye(float[] pShape)
@@ -84,10 +98,7 @@ private static void UpdateEye(float[] pShape)
8498
eye.Right.Gaze.x = pShape[(int)BlendShape.Index.EyeLookOut_R] - pShape[(int)BlendShape.Index.EyeLookIn_R];
8599
eye.Right.Gaze.y = pShape[(int)BlendShape.Index.EyeLookUp_R] - pShape[(int)BlendShape.Index.EyeLookDown_R];
86100
#endregion
87-
}
88-
89-
private void UpdateExpression(float[] pShape)
90-
{
101+
91102
#region Brow
92103
SetParam(pShape, BlendShape.Index.BrowInnerUp, UnifiedExpressions.BrowInnerUpLeft);
93104
SetParam(pShape, BlendShape.Index.BrowInnerUp, UnifiedExpressions.BrowInnerUpRight);
@@ -105,7 +116,10 @@ private void UpdateExpression(float[] pShape)
105116
SetParam(pShape, BlendShape.Index.EyeWide_L, UnifiedExpressions.EyeWideLeft);
106117
SetParam(pShape, BlendShape.Index.EyeWide_R, UnifiedExpressions.EyeWideRight);
107118
#endregion
119+
}
108120

121+
private void UpdateExpression(float[] pShape)
122+
{
109123
#region Jaw
110124
SetParam(pShape, BlendShape.Index.JawOpen, UnifiedExpressions.JawOpen);
111125
SetParam(pShape, BlendShape.Index.JawLeft, UnifiedExpressions.JawLeft);
@@ -118,11 +132,11 @@ private void UpdateExpression(float[] pShape)
118132
SetParam(pShape, BlendShape.Index.CheekSquint_L, UnifiedExpressions.CheekSquintLeft);
119133
SetParam(pShape, BlendShape.Index.CheekSquint_R, UnifiedExpressions.CheekSquintRight);
120134

121-
float mouthLeft = SmoothValue(pShape[(int)BlendShape.Index.MouthLeft], ref lastMouthLeft);
122-
float mouthRight = SmoothValue(pShape[(int)BlendShape.Index.MouthRight], ref lastMouthRight);
135+
var mouthLeft = SmoothValue(pShape[(int)BlendShape.Index.MouthLeft], ref _lastMouthLeft);
136+
var mouthRight = SmoothValue(pShape[(int)BlendShape.Index.MouthRight], ref _lastMouthRight);
123137

124-
float cheekPuff = pShape[(int)BlendShape.Index.CheekPuff];
125-
float diffThreshold = 0.1f;
138+
var cheekPuff = pShape[(int)BlendShape.Index.CheekPuff];
139+
const float diffThreshold = 0.1f;
126140

127141
if (cheekPuff > 0.1f)
128142
{
@@ -195,13 +209,13 @@ private void UpdateExpression(float[] pShape)
195209
#endregion
196210

197211
#region Tongue
198-
SetParam(pShape, BlendShape.Index.TongueOut, UnifiedExpressions.TongueOut);
212+
SetParam(pShape[(int)BlendShape.Index.TongueOut] > 0f ? 1f : 0f, UnifiedExpressions.TongueOut);
199213
#endregion
200214
}
201215

202-
private float SmoothValue(float newValue, ref float lastValue)
216+
private static float SmoothValue(float newValue, ref float lastValue)
203217
{
204-
lastValue += (newValue - lastValue) * smoothingFactor;
218+
lastValue += (newValue - lastValue) * SmoothingFactor;
205219
return lastValue;
206220
}
207221

VRCFTPicoModule/VRCFTPicoModule.cs

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,92 @@
11
using Microsoft.Extensions.Logging;
22
using System.Net.Sockets;
33
using VRCFaceTracking;
4+
using VRCFTPicoModule.Utils;
45

56
namespace VRCFTPicoModule;
67

7-
public partial class VRCFTPicoModule : ExtTrackingModule
8+
public class VRCFTPicoModule : ExtTrackingModule
89
{
9-
private static readonly int[] Ports = { 29765, 29763 };
10+
private static readonly int[] Ports = [29765, 29763];
1011
private static readonly UdpClient[] Clients = Ports.Select(port => new UdpClient(port) { Client = { ReceiveTimeout = 100 } }).ToArray();
11-
private static UdpClient udpClient = new();
12-
private static int Port = 0;
13-
private Updater? updater;
12+
private static UdpClient _udpClient = new();
13+
private static int _port;
14+
private Updater? _updater;
15+
private (bool, bool) _trackingAvailable;
1416

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

1719
public override (bool eyeSuccess, bool expressionSuccess) Initialize(bool eyeAvailable, bool expressionAvailable)
1820
{
1921
Logger.LogInformation("Starting initialization");
22+
_trackingAvailable = (eyeAvailable, expressionAvailable);
2023
var initializationResult = InitializeAsync().GetAwaiter().GetResult();
21-
if (initializationResult.eyeSuccess && initializationResult.expressionSuccess)
22-
{
24+
25+
if (!initializationResult.Item1 || !initializationResult.Item2)
2326
UpdateModuleInfo();
24-
}
27+
2528
return initializationResult;
2629
}
2730

2831
private async Task<(bool eyeSuccess, bool expressionSuccess)> InitializeAsync()
2932
{
3033
Logger.LogDebug("Initializing UDP Clients on ports: {0}", string.Join(", ", Ports));
3134

32-
int portIndex = await ListenOnPorts();
35+
var portIndex = await ListenOnPorts();
3336
if (portIndex == -1) return (false, false);
3437

35-
Port = Ports[portIndex];
36-
udpClient = new UdpClient(Port);
37-
Logger.LogInformation("Using port: {0}", Port);
38+
_port = Ports[portIndex];
39+
_udpClient = new UdpClient(_port);
40+
Logger.LogInformation("Using port: {0}", _port);
41+
42+
if (!_trackingAvailable.Item1)
43+
Logger.LogInformation("Eye tracking is disabled");
44+
if (!_trackingAvailable.Item2)
45+
Logger.LogInformation("Expression tracking is disabled");
3846

39-
updater = new Updater(udpClient, Logger, Port == Ports[1]);
47+
_updater = new Updater(_udpClient, Logger, _port == Ports[1], _trackingAvailable);
4048

41-
return (true, true);
49+
return _trackingAvailable;
4250
}
4351

4452
private void UpdateModuleInfo()
4553
{
4654
ModuleInformation.Name = "PICO Connect";
4755
var stream = GetType().Assembly.GetManifestResourceStream("VRCFTPicoModule.Assets.pico.png");
48-
ModuleInformation.StaticImages = stream != null ? new List<Stream> { stream } : ModuleInformation.StaticImages;
56+
ModuleInformation.StaticImages = stream != null ? [stream] : ModuleInformation.StaticImages;
4957
}
5058

5159
private async Task<int> ListenOnPorts()
5260
{
5361
try
5462
{
5563
var tasks = Clients.Select(client => client.ReceiveAsync()).ToArray();
56-
var completedTask = await Task.WhenAny(tasks);
57-
58-
if (completedTask != null)
64+
65+
if (tasks.Length == 0)
5966
{
60-
foreach (var client in Clients) client.Dispose();
61-
return Array.IndexOf(tasks, completedTask);
67+
return -1;
6268
}
69+
70+
var completedTask = await Task.WhenAny(tasks);
71+
72+
foreach (var client in Clients) client.Dispose();
73+
74+
return Array.IndexOf(tasks, completedTask);
6375
}
6476
catch (Exception ex)
6577
{
6678
Logger.LogError("Initialization failed, exception: {0}", ex);
6779
}
80+
6881
return -1;
6982
}
7083

7184
public override void Update()
7285
{
73-
if (updater == null)
86+
if (_updater == null)
7487
return;
75-
updater.moduleState = Status;
76-
updater.Update();
88+
89+
_updater.Update(Status);
7790
}
7891

7992
public override void Teardown()
@@ -82,7 +95,7 @@ public override void Teardown()
8295
{
8396
client.Dispose();
8497
}
85-
udpClient.Dispose();
86-
updater = null;
98+
_udpClient.Dispose();
99+
_updater = null;
87100
}
88101
}

VRCFTPicoModule/VRCFTPicoModule.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFramework>net7.0</TargetFramework>
4-
<LangVersion>latest</LangVersion>
4+
<LangVersion>preview</LangVersion>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<AssemblyVersion>0.1.7</AssemblyVersion>

0 commit comments

Comments
 (0)