diff --git a/OSCCollar/OSCCollar.cs b/OSCCollar/OSCCollar.cs index 89e622b..cab3d4c 100644 --- a/OSCCollar/OSCCollar.cs +++ b/OSCCollar/OSCCollar.cs @@ -4,75 +4,118 @@ namespace VRC_Console; public class OSCCollar { + + public static void Main(string[] args) + { + OSCCollar collar = new OSCCollar(); + } + private OscServer Server { get; init; } private OscClient Client { get; init; } private float _collarAngle, _collarStretch; - private const float StretchDeadzone = 0.1f; - private const float TurnDeadZone = 5f / 180f; //5 degrees - private DateTime _lastMessageSent = DateTime.UnixEpoch; - private bool _sendingMessage = false; + private double _leftOrRight = 1; + private double _verticalMovement = 0; + private double _horizontalMovement = 0; + private bool _allowMoving = false; + private uint _nilSent = 0; + private DateTime _lastNilMessageSent = DateTime.UnixEpoch; + private DateTime _lastConsoleOutput = DateTime.UnixEpoch; + private static readonly TimeSpan ConsoleUpdateInterval = TimeSpan.FromMilliseconds(50); + private static readonly TimeSpan UpdateFieldsTimeout = TimeSpan.FromMilliseconds(1); + private static readonly TimeSpan NilMessageMaxTimeout = TimeSpan.FromMilliseconds(400); - public OSCCollar() + private OSCCollar() { this.Server = new OscServer(9001); this.Client = new OscClient("127.0.0.1", 9000); this._collarAngle = 0; this._collarStretch = 0; - Server.TryAddMethodPair("/avatar/parameters/Collar_Angle", CollarAngleHandle, HandleCollarChange); - Server.TryAddMethodPair("/avatar/parameters/Collar_Stretch", CollarStretchHandle, HandleCollarChange); + Server.TryAddMethod("/avatar/parameters/Collar_Angle", CollarAngleHandle); + Server.TryAddMethod("/avatar/parameters/CollarRight", CollarLeftRightHandle); + Server.TryAddMethod("/avatar/parameters/Collar_Allow_Moving", AllowMovingHandle); + Server.TryAddMethodPair("/avatar/parameters/Collar_Stretch", CollarStretchHandle, CalculateMovement); + this.Server.Start(); - Thread sendNullInputThread = new Thread(SendNullInput); - sendNullInputThread.Start(); + Thread runningThread = new Thread(RunningThread); + runningThread.Start(); + } + + private void PrintOutput(double verticalMovement, double horizontalMovement) + { + Console.Clear(); + Console.WriteLine($"OSC Collar - Status: {(_allowMoving ? "enabled" : "disabled")}"); + Console.WriteLine("==============================================================="); + Console.WriteLine($"Angle:...............{_collarAngle:0.000000}"); + Console.WriteLine($"Left/Right:..........{(_leftOrRight < 0 ? "left" : "right")}"); + Console.WriteLine($"Stretch:.............{_collarStretch:0.000000}"); + Console.WriteLine($"Vertical Movement:...{verticalMovement:0.000000}"); + Console.WriteLine($"Horizontal Movement:.{horizontalMovement:0.000000}"); + Console.WriteLine(""); + Console.WriteLine(""); + Console.WriteLine(""); + Console.WriteLine(""); + Console.WriteLine(""); + Console.WriteLine(""); + Console.WriteLine(""); + Console.WriteLine($"Sending /input nil {_nilSent}"); + } + + private void AllowMovingHandle(OscMessageValues messageValues) + { + this._allowMoving = messageValues.ReadBooleanElement(0); } private void CollarAngleHandle(OscMessageValues messageValues) { this._collarAngle = messageValues.ReadFloatElement(0); } - + + private void CollarLeftRightHandle(OscMessageValues messageValues) + { + this._leftOrRight = messageValues.ReadBooleanElement(0) ? 1 : -1; + } + private void CollarStretchHandle(OscMessageValues messageValues) { this._collarStretch = messageValues.ReadFloatElement(0); } - private void SendNullInput() + private const float PowerAdjust = 0.25f; + private void CalculateMovement() + { + float degrees = _collarAngle * 180; + double radians = degrees * Math.PI / 180; + _verticalMovement = Math.Cos(radians) * this._collarStretch * PowerAdjust; + _horizontalMovement = Math.Sin(radians) * this._collarStretch * PowerAdjust * _leftOrRight; + } + + private void RunningThread() { while (true) { - if (!_sendingMessage) + this.Server.Update(); + if (_lastNilMessageSent.Add(NilMessageMaxTimeout) < DateTime.Now) { this.Client.Send("/input/Vertical", 0f); - this.Client.Send("/input/LookHorizontal", 0f); - this._lastMessageSent = DateTime.Now; + this.Client.Send("/input/Horizontal", 0f); + this._lastNilMessageSent = DateTime.Now; + _nilSent++; } - Thread.Sleep(400); + + if (_allowMoving) + { + Thread.Sleep(1); + this.Client.Send("/input/Vertical", Convert.ToSingle(_verticalMovement)); + this.Client.Send("/input/Horizontal", Convert.ToSingle(_horizontalMovement)); + } + + if (_lastConsoleOutput.Add(ConsoleUpdateInterval) < DateTime.Now) + { + PrintOutput(_verticalMovement, _horizontalMovement); + _lastConsoleOutput = DateTime.Now; + } + + Thread.Sleep(UpdateFieldsTimeout); } } - - private static readonly TimeSpan MessageTimeoutSpan = TimeSpan.FromMilliseconds(10); - private void HandleCollarChange() - { - _sendingMessage = true; - TimeSpan messageTimeout = _lastMessageSent.Add(MessageTimeoutSpan).Subtract(DateTime.Now); - if(messageTimeout > TimeSpan.FromMilliseconds(0)) - Thread.Sleep(messageTimeout); - - if (this._collarStretch > StretchDeadzone) - this.Client.Send("/input/Vertical", this._collarStretch); - else - this.Client.Send("/input/Vertical", 0f); - - if (Math.Abs(this._collarAngle) > TurnDeadZone) - this.Client.Send("/input/LookHorizontal", CalculateTurnPower(this._collarAngle)); - else - this.Client.Send("/input/LookHorizontal", 0f); - _sendingMessage = false; - } - - private const float MaxAngle = 180f; - private const float MaxPower = 0.5f; - private static float CalculateTurnPower(float boneAngle) - { - return (boneAngle / MaxAngle) * MaxPower; - } } \ No newline at end of file