|
| 1 | +using HarmonyLib; |
| 2 | +using NeosModLoader; |
| 3 | +using FrooxEngine; |
| 4 | +using BaseX; |
| 5 | +using System; |
| 6 | + |
| 7 | +namespace NeosVarjoEye |
| 8 | +{ |
| 9 | + public class NeosVarjoEye : NeosMod |
| 10 | + { |
| 11 | + public static GazeData gazeData; |
| 12 | + |
| 13 | + public override string Name => "Neos-Varjo-Eye-Integration"; |
| 14 | + public override string Author => "dfgHiatus"; |
| 15 | + public override string Version => "1.0.1"; |
| 16 | + public override string Link => "https://github.com/dfgHiatus/Neos-Eye-Face-API/"; |
| 17 | + public override void OnEngineInit() |
| 18 | + { |
| 19 | + // Harmony.DEBUG = true; |
| 20 | + Harmony harmony = new Harmony("net.dfgHiatus.Neos-Eye-Face-API"); |
| 21 | + harmony.PatchAll(); |
| 22 | + } |
| 23 | + |
| 24 | + [HarmonyPatch(typeof(InputInterface), MethodType.Constructor)] |
| 25 | + [HarmonyPatch(new[] { typeof(Engine) })] |
| 26 | + public class InputInterfaceCtorPatch |
| 27 | + { |
| 28 | + public static void Postfix(InputInterface __instance) |
| 29 | + { |
| 30 | + try |
| 31 | + { |
| 32 | + if (!VarjoTrackingModule.tracker.ConnectToPipe()) |
| 33 | + { |
| 34 | + Warn("Varjo headset isn't detected"); |
| 35 | + } |
| 36 | + else |
| 37 | + { |
| 38 | + Debug("Gaze tracking enabled for Varjo Areo"); |
| 39 | + GenericInputDevice gen = new GenericInputDevice(); |
| 40 | + Debug("Module Name: " + gen.ToString()); |
| 41 | + __instance.RegisterInputDriver(gen); |
| 42 | + } |
| 43 | + |
| 44 | + } |
| 45 | + catch (Exception e) |
| 46 | + { |
| 47 | + Warn("Module failed to initiallize."); |
| 48 | + Warn(e.ToString()); |
| 49 | + } |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + [HarmonyPatch(typeof(Engine), "Shutdown")] |
| 54 | + public class ShutdownPatch |
| 55 | + { |
| 56 | + public static bool Prefix() |
| 57 | + { |
| 58 | + VarjoTrackingModule.tracker.Teardown(); |
| 59 | + return true; |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + public class GenericInputDevice : IInputDriver |
| 64 | + { |
| 65 | + public Eyes eyes; |
| 66 | + public int UpdateOrder => 100; |
| 67 | + |
| 68 | + public float Alpha = 1.5f; // Eye Swing Up/Down |
| 69 | + // public float Beta = 1f; // Eye Swing Left/Right |
| 70 | + |
| 71 | + // Idle pupil size in mm. Did you know the average human pupil size is between 4 and 6mm? |
| 72 | + // pupilSize is normalized from 0 to 1, so idle is 0.5 or ~3mm |
| 73 | + public float userPupilDiameter = 0.06f; |
| 74 | + |
| 75 | + public void CollectDeviceInfos(DataTreeList list) |
| 76 | + { |
| 77 | + DataTreeDictionary EyeDataTreeDictionary = new DataTreeDictionary(); |
| 78 | + EyeDataTreeDictionary.Add("Name", "Varjo Eye Tracking"); |
| 79 | + EyeDataTreeDictionary.Add("Type", "Eye Tracking"); |
| 80 | + EyeDataTreeDictionary.Add("Model", "Varjo Areo"); |
| 81 | + list.Add(EyeDataTreeDictionary); |
| 82 | + } |
| 83 | + |
| 84 | + public void RegisterInputs(InputInterface inputInterface) |
| 85 | + { |
| 86 | + eyes = new Eyes(inputInterface, "Varjo Eye Tracking"); |
| 87 | + } |
| 88 | + |
| 89 | + public void UpdateInputs(float deltaTime) |
| 90 | + { |
| 91 | + VarjoTrackingModule.tracker.Update(); |
| 92 | + |
| 93 | + // Current eye data takes on a data format similar to the Pimax, x and y values normalized from -1 to 1 |
| 94 | + eyes.IsEyeTrackingActive = Engine.Current.InputInterface.VR_Active; |
| 95 | + |
| 96 | + eyes.LeftEye.IsDeviceActive = Engine.Current.InputInterface.VR_Active; |
| 97 | + eyes.LeftEye.IsTracking = gazeData.leftStatus == GazeEyeStatus.Compensated || |
| 98 | + gazeData.leftStatus == GazeEyeStatus.Tracked; |
| 99 | + eyes.LeftEye.Direction = (float3)new double3(gazeData.leftEye.forward.x, |
| 100 | + gazeData.leftEye.forward.y, |
| 101 | + gazeData.leftEye.forward.z); |
| 102 | + eyes.LeftEye.RawPosition = (float3)new double3(gazeData.leftEye.origin.x, |
| 103 | + gazeData.leftEye.origin.y, |
| 104 | + gazeData.leftEye.origin.z); |
| 105 | + eyes.LeftEye.PupilDiameter = (float)(gazeData.leftPupilSize * userPupilDiameter); |
| 106 | + eyes.LeftEye.Openness = gazeData.leftStatus == GazeEyeStatus.Invalid ? 0f : 1f; |
| 107 | + eyes.LeftEye.Widen = (float)MathX.Clamp01(gazeData.leftEye.forward.y); |
| 108 | + eyes.LeftEye.Squeeze = 0f; |
| 109 | + eyes.LeftEye.Frown = 0f; |
| 110 | + |
| 111 | + eyes.RightEye.IsDeviceActive = Engine.Current.InputInterface.VR_Active; |
| 112 | + eyes.RightEye.IsTracking = gazeData.rightStatus == GazeEyeStatus.Compensated || |
| 113 | + gazeData.rightStatus == GazeEyeStatus.Tracked; |
| 114 | + eyes.RightEye.Direction = (float3)new double3(gazeData.rightEye.forward.x, |
| 115 | + gazeData.rightEye.forward.y, |
| 116 | + gazeData.rightEye.forward.z); |
| 117 | + eyes.RightEye.RawPosition = (float3)new double3(gazeData.rightEye.origin.x, |
| 118 | + gazeData.rightEye.origin.y, |
| 119 | + gazeData.rightEye.origin.z); |
| 120 | + eyes.RightEye.PupilDiameter = (float)(gazeData.rightPupilSize * userPupilDiameter); |
| 121 | + eyes.RightEye.Openness = gazeData.rightStatus == GazeEyeStatus.Invalid ? 0f : 1f; |
| 122 | + eyes.RightEye.Widen = (float)MathX.Clamp01(gazeData.rightEye.forward.y); |
| 123 | + eyes.RightEye.Squeeze = 0f; |
| 124 | + eyes.RightEye.Frown = 0f; |
| 125 | + |
| 126 | + eyes.CombinedEye.IsDeviceActive = Engine.Current.InputInterface.VR_Active; |
| 127 | + eyes.CombinedEye.IsTracking = gazeData.status == GazeStatus.Valid; |
| 128 | + eyes.CombinedEye.Direction = (float3)new double3(gazeData.gaze.forward.x, |
| 129 | + gazeData.gaze.forward.y, |
| 130 | + gazeData.gaze.forward.z); |
| 131 | + eyes.CombinedEye.RawPosition = (float3)new double3(gazeData.gaze.origin.x, |
| 132 | + gazeData.gaze.origin.y, |
| 133 | + gazeData.gaze.origin.z); |
| 134 | + eyes.CombinedEye.PupilDiameter = MathX.Average((float)(gazeData.leftPupilSize * userPupilDiameter), |
| 135 | + (float)(gazeData.rightPupilSize * userPupilDiameter)); |
| 136 | + eyes.CombinedEye.Openness = gazeData.leftStatus == GazeEyeStatus.Invalid || |
| 137 | + gazeData.rightStatus == GazeEyeStatus.Invalid ? 0f : 1f; |
| 138 | + eyes.CombinedEye.Widen = (float)MathX.Clamp01(gazeData.gaze.forward.y); |
| 139 | + eyes.CombinedEye.Squeeze = 0f; |
| 140 | + eyes.CombinedEye.Frown = 0f; |
| 141 | + |
| 142 | + // Convergence Distance is NOT Focus Distance, but we need to get it in somehow |
| 143 | + if (gazeData.stability > 0.75) { |
| 144 | + eyes.ConvergenceDistance = (float) gazeData.focusDistance; |
| 145 | + } |
| 146 | + |
| 147 | + // eyes.Timestamp = gazeData.captureTime / 1000000000; // Convert nanoseconds to seconds with this one nifty trick! |
| 148 | + eyes.Timestamp = gazeData.frameNumber; |
| 149 | + } |
| 150 | + } |
| 151 | + } |
| 152 | +} |
0 commit comments