Skip to content
This repository was archived by the owner on Aug 3, 2024. It is now read-only.

Commit

Permalink
notifications done; chaperone WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
galister committed Mar 5, 2023
1 parent 3af6056 commit 82b6192
Show file tree
Hide file tree
Showing 35 changed files with 5,968 additions and 137 deletions.
152 changes: 152 additions & 0 deletions Core/ChaperoneManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using Newtonsoft.Json;
using WlxOverlay.Numerics;
using WlxOverlay.Overlays;
using WlxOverlay.Types;

namespace WlxOverlay.Core;

public class ChaperoneManager
{
public static readonly ChaperoneManager Instance = new();

public bool WantVisible;
public float MaxAlpha = 1f;
public float FadeDistance = 5f;
public List<ChaperonePolygon> Polygons = new();

private readonly List<ChaperoneLine> _chaperoneLines = new();

public void Render()
{
var distance = HmdDistanceToChaperoneEdge();
var alpha = Mathf.Clamp(1 - (distance - FadeDistance) / FadeDistance, 0, 1) * MaxAlpha;

//foreach (var line in ChaperoneLines)
// line.Alpha = alpha;
}

public void PolygonsChanged()
{
var j = 0;
foreach (var polygon in Polygons)
{
var maxIdx = polygon.Points.Count - 1;
for (var i = 0; i < maxIdx; i++)
{
var start = polygon.Points[i];
var end = polygon.Points[i + 1];

ChaperoneLine line;
if (j >= _chaperoneLines.Count)
{
_chaperoneLines.Add(line = new ChaperoneLine { WantVisible = true });
OverlayManager.Instance.RegisterChild(line);
j++;
}
else
line = _chaperoneLines[j++];

line.SetPoints(start, end);
line.Color = polygon.Color;
}
}
for (; j < _chaperoneLines.Count; j++)
{
var lastIdx = _chaperoneLines.Count - 1;
var chap = _chaperoneLines[lastIdx];
_chaperoneLines.RemoveAt(lastIdx);
chap.Dispose();
}
}

public float HmdDistanceToChaperoneEdge()
{
return _chaperoneLines.Count < 1
? float.MaxValue
: _chaperoneLines.Min(x => x.DistanceTo(InputManager.HmdTransform.origin));
}

private Transform3D ReferenceTransform()
{
var pLeft = InputManager.PoseState["LeftHand"];
var pRight = InputManager.PoseState["RightHand"];

var baseTransform = Transform3D.Identity;
baseTransform.origin = (pLeft.origin + pRight.origin) / 2;
return baseTransform.LookingAt(pRight.origin, Vector3.Up);
}

public void LoadFromFile()
{
if (!Config.TryGetFile("chaperone.json", out var path))
return;

var transform = ReferenceTransform();

try
{
var conf = JsonConvert.DeserializeObject<ChaperoneConfig>(File.ReadAllText(path));
FadeDistance = conf!.FadeDistance;
MaxAlpha = conf!.MaxAlpha;
WantVisible = conf!.Visible;

Polygons.Clear();
if (conf.Polygons != null)
{
foreach (var poly in conf.Polygons)
{
poly.Apply(transform);
Polygons.Add(poly);
}
}
PolygonsChanged();
}
catch { /* */ }
}

public void SaveToFile()
{
var path = Path.Combine(Config.ConfigFolders.First(), "chaperone.json");

var transform = ReferenceTransform().Inverse();
var savedPolygons = new List<ChaperonePolygon>();
foreach (var poly in Polygons)
{
var newPoly = new ChaperonePolygon
{
Color = poly.Color,
Points = poly.Points.ToList()
};
newPoly.Apply(transform);
savedPolygons.Add(newPoly);
}

var json = JsonConvert.SerializeObject(new ChaperoneConfig
{
Visible = WantVisible,
Polygons = savedPolygons,
MaxAlpha = MaxAlpha,
FadeDistance = FadeDistance
}, Formatting.Indented);
File.WriteAllText(path, json);
}

private struct ChaperoneConfig
{
public bool Visible;
public float FadeDistance;
public float MaxAlpha;
public List<ChaperonePolygon>? Polygons;
}
}

public class ChaperonePolygon
{
public Vector3 Color;
public List<Vector3> Points = new();

public void Apply(Transform3D transform)
{
Points = Points.Select(x => transform * x).ToList();
}
}
1 change: 1 addition & 0 deletions Core/IInteractor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

16 changes: 8 additions & 8 deletions Core/InputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static void Initialize()
Console.WriteLine(OpenVR.GetStringForHmdError(error));
Environment.Exit(1);
}
Console.WriteLine("IVRInput: pass");
Console.WriteLine($"{OpenVR.IVRInput_Version}: pass");

Instance.LoadActionSets();
Instance.InitExportFileHandles();
Expand Down Expand Up @@ -295,14 +295,11 @@ public void UpdateDeviceStates()
{
DeviceStates.Clear();

if (_hmd == null)
_hmd = TrackedDevice.FromDeviceIdx(OpenVR.k_unTrackedDeviceIndex_Hmd);
if (_hmd != null)
{
_hmd = TrackedDevice.FromDeviceIdx(OpenVR.k_unTrackedDeviceIndex_Hmd);
if (_hmd != null)
{
_hmd.Role = TrackedDeviceRole.Hmd;
DeviceStates[_hmd.Serial] = _hmd;
}
_hmd.Role = TrackedDeviceRole.Hmd;
DeviceStates[_hmd.Serial] = _hmd;
}

var numDevs = OpenVR.System.GetSortedTrackedDeviceIndicesOfClass(ETrackedDeviceClass.Controller, _deviceIds, 0);
Expand Down Expand Up @@ -341,6 +338,7 @@ public void UpdateDeviceStates()

DeviceStatesSorted.Clear();
DeviceStatesSorted.AddRange(DeviceStates.Values.Where(dev => dev.Role != TrackedDeviceRole.None && dev.SoC >= 0).OrderBy(dev => dev.Role).ThenBy(dev => dev.Serial));
GC.Collect();
}

public void Dispose()
Expand Down Expand Up @@ -377,6 +375,7 @@ public class TrackedDevice
public string Serial = null!;
public uint Index;
public float SoC;
public float LastSoC;
public bool Charging;
public TrackedDeviceRole Role;

Expand All @@ -394,6 +393,7 @@ public class TrackedDevice
device.Serial = Sb.ToString();
Sb.Clear();

device.LastSoC = device.SoC;
device.SoC = OpenVR.System.GetFloatTrackedDeviceProperty(deviceIdx,
ETrackedDeviceProperty.Prop_DeviceBatteryPercentage_Float, ref lastErr);
if (lastErr == ETrackedPropertyError.TrackedProp_Success)
Expand Down
155 changes: 155 additions & 0 deletions Core/NotificationsManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
using Newtonsoft.Json;
using WlxOverlay.Overlays;
using WlxOverlay.Types;

namespace WlxOverlay.Core;

public class NotificationsManager : IDisposable
{
private static NotificationsManager _instance = null!;

private readonly IPEndPoint _listenEndpoint;
private readonly Socket _listenSocket;
private readonly byte[] _listenBuffer = new byte[1024*16];

private readonly CancellationTokenSource _cancel = new();
private Task? _listener;
private Task? _notifier;

private readonly object _lockObject = new();
private readonly Queue<XSOMessage> _messages = new(10);

private DateTime _nextToast = DateTime.MinValue;

public static void Initialize()
{
_instance = new NotificationsManager();
_instance.Start();
}

public static void Toast(string title, string content, float timeout = 10)
{
_instance._messages.Enqueue(new XSOMessage
{
timeout = timeout,
height = 110f,
messageType = 1,
content = content,
title = title,
opacity = 1
});
}

private NotificationsManager()
{
try
{
_listenEndpoint = IPEndPoint.Parse(Config.Instance.NotificationsEndpoint);
}
catch
{
Console.WriteLine("FATAL: Could not parse config.yaml entry: notifications_endpoint");
throw;
}
_listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
}

private void Start()
{
_listener = ListenerAsync(_cancel.Token);
_notifier = NotifierAsync(_cancel.Token);
}

private async Task ListenerAsync(CancellationToken _cancellationToken)
{
try
{
try
{
_listenSocket.Bind(_listenEndpoint);
Console.WriteLine($"Listening for notifications @ {_listenEndpoint.Address}:{_listenEndpoint.Port}");
}
catch
{
Console.WriteLine($"Could not listen for notifications @ {_listenEndpoint.Address}:{_listenEndpoint.Port}");
throw;
}
var remoteEp = new IPEndPoint(IPAddress.Any, 0);
while (_listenSocket.IsBound && !_cancellationToken.IsCancellationRequested)
{
try
{
var result = await _listenSocket.ReceiveFromAsync(_listenBuffer, SocketFlags.None, remoteEp);
var receivedBytes = new ArraySegment<byte>(_listenBuffer, 0, result.ReceivedBytes);
BytesReceived(receivedBytes);
}
catch (Exception x)
{
Console.WriteLine(x.ToString());
}
}
}
catch (Exception x)
{
Console.WriteLine(x);
}
}

private void BytesReceived(ArraySegment<byte> bytes)
{
var json = Encoding.UTF8.GetString(bytes);
var message = JsonConvert.DeserializeObject<XSOMessage>(json);

if ( message.messageType == 1)
lock (_lockObject)
_messages.Enqueue(message);
}

private async Task NotifierAsync(CancellationToken _cancellationToken)
{
while (!_cancellationToken.IsCancellationRequested)
{
await Task.Delay(100, _cancellationToken);

XSOMessage message;
lock (_lockObject)
if (!_messages.TryDequeue(out message))
continue;

var toast = new Toast(message.title, message.content, message.opacity, (uint)message.height, message.timeout);
OverlayManager.Instance.RegisterChild(toast);
_nextToast = DateTime.UtcNow.AddSeconds(2);
}
}

public void Dispose()
{
_cancel.Cancel();

_listener?.Dispose();
_notifier?.Dispose();
_cancel.Dispose();
_listenSocket.Dispose();
}
}

// ReSharper disable InconsistentNaming
// ReSharper disable UnusedAutoPropertyAccessor.Global
public struct XSOMessage
{
public int messageType { get; set; }
public int index { get; set; }
public float volume { get; set; }
public string audioPath { get; set; }
public float timeout { get; set; }
public string title { get; set; }
public string? content { get; set; }
public string? icon { get; set; }
public float height { get; set; }
public float opacity { get; set; }
public bool useBase64Icon { get; set; }
public string? sourceApp { get; set; }
}
Loading

0 comments on commit 82b6192

Please sign in to comment.