├── src ├── java │ └── VRE.Vridge.API │ │ ├── settings.gradle │ │ ├── VRE.Vridge.API.Client │ │ ├── src │ │ │ └── main │ │ │ │ ├── javalite │ │ │ │ └── com │ │ │ │ │ └── riftcat │ │ │ │ │ └── vridge │ │ │ │ │ └── api │ │ │ │ │ └── client │ │ │ │ │ └── java │ │ │ │ │ └── proto │ │ │ │ │ └── .gitignore │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── riftcat │ │ │ │ │ └── vridge │ │ │ │ │ └── api │ │ │ │ │ └── client │ │ │ │ │ └── java │ │ │ │ │ ├── proxy │ │ │ │ │ ├── VRidgeApiProxy.java │ │ │ │ │ ├── IBroadcastListener.java │ │ │ │ │ ├── ClientProxyBase.java │ │ │ │ │ ├── BroadcastProxy.java │ │ │ │ │ ├── ControllerProxy.java │ │ │ │ │ └── HeadTrackingProxy.java │ │ │ │ │ ├── utils │ │ │ │ │ ├── ILog.java │ │ │ │ │ ├── APILogger.java │ │ │ │ │ ├── SocketHelpers.java │ │ │ │ │ ├── SerializationUtils.java │ │ │ │ │ └── ButtonMask.java │ │ │ │ │ ├── control │ │ │ │ │ ├── BaseControlMessage.java │ │ │ │ │ ├── ControlRequestCode.java │ │ │ │ │ ├── responses │ │ │ │ │ │ ├── EndpointCreated.java │ │ │ │ │ │ ├── APIStatus.java │ │ │ │ │ │ ├── EndpointStatus.java │ │ │ │ │ │ └── ControlResponseHeader.java │ │ │ │ │ ├── requests │ │ │ │ │ │ ├── RequestEndpoint.java │ │ │ │ │ │ └── ControlRequestHeader.java │ │ │ │ │ └── ControlResponseCode.java │ │ │ │ │ ├── codes │ │ │ │ │ ├── TrackedDeviceStatus.java │ │ │ │ │ ├── ControllerStateRequestCodes.java │ │ │ │ │ ├── HeadTrackingResponseCodes.java │ │ │ │ │ └── HeadTrackingRequestCodes.java │ │ │ │ │ ├── Capabilities.java │ │ │ │ │ ├── EndpointNames.java │ │ │ │ │ ├── remotes │ │ │ │ │ ├── VridgeRemoteConnectionStatus.java │ │ │ │ │ ├── RemoteBase.java │ │ │ │ │ ├── beacons │ │ │ │ │ │ ├── VridgeServerBeacon.java │ │ │ │ │ │ └── VridgeServerBeaconList.java │ │ │ │ │ ├── DiscoveryClient.java │ │ │ │ │ ├── HeadRemote.java │ │ │ │ │ └── ControllerRemote.java │ │ │ │ │ └── APIClient.java │ │ │ │ └── proto │ │ │ │ └── VridgeApiProtoDefinition.proto │ │ └── build.gradle │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── .idea │ │ ├── encodings.xml │ │ ├── modules.xml │ │ ├── runConfigurations.xml │ │ ├── misc.xml │ │ └── codeStyles │ │ │ └── Project.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── .gitignore │ │ ├── gradlew.bat │ │ └── gradlew ├── csharp │ ├── VRE.Vridge.API.Client │ │ ├── Messages │ │ │ ├── OpenVR │ │ │ │ ├── readme.txt │ │ │ │ ├── VRControllerAxis_t.cs │ │ │ │ ├── VRController.cs │ │ │ │ ├── VRControllerState_t.cs │ │ │ │ └── EVRButtonId.cs │ │ │ ├── BasicTypes │ │ │ │ └── HandType.cs │ │ │ ├── v3 │ │ │ │ ├── Discovery │ │ │ │ │ ├── BeaconOrigin.cs │ │ │ │ │ └── Beacon.cs │ │ │ │ ├── TrackedDeviceStatus.cs │ │ │ │ ├── Controller │ │ │ │ │ ├── Responses │ │ │ │ │ │ └── ControllerStateResponse.cs │ │ │ │ │ ├── HeadRelation.cs │ │ │ │ │ ├── VRController.cs │ │ │ │ │ └── Requests │ │ │ │ │ │ └── ControllerStateRequest.cs │ │ │ │ ├── HeadTracking │ │ │ │ │ └── Responses │ │ │ │ │ │ ├── TrackedPose.cs │ │ │ │ │ │ └── HeadTrackingResponse.cs │ │ │ │ └── Broadcast │ │ │ │ │ └── HapticPulse.cs │ │ │ ├── Control │ │ │ │ ├── ControlRequestCode.cs │ │ │ │ ├── Responses │ │ │ │ │ ├── APIStatus.cs │ │ │ │ │ ├── EndpointCreated.cs │ │ │ │ │ ├── EndpointStatus.cs │ │ │ │ │ └── ControlResponseHeader.cs │ │ │ │ ├── Requests │ │ │ │ │ ├── RequestEndpoint.cs │ │ │ │ │ └── ControlRequestHeader.cs │ │ │ │ ├── BaseControlMessage.cs │ │ │ │ └── ControlResponseCode.cs │ │ │ └── KeepAlive.cs │ │ ├── Remotes │ │ │ ├── Capabilities.cs │ │ │ ├── VridgeRemoteConnectionStatus.cs │ │ │ ├── Beacons │ │ │ │ ├── VridgeServerBeaconList.cs │ │ │ │ └── VridgeServerBeacon.cs │ │ │ ├── RemoteBase.cs │ │ │ ├── DiscoveryClient.cs │ │ │ ├── HeadRemote.cs │ │ │ └── ControllerRemote.cs │ │ ├── EndpointNames.cs │ │ ├── Helpers │ │ │ ├── Logger.cs │ │ │ ├── MathHelpers.cs │ │ │ └── SerializationHelpers.cs │ │ ├── VRE.Vridge.API.Client.csproj │ │ └── Proxy │ │ │ ├── ClientProxyBasePB.cs │ │ │ ├── Broadcasts │ │ │ └── BroadcastProxy.cs │ │ │ ├── Controller │ │ │ └── ControllerProxy.cs │ │ │ ├── ClientProxyBase.cs │ │ │ ├── APIClient.cs │ │ │ └── HeadTracking │ │ │ └── HeadTrackingProxy.cs │ ├── examples │ │ └── VRE.Vridge.API.DesktopClient │ │ │ ├── icon.ico │ │ │ ├── icon.png │ │ │ ├── App.config │ │ │ ├── Properties │ │ │ ├── Settings.settings │ │ │ ├── Settings.Designer.cs │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ │ ├── App.xaml.cs │ │ │ ├── ViewModel │ │ │ ├── ControlTarget.cs │ │ │ ├── TrackingType.cs │ │ │ └── ControllerMode.cs │ │ │ ├── App.xaml │ │ │ ├── packages.config │ │ │ ├── Helpers.cs │ │ │ ├── Converters │ │ │ └── BoolToVisibilityConverter.cs │ │ │ ├── Extensions.cs │ │ │ ├── View │ │ │ ├── LabeledSlider.xaml │ │ │ ├── LabeledSlider.xaml.cs │ │ │ ├── ControlWindow.xaml.cs │ │ │ └── ControlWindow.xaml │ │ │ └── VRE.Vridge.API.DesktopTester.csproj │ └── VRE.Vridge.API.sln └── vridge-api.proto ├── .gitignore ├── LICENSE └── README.md /src/java/VRE.Vridge.API/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':VRE.Vridge.API.Client' 2 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/javalite/com/riftcat/vridge/api/client/java/proto/.gitignore: -------------------------------------------------------------------------------- 1 | *.java -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/OpenVR/readme.txt: -------------------------------------------------------------------------------- 1 | Classes in OpenVR folder are 1:1 copies of OpenVR structs. See OpenVR reference for details. -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiftCat/vridge-api/HEAD/src/csharp/examples/VRE.Vridge.API.DesktopClient/icon.ico -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiftCat/vridge-api/HEAD/src/csharp/examples/VRE.Vridge.API.DesktopClient/icon.png -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RiftCat/vridge-api/HEAD/src/java/VRE.Vridge.API/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/BasicTypes/HandType.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.BasicTypes 2 | { 3 | public enum HandType : byte 4 | { 5 | Left, 6 | Right 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/Discovery/BeaconOrigin.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.v3.Discovery 2 | { 3 | public enum BeaconOrigin 4 | { 5 | Server, 6 | Client 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/proxy/VRidgeApiProxy.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.proxy; 2 | 3 | public interface VRidgeApiProxy { 4 | void disconnect(); 5 | } 6 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/ILog.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.utils; 2 | 3 | public interface ILog { 4 | void debug(String s); 5 | void error(String s); 6 | } 7 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/ControlRequestCode.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.Control 2 | { 3 | public enum ControlRequestCode 4 | { 5 | RequestEndpoint = 1, 6 | RequestStatus = 2, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | NuGet Packages 2 | *.nupkg 3 | # The packages folder can be ignored because of Package Restore 4 | **/packages/* 5 | 6 | [Dd]ebug/ 7 | [Dd]ebugPublic/ 8 | [Rr]elease/ 9 | [Rr]eleases/ 10 | x64/ 11 | x86/ 12 | bld/ 13 | [Bb]in/ 14 | [Oo]bj/ 15 | [Ll]og/ 16 | 17 | .vs -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/BaseControlMessage.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control; 2 | 3 | public class BaseControlMessage { 4 | 5 | public int ProtocolVersion = 3; 6 | public int Code; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace VRE.Vridge.API.DesktopTester 4 | { 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/KeepAlive.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages 2 | { 3 | // Send this with one zero byte to API service (excluding control) to keep the connection from timing out 4 | public struct KeepAlive 5 | { 6 | public byte[] Zero; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jan 22 19:43:41 CET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/ControlRequestCode.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control; 2 | 3 | public class ControlRequestCode { 4 | 5 | public static int RequestEndpoint = 1; 6 | public static int RequestStatus = 2; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/responses/EndpointCreated.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control.responses; 2 | 3 | public class EndpointCreated extends ControlResponseHeader { 4 | public int TimeoutSec; 5 | public int Port; 6 | } 7 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/Capabilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace VRE.Vridge.API.Client.Remotes 6 | { 7 | [Flags] 8 | public enum Capabilities 9 | { 10 | HeadTracking = 1, 11 | Controllers = 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/ViewModel/ControlTarget.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.DesktopTester.ViewModel 2 | { 3 | public enum ControlTarget 4 | { 5 | Head = 0, 6 | Controller1 = 1, 7 | Controller2 = 2, 8 | Controller3 = 3, 9 | Controller4 = 4, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/proxy/IBroadcastListener.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.proxy; 2 | 3 | import com.riftcat.vridge.api.client.java.proto.HapticPulse; 4 | 5 | public interface IBroadcastListener{ 6 | void onHapticPulse(HapticPulse pulse); 7 | } 8 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/TrackedDeviceStatus.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.codes; 2 | 3 | public class TrackedDeviceStatus { 4 | public static final byte Active = 0; 5 | public static final byte TempUnavailable = 1; 6 | public static final byte Disabled = 2; 7 | } 8 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/Capabilities.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java; 2 | 3 | public enum Capabilities { 4 | HeadTracking(1), 5 | Controllers(2); 6 | 7 | private final int value; 8 | 9 | Capabilities(int value) { 10 | this.value = value; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/EndpointNames.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java; 2 | 3 | public class EndpointNames { 4 | public static final String HeadTracking = "HeadTracking"; 5 | public static final String Controller = "Controller"; 6 | public static final String Broadcast = "Broadcast"; 7 | } 8 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/remotes/VridgeRemoteConnectionStatus.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.remotes; 2 | 3 | public enum VridgeRemoteConnectionStatus { 4 | 5 | /** Connected or will be auto-connected on first call */ 6 | Okay, 7 | Unreachable, 8 | InUse, 9 | UnexpectedError 10 | } 11 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/ViewModel/TrackingType.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.DesktopTester.ViewModel 2 | { 3 | /// 4 | /// Tracking mode which decides how to use user values. 5 | /// 6 | public enum TrackingType 7 | { 8 | Position, 9 | PositionAndRotation, 10 | SyncOffset, 11 | AsyncOffset 12 | } 13 | } -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/ControllerStateRequestCodes.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.codes; 2 | 3 | public class ControllerStateRequestCodes { 4 | public static int Disconnect = 255; 5 | public static int SendFullState = 1; 6 | public static int RecenterHead = 2; 7 | 8 | public static int Origin_Zero = 0; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/EndpointNames.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client 2 | { 3 | /// 4 | /// Endpoint names for API services. 5 | /// 6 | public static class EndpointNames 7 | { 8 | public static string HeadTracking = "HeadTracking"; 9 | public static string Controller = "Controller"; 10 | public static string Broadcast = "Broadcast"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Helpers/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text; 5 | using NetMQ; 6 | 7 | namespace VRE.Vridge.API.Client.Helpers 8 | { 9 | internal static class Logger 10 | { 11 | internal static void Debug(string msg) 12 | { 13 | 14 | } 15 | 16 | internal static void Error(string msg) 17 | { 18 | 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/VridgeRemoteConnectionStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace VRE.Vridge.API.Client.Remotes 6 | { 7 | public enum VridgeRemoteConnectionStatus 8 | { 9 | /// 10 | /// Connected or will be auto-connected on method call. 11 | /// 12 | Okay, 13 | 14 | Unreachable, 15 | InUse, 16 | UnexpectedError 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/HeadTrackingResponseCodes.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.codes; 2 | 3 | public class HeadTrackingResponseCodes { 4 | 5 | public static int AcceptedYourData = 0; 6 | 7 | public static int SendingCurrentTrackedPose = 2; 8 | 9 | public static int PhoneDataTimeout = 253; 10 | public static int BadRequest = 254; 11 | public static int Disconnecting = 255; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/Responses/APIStatus.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace VRE.Vridge.API.Client.Messages.Control.Responses 4 | { 5 | /// 6 | /// Contains list of available (or not) API services. 7 | /// 8 | public class APIStatus : ControlResponseHeader 9 | { 10 | public List Endpoints; 11 | 12 | public APIStatus(ControlResponseCode code) : base(code) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/Requests/RequestEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.Control.Requests 2 | { 3 | public class RequestEndpoint : ControlRequestHeader 4 | { 5 | public string RequestedEndpointName; 6 | 7 | public RequestEndpoint(string name, string requestingAppName) 8 | : base(requestingAppName, ControlRequestCode.RequestEndpoint) 9 | { 10 | RequestedEndpointName = name; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/TrackedDeviceStatus.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.v3 2 | { 3 | public enum TrackedDeviceStatus 4 | { 5 | /// 6 | /// Currently tracked. 7 | /// 8 | Active = 0, 9 | 10 | /// 11 | /// Not in tracking range or tracking unavailable due to other reasons. 12 | /// 13 | TempUnavailable = 1, 14 | 15 | /// 16 | /// Permamently gone. 17 | /// 18 | Disabled = 2 19 | } 20 | } -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/BaseControlMessage.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | 4 | namespace VRE.Vridge.API.Client.Messages.Control 5 | { 6 | public class BaseControlMessage 7 | { 8 | public int ProtocolVersion; 9 | public int Code; 10 | 11 | public BaseControlMessage(int versionCode, int code) 12 | { 13 | ProtocolVersion = versionCode; 14 | Code = code; 15 | } 16 | 17 | public BaseControlMessage() 18 | { 19 | // Deserialization only 20 | } 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/OpenVR/VRControllerAxis_t.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using ProtoBuf; 3 | 4 | namespace VRE.Vridge.API.Client.Messages.OpenVR 5 | { 6 | [StructLayout(LayoutKind.Sequential, Size = 8)] 7 | [ProtoContract] 8 | public struct VRControllerAxis_t 9 | { 10 | [ProtoMember(1)] 11 | public float x; 12 | 13 | [ProtoMember(2)] 14 | public float y; 15 | 16 | public VRControllerAxis_t(float x, float y) 17 | { 18 | this.x = x; 19 | this.y = y; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/requests/RequestEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control.requests; 2 | 3 | import com.riftcat.vridge.api.client.java.control.ControlRequestCode; 4 | 5 | public class RequestEndpoint extends ControlRequestHeader{ 6 | 7 | public String RequestedEndpointName; 8 | 9 | public RequestEndpoint(String name, String appName){ 10 | super(appName); 11 | Code = ControlRequestCode.RequestEndpoint; 12 | RequestedEndpointName = name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/Requests/ControlRequestHeader.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.Control.Requests 2 | { 3 | public class ControlRequestHeader : BaseControlMessage 4 | { 5 | public string RequestingAppName; 6 | 7 | public ControlRequestHeader(string requestingAppName, ControlRequestCode code) : base(3, (int)code) 8 | { 9 | RequestingAppName = requestingAppName; 10 | } 11 | 12 | public ControlRequestHeader() : base() 13 | { 14 | // Deserialization only 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/Discovery/Beacon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using ProtoBuf; 5 | 6 | namespace VRE.Vridge.API.Client.Messages.v3.Discovery 7 | { 8 | [ProtoContract] 9 | public class Beacon 10 | { 11 | [ProtoMember(1)] 12 | public BeaconOrigin Role; 13 | 14 | [ProtoMember(2)] 15 | public string MachineName { get; set; } 16 | 17 | [ProtoMember(3)] 18 | public string UserName { get; set; } 19 | 20 | [ProtoMember(4)] 21 | public string HumanReadableVersion { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/requests/ControlRequestHeader.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control.requests; 2 | 3 | import com.riftcat.vridge.api.client.java.control.BaseControlMessage; 4 | import com.riftcat.vridge.api.client.java.control.ControlRequestCode; 5 | 6 | public class ControlRequestHeader extends BaseControlMessage { 7 | public String RequestingAppName; 8 | 9 | public ControlRequestHeader(String requestingAppName){ 10 | RequestingAppName = requestingAppName; 11 | } 12 | 13 | public ControlRequestHeader(int reqCode){ 14 | Code = reqCode; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/VRE.Vridge.API.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net47 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | mavenCentral() 7 | google() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.0' 11 | classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | google() 21 | maven { 22 | url 'https://maven.google.com/' 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/remotes/RemoteBase.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.remotes; 2 | 3 | import com.riftcat.vridge.api.client.java.proxy.ClientProxyBase; 4 | import com.riftcat.vridge.api.client.java.proxy.VRidgeApiProxy; 5 | 6 | class RemoteBase { 7 | 8 | private boolean isDisposed; 9 | private VRidgeApiProxy proxy; 10 | 11 | protected RemoteBase(VRidgeApiProxy proxy){ 12 | this.proxy = proxy; 13 | } 14 | 15 | public boolean isDisposed() { 16 | return isDisposed; 17 | } 18 | 19 | public void dispose(){ 20 | isDisposed = true; 21 | if(proxy != null){ 22 | proxy.disconnect(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/APILogger.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.utils; 2 | 3 | import java.util.LinkedList; 4 | 5 | public class APILogger { 6 | 7 | private static LinkedList loggers = new LinkedList(); 8 | 9 | public static void AddLogListener(ILog logger){ 10 | loggers.add(logger); 11 | } 12 | 13 | public static void debug(String s){ 14 | for (ILog log : loggers) { 15 | log.debug(s); 16 | } 17 | } 18 | 19 | public static void zmq(String s) { 20 | //debug("ZMQ: " + s); 21 | } 22 | 23 | public static void error(String s) { 24 | for (ILog log : loggers) { 25 | log.error(s); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/SocketHelpers.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.utils; 2 | 3 | import com.google.gson.Gson; 4 | 5 | import org.zeromq.ZMQ; 6 | 7 | public class SocketHelpers { 8 | 9 | public static Gson Serializer; 10 | 11 | static{ 12 | Serializer = new Gson(); 13 | } 14 | 15 | public static boolean SendAsJson(ZMQ.Socket socket, Object obj){ 16 | 17 | String json = Serializer.toJson(obj); 18 | return socket.send(json); 19 | } 20 | 21 | public static T ReceiveByJson(ZMQ.Socket socket, Class type){ 22 | String json = socket.recvStr(); 23 | Object obj = Serializer.fromJson(json, type); 24 | 25 | return (T) obj; 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Media.Media3D; 3 | 4 | namespace VRE.Vridge.API.DesktopTester 5 | { 6 | public static class Helpers 7 | { 8 | public static Quaternion QuaternionFromYawPitchRoll(float yaw, float pitch, float roll) 9 | { 10 | double angleSquared = pitch * pitch + yaw * yaw + roll * roll; 11 | double s = 0; 12 | double c = 1; 13 | if (angleSquared > 0) 14 | { 15 | double angle = Math.Sqrt(angleSquared); 16 | s = Math.Sin(angle * 0.5f) / angle; 17 | c = Math.Cos(angle * 0.5f); 18 | } 19 | 20 | return new Quaternion(s * pitch, s * yaw, s * roll, c); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/Responses/EndpointCreated.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using VRE.Vridge.API.Client.Proxy; 3 | 4 | namespace VRE.Vridge.API.Client.Messages.Control.Responses 5 | { 6 | public class EndpointCreated : ControlResponseHeader 7 | { 8 | public int Port { get; set; } 9 | 10 | /// 11 | /// Timeout after which the API server will disconnect you after not sending data for given amount of seconds. 12 | /// Use keep-alive (byte[1] {0}) packets if you want to stay silent but connected. 13 | /// See for example impl. 14 | /// 15 | public int TimeoutSec { get; set; } 16 | 17 | public EndpointCreated(ControlResponseCode code) : base(code) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/Controller/Responses/ControllerStateResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using ProtoBuf; 3 | 4 | namespace VRE.Vridge.API.Client.Messages.v3.Controller.Responses 5 | { 6 | [ProtoContract] 7 | public struct ControllerStateResponse 8 | { 9 | [ProtoMember(1)] 10 | public int Version; 11 | 12 | [ProtoMember(2)] 13 | public byte ReplyCode; 14 | 15 | public enum Response 16 | { 17 | /// 18 | /// In response to disconnect request. 19 | /// 20 | Disconnecting = 255, 21 | 22 | /// 23 | /// When request was not understood 24 | /// 25 | BadRequest = 254, 26 | 27 | AcceptedYourData = 0 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Converters/BoolToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace VRE.Vridge.API.DesktopTester.Converters 7 | { 8 | /// 9 | /// Converts between bool and WPF control visibility. 10 | /// 11 | public class BoolToVisibilityConverter : IValueConverter 12 | { 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | return (value is bool && (bool)value) ? Visibility.Visible : Visibility.Collapsed; 16 | } 17 | 18 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | return value is Visibility && (Visibility)value == Visibility.Visible; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/OpenVR/VRController.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace VRE.Vridge.API.Client.Messages.OpenVR 4 | { 5 | [StructLayout(LayoutKind.Sequential, Size = 196)] 6 | public struct VRController 7 | { 8 | public int ControllerId; 9 | 10 | public int Status; 11 | 12 | public double Timestamp; 13 | 14 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 15 | public float[] OrientationMatrix; 16 | 17 | public VRControllerState_t ButtonState; 18 | 19 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 20 | public double[] Acceleration; 21 | 22 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 23 | public double[] Velocity; 24 | 25 | //0-undefined, 1-left, 2-right 26 | public int Role; 27 | 28 | public double PoseTimeOffset; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Proxy/ClientProxyBasePB.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Timers; 4 | using NetMQ; 5 | using NetMQ.Sockets; 6 | using VRE.Vridge.API.Client.Helpers; 7 | 8 | namespace VRE.Vridge.API.Client.Proxy 9 | { 10 | /// 11 | /// Updated to use ProtoBufs instead of default C# marshalling. 12 | /// 13 | public class ClientProxyBasePB : ClientProxyBase 14 | { 15 | public ClientProxyBasePB(string endpointAddress, bool keepAlive = false) : base(endpointAddress, keepAlive) { } 16 | 17 | protected sealed override byte[] Serialize(object o) 18 | { 19 | return SerializationHelpers.ProtoSerialize(o); 20 | } 21 | 22 | protected sealed override T Deserialize(byte[] array) 23 | { 24 | return SerializationHelpers.ProtoDeserialize(array); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/Controller/HeadRelation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace VRE.Vridge.API.Client.Messages.v3.Controller 6 | { 7 | public enum HeadRelation : byte 8 | { 9 | /// 10 | /// Pose is unrelated to current head pose. 11 | /// 12 | Unrelated = 0, 13 | 14 | /// 15 | /// Pose already is in head space. 16 | /// Will be auto-adjusted when head is recentered. 17 | /// 18 | IsInHeadSpace = 1, 19 | 20 | /// 21 | /// Pose is unrelated but is to be remapped in a way that assumes that pose forward 22 | /// should always be aligned to head's forward. Effectively the given pose is relative angle from current's head forward. 23 | /// 24 | SticksToHead = 2 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/responses/APIStatus.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control.responses; 2 | 3 | import com.riftcat.vridge.api.client.java.EndpointNames; 4 | 5 | import java.util.List; 6 | 7 | public class APIStatus { 8 | 9 | public List Endpoints; 10 | 11 | @Override 12 | public String toString() { 13 | 14 | String strStatus = ""; 15 | 16 | for(EndpointStatus status : Endpoints){ 17 | strStatus += status.Name + "(" + status.codeString() + ") | "; 18 | } 19 | 20 | return strStatus; 21 | } 22 | 23 | public EndpointStatus findEndpoint(String name){ 24 | for (EndpointStatus endpoint : Endpoints) { 25 | if(endpoint.Name.equals(name)){ 26 | return endpoint; 27 | } 28 | } 29 | 30 | return null; } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/codes/HeadTrackingRequestCodes.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.codes; 2 | 3 | public class HeadTrackingRequestCodes { 4 | 5 | public static int Disconnect = 255; 6 | 7 | public static int ChangeState = 254; 8 | public static int Recenter = 50; 9 | 10 | public static int SendPoseMatrixRotationOnly = 0; 11 | public static int SendPoseMatrixFull = 6; 12 | public static int SendRotationMatrix = 1; 13 | public static int SendRadRotationAndPosition = 3; 14 | public static int SendQuatRotationAndPosition = 4; 15 | public static int SendPositionOnly = 5; 16 | 17 | public static int RequestSyncOffset = 100; 18 | public static int RequestReadOnlyPose = 199; 19 | public static int RequestReadOnlyPhonePose = 200; 20 | 21 | public static int SetYawOffset = 201; 22 | public static int ResetYawOffset = 21; 23 | } 24 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/responses/EndpointStatus.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control.responses; 2 | 3 | import com.riftcat.vridge.api.client.java.control.ControlResponseCode; 4 | 5 | public class EndpointStatus extends ControlResponseHeader { 6 | 7 | public String Name; 8 | public String InUseBy; 9 | 10 | public EndpointStatus(String endpointName){ 11 | this.Code = ControlResponseCode.OK; 12 | this.Name = endpointName; 13 | } 14 | 15 | public String codeString(){ 16 | if(Code == ControlResponseCode.NotAvailable){ 17 | return "n/a"; 18 | } 19 | if(Code == ControlResponseCode.InUse){ 20 | return "In use by " + InUseBy; 21 | } 22 | if(Code == ControlResponseCode.OK){ 23 | return "Ready"; 24 | } 25 | 26 | return "unknown"; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/Responses/EndpointStatus.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace VRE.Vridge.API.Client.Messages.Control.Responses 4 | { 5 | public class EndpointStatus : ControlResponseHeader 6 | { 7 | [JsonProperty("Name")] 8 | public string Name; 9 | 10 | [JsonProperty("InUseBy")] 11 | public string InUseBy; 12 | 13 | public EndpointStatus(string endpointName) : base(ControlResponseCode.OK) 14 | { 15 | this.Name = endpointName; 16 | } 17 | 18 | public override string ToString() 19 | { 20 | if (Code == (int) ControlResponseCode.InUse) 21 | { 22 | return Name + ": " + (ControlResponseCode)Code + " by " + InUseBy; 23 | } 24 | else 25 | { 26 | return Name + ": " + (ControlResponseCode)Code; 27 | } 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/ControlResponseCode.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.Control 2 | { 3 | public enum ControlResponseCode 4 | { 5 | /// 6 | /// API awaits connection at given endpoint. 7 | /// 8 | OK = 0, 9 | 10 | /// 11 | /// API is not available because of undefined reason. 12 | /// 13 | NotAvailable = 1, 14 | 15 | /// 16 | /// API is in use by another client 17 | /// 18 | InUse = 2, 19 | 20 | /// 21 | /// Client is trying to use something that requires API client to be updated to more recent version 22 | /// 23 | ClientOutdated = 3, 24 | 25 | /// 26 | /// VRidge needs to be updated or client is not following protocol 27 | /// 28 | ServerOutdated = 4, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/Control/Responses/ControlResponseHeader.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.Control.Responses 2 | { 3 | public class ControlResponseHeader : BaseControlMessage 4 | { 5 | // Predefined responses 6 | public static ControlResponseHeader ResponseInUse = 7 | new ControlResponseHeader(ControlResponseCode.InUse); 8 | 9 | public static ControlResponseHeader ResponseClientOutdated = 10 | new ControlResponseHeader(ControlResponseCode.ClientOutdated); 11 | 12 | public static ControlResponseHeader ResponseNotAvailable = 13 | new ControlResponseHeader(ControlResponseCode.NotAvailable); 14 | 15 | public static ControlResponseHeader ResponseServerOutdated = 16 | new ControlResponseHeader(ControlResponseCode.ServerOutdated); 17 | 18 | public ControlResponseHeader(ControlResponseCode code) : base(3, (int) code) 19 | { 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/ControlResponseCode.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control; 2 | 3 | public class ControlResponseCode { 4 | 5 | /// 6 | /// API awaits connection at given endpoint. 7 | /// 8 | public static int OK = 0; 9 | 10 | /// 11 | /// API is not available because of undefined reason. 12 | /// 13 | public static int NotAvailable = 1; 14 | 15 | /// 16 | /// API is in use by another client 17 | /// 18 | public static int InUse = 2; 19 | 20 | /// 21 | /// Client is trying to use something that requires API client to be updated to more recent version 22 | /// 23 | public static int ClientOutdated = 3; 24 | 25 | /// 26 | /// VRidge needs to be updated or client is not following protocol 27 | /// 28 | public static int ServerOutdated = 4; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/OpenVR/VRControllerState_t.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using ProtoBuf; 3 | 4 | namespace VRE.Vridge.API.Client.Messages.OpenVR 5 | { 6 | /// 7 | /// See VRControllerState_t in OpenVR docs. 8 | /// 9 | [StructLayout(LayoutKind.Sequential, Size = 60)] 10 | [ProtoContract] 11 | public struct VRControllerState_t 12 | { 13 | [ProtoMember(1)] 14 | public uint unPacketNum; 15 | 16 | [ProtoMember(2)] 17 | public ulong ulButtonPressed; 18 | 19 | [ProtoMember(3)] 20 | public ulong ulButtonTouched; 21 | 22 | [ProtoMember(4)] 23 | public VRControllerAxis_t rAxis0; 24 | 25 | [ProtoMember(5)] 26 | public VRControllerAxis_t rAxis1; 27 | 28 | [ProtoMember(7)] 29 | public VRControllerAxis_t rAxis2; 30 | 31 | [ProtoMember(8)] 32 | public VRControllerAxis_t rAxis3; 33 | 34 | [ProtoMember(9)] 35 | public VRControllerAxis_t rAxis4; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 RiftCat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/control/responses/ControlResponseHeader.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.control.responses; 2 | 3 | import com.riftcat.vridge.api.client.java.control.BaseControlMessage; 4 | import com.riftcat.vridge.api.client.java.control.ControlResponseCode; 5 | 6 | public class ControlResponseHeader extends BaseControlMessage { 7 | 8 | // Predefined responses 9 | public static ControlResponseHeader ResponseInUse; 10 | public static ControlResponseHeader ResponseClientOutdated; 11 | public static ControlResponseHeader ResponseNotAvailable; 12 | 13 | static{ 14 | // Predefined responses 15 | ResponseNotAvailable = new ControlResponseHeader(); 16 | ResponseNotAvailable.Code = ControlResponseCode.NotAvailable; 17 | 18 | ResponseClientOutdated = new ControlResponseHeader(); 19 | ResponseClientOutdated.Code = ControlResponseCode.ClientOutdated; 20 | 21 | ResponseInUse = new ControlResponseHeader(); 22 | ResponseInUse.Code = ControlResponseCode.InUse; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/SerializationUtils.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.utils; 2 | 3 | import com.google.protobuf.ByteString; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | 8 | public class SerializationUtils { 9 | 10 | public static ByteString byteStringFromFloats(float...args){ 11 | return ByteString.copyFrom(byteArrayFromFloats(args)); 12 | } 13 | 14 | public static byte[] byteArrayFromFloats(float... args){ 15 | ByteBuffer data = ByteBuffer.allocate(args.length * 4); 16 | data.order(ByteOrder.LITTLE_ENDIAN); 17 | for (float arg : args) { 18 | data.putFloat(arg); 19 | } 20 | 21 | return data.array(); 22 | } 23 | 24 | public static ByteString byteStringFromFloatArray(float[] array){ 25 | ByteBuffer data = ByteBuffer.allocate(array.length * 4); 26 | data.order(ByteOrder.LITTLE_ENDIAN); 27 | for (float arg : array) { 28 | data.putFloat(arg); 29 | } 30 | 31 | return ByteString.copyFrom(data.array()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace VRE.Vridge.API.DesktopTester.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.6.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/Beacons/VridgeServerBeaconList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using VRE.Vridge.API.Client.Messages.v3.Discovery; 5 | 6 | namespace VRE.Vridge.API.Client.Remotes.Beacons 7 | { 8 | class VridgeServerBeaconList 9 | { 10 | private readonly Dictionary timedList = new Dictionary(); 11 | 12 | public void Add(Beacon beacon, string endpoint) 13 | { 14 | if (timedList.ContainsKey(endpoint)) 15 | { 16 | timedList.Remove(endpoint); 17 | } 18 | 19 | timedList.Add(endpoint, new VridgeServerBeacon(beacon, endpoint)); 20 | } 21 | 22 | public List FreshServers 23 | { 24 | get 25 | { 26 | var beacons = new List(); 27 | foreach (var beacon in timedList.Values) 28 | { 29 | if (beacon.IsFresh) 30 | { 31 | beacons.Add(beacon); 32 | } 33 | } 34 | 35 | return beacons; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/HeadTracking/Responses/TrackedPose.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using ProtoBuf; 5 | 6 | namespace VRE.Vridge.API.Client.Messages.v3.HeadTracking.Responses 7 | { 8 | [ProtoContract] 9 | public class TrackedPose 10 | { 11 | /// 12 | /// Current head orientation as 4 element XYZW quaternion. 13 | /// 14 | [ProtoMember(1)] 15 | public float[] HeadOrientation; 16 | 17 | /// 18 | /// Current head position as 3 element XYZ vector. 19 | /// 20 | [ProtoMember(2)] 21 | public float[] HeadPosition; 22 | 23 | /// 24 | /// Current offset applied to each head-related (Controller w/ HeadRelation.IsInHeadSpace or internal VRidge mobile tracking data) pose due to user recenter. 25 | /// In 99% cases, you can forget about it. 26 | /// 27 | [ProtoMember(3)] 28 | public float RecenterYawOffset; 29 | 30 | /// 31 | /// Current offset applied to each pose due to API SetYawOffset call. 32 | /// 33 | [ProtoMember(4)] 34 | public float ApiYawOffset; 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/remotes/beacons/VridgeServerBeacon.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.remotes.beacons; 2 | 3 | import com.riftcat.vridge.api.client.java.proto.Beacon; 4 | 5 | import java.net.InetAddress; 6 | 7 | public class VridgeServerBeacon { 8 | private static long timeoutMs = 5000; 9 | 10 | private Beacon beacon; 11 | private InetAddress endpoint; 12 | private long timestmapMs; 13 | 14 | public VridgeServerBeacon(Beacon beacon, InetAddress endpoint) { 15 | this.beacon = beacon; 16 | this.endpoint = endpoint; 17 | timestmapMs = System.currentTimeMillis(); 18 | } 19 | 20 | public Beacon getBeacon() { 21 | return beacon; 22 | } 23 | 24 | public InetAddress getEndpoint() { 25 | return endpoint; 26 | } 27 | 28 | public boolean isFresh(){ 29 | return timestmapMs + timeoutMs > System.currentTimeMillis(); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return beacon.getRole() + "|" + 35 | beacon.getHumanReadableVersion() + "|" + 36 | beacon.getMachineName() + "|" + 37 | beacon.getUserName() + "@" + 38 | endpoint.getHostAddress(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/remotes/beacons/VridgeServerBeaconList.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.remotes.beacons; 2 | 3 | import com.riftcat.vridge.api.client.java.proto.Beacon; 4 | 5 | import java.net.InetAddress; 6 | import java.util.ArrayList; 7 | import java.util.Dictionary; 8 | import java.util.HashMap; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | 12 | public class VridgeServerBeaconList { 13 | private HashMap timedList = new HashMap(); 14 | 15 | public synchronized void add(Beacon beacon, InetAddress endpoint){ 16 | if(timedList.containsKey(endpoint)){ 17 | timedList.remove(endpoint); 18 | } 19 | 20 | timedList.put(endpoint, new VridgeServerBeacon(beacon, endpoint)); 21 | } 22 | 23 | public synchronized List getFreshServers(){ 24 | LinkedList beacons = new LinkedList(); 25 | for (VridgeServerBeacon vridgeServerBeacon : timedList.values()) { 26 | if(vridgeServerBeacon.isFresh()){ 27 | beacons.add(vridgeServerBeacon); 28 | } 29 | } 30 | 31 | return beacons; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'com.google.protobuf' 3 | 4 | dependencies { 5 | compile fileTree(dir: 'libs', include: ['*.jar']) 6 | compile 'com.google.protobuf:protobuf-lite:3.0.0' 7 | compile group: 'org.zeromq', name: 'jeromq', version: '0.4.3' 8 | compile group: 'com.google.code.gson', name: 'gson', version: '1.7.2' 9 | 10 | } 11 | 12 | sourceCompatibility = "1.6" 13 | targetCompatibility = "1.6" 14 | 15 | sourceSets{ 16 | main{ 17 | proto{ 18 | 19 | } 20 | java{ 21 | main.java.srcDirs += 'src/main/javalite' 22 | } 23 | } 24 | } 25 | 26 | 27 | protobuf { 28 | protoc { 29 | // Download from repositories 30 | artifact = 'com.google.protobuf:protoc:3.0.0' 31 | } 32 | plugins { 33 | javalite { 34 | // The codegen for lite comes as a separate artifact 35 | artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' 36 | } 37 | } 38 | generateProtoTasks { 39 | all().each { task -> 40 | task.builtins { 41 | remove java 42 | } 43 | task.plugins { 44 | javalite { } 45 | } 46 | } 47 | } 48 | 49 | generatedFilesBaseDir = "$projectDir/src" 50 | } -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/Beacons/VridgeServerBeacon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text; 5 | using VRE.Vridge.API.Client.Messages.v3.Discovery; 6 | 7 | namespace VRE.Vridge.API.Client.Remotes.Beacons 8 | { 9 | public class VridgeServerBeacon 10 | { 11 | private const long TimeoutMs = 5000; 12 | private static readonly Stopwatch Timer = Stopwatch.StartNew(); 13 | 14 | private readonly Beacon beacon; 15 | private readonly string endpoint; 16 | private readonly long timestmapMs; 17 | 18 | public VridgeServerBeacon(Beacon beacon, string endpoint) 19 | { 20 | this.beacon = beacon; 21 | this.endpoint = endpoint; 22 | timestmapMs = Timer.ElapsedMilliseconds; 23 | } 24 | 25 | public Beacon Beacon => beacon; 26 | public string Endpoint => endpoint; 27 | public bool IsFresh => timestmapMs + TimeoutMs > Timer.ElapsedMilliseconds; 28 | 29 | public override string ToString() 30 | { 31 | return beacon.Role + "|" + 32 | beacon.HumanReadableVersion + "|" + 33 | beacon.MachineName + "|" + 34 | beacon.UserName + "@" + 35 | endpoint; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/Broadcast/HapticPulse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using ProtoBuf; 8 | 9 | namespace VRE.Vridge.API.Client.Messages.v3.Broadcast 10 | { 11 | [ProtoContract] 12 | public struct HapticPulse 13 | { 14 | /// 15 | /// Identifier defined by controller when it sends its requests to OpenVR. 16 | /// 17 | [ProtoMember(1)] 18 | public int ControllerId; 19 | 20 | 21 | /// 22 | /// Duration of pulse in microseconds, provided by VR game. 23 | /// 24 | [ProtoMember(2)] 25 | public uint LengthUs; 26 | 27 | /// 28 | /// High resolution (1us) timestamp in microseconds - based on QueryPerformanceCounter(). 29 | /// Can be optionally used to smooth out or join pulses together. 30 | /// 31 | /// 32 | /// Timestamp is created when the pulse was submitted to HMD driver. 33 | /// Wrap-arounds will happen so use it for interval measurement, not absolute timings. 34 | /// 35 | /// 36 | [ProtoMember(3)] 37 | public uint TimestampUs; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using System.Windows.Media.Media3D; 5 | 6 | namespace VRE.Vridge.API.DesktopTester 7 | { 8 | public static class Extensions 9 | { 10 | /// 11 | /// Converts 4x4 matrix into flat array with column-major layout. 12 | /// 13 | /// 14 | /// 15 | public static float[] FlattenAsColumnMajor(this Matrix3D matrix) 16 | { 17 | var array = new float[16]; 18 | array[0] = (float)matrix.M11; 19 | array[1] = (float)matrix.M21; 20 | array[2] = (float)matrix.M31; 21 | array[3] = (float)matrix.OffsetX; 22 | array[4] = (float)matrix.M12; 23 | array[5] = (float)matrix.M22; 24 | array[6] = (float)matrix.M32; 25 | array[7] = (float)matrix.OffsetY; 26 | array[8] = (float)matrix.M13; 27 | array[9] = (float)matrix.M23; 28 | array[10] = (float)matrix.M33; 29 | array[11] = (float)matrix.OffsetZ; 30 | array[12] = (float)matrix.M14; 31 | array[13] = (float)matrix.M24; 32 | array[14] = (float)matrix.M34; 33 | array[15] = (float)matrix.M44; 34 | 35 | return array; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # IntelliJ 37 | *.iml 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/gradle.xml 41 | .idea/assetWizardSettings.xml 42 | .idea/dictionaries 43 | .idea/libraries 44 | .idea/caches 45 | 46 | # Keystore files 47 | # Uncomment the following lines if you do not want to check your keystore files in. 48 | #*.jks 49 | #*.keystore 50 | 51 | # External native build folder generated in Android Studio 2.2 and later 52 | .externalNativeBuild 53 | 54 | # Google Services (e.g. APIs or Firebase) 55 | # google-services.json 56 | 57 | # Freeline 58 | freeline.py 59 | freeline/ 60 | freeline_project_description.json 61 | 62 | # fastlane 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | fastlane/readme.md 68 | 69 | # Version control 70 | vcs.xml 71 | 72 | # lint 73 | lint/intermediates/ 74 | lint/generated/ 75 | lint/outputs/ 76 | lint/tmp/ 77 | # lint/reports/ -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/ViewModel/ControllerMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace VRE.Vridge.API.DesktopTester.ViewModel 8 | { 9 | public enum ControllerMode 10 | { 11 | /// 12 | /// Pose is unrelated to current head pose. 13 | /// 14 | Unrelated = 0, 15 | 16 | /// 17 | /// Pose already is in head space. 18 | /// Will be auto-adjusted when head is recentered. 19 | /// 20 | IsInHeadSpace = 1, 21 | 22 | /// 23 | /// Pose is unrelated but is to be remapped in a way that assumes that pose forward 24 | /// should always be aligned to head's forward. Effectively the given pose is relative angle from current's head forward. 25 | /// HeadRelation = SticksToHead, Position = defined 26 | /// 27 | SticksToHead = 2, 28 | 29 | /// 30 | /// Equivalent to setting position to NULL from API perspective. 31 | /// It won't auto-follow head when controller data is not being sent. 32 | /// HeadRelation = Unrelated, Position = null 33 | /// 34 | ThreeDof = 3, 35 | 36 | /// 37 | /// Equivalent to setting position to NULL from API perspective. 38 | /// Also instructs API server to keep updating the controller pose whenever head pose changes. 39 | /// HeadRelation = SticksToHead, Position = null 40 | /// 41 | StickyThreeDof = 4 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2002 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VRE.Vridge.API.Client", "VRE.Vridge.API.Client\VRE.Vridge.API.Client.csproj", "{11FB8F50-AE40-42C2-AC82-7A8D84A7B76E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRE.Vridge.API.DesktopTester", "examples\VRE.Vridge.API.DesktopClient\VRE.Vridge.API.DesktopTester.csproj", "{97ECBE1C-03EF-461D-A389-312C729A7EA6}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {11FB8F50-AE40-42C2-AC82-7A8D84A7B76E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {11FB8F50-AE40-42C2-AC82-7A8D84A7B76E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {11FB8F50-AE40-42C2-AC82-7A8D84A7B76E}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {11FB8F50-AE40-42C2-AC82-7A8D84A7B76E}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {97ECBE1C-03EF-461D-A389-312C729A7EA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {97ECBE1C-03EF-461D-A389-312C729A7EA6}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {97ECBE1C-03EF-461D-A389-312C729A7EA6}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {97ECBE1C-03EF-461D-A389-312C729A7EA6}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4360A98A-0C26-4BA7-A3F6-5DF6C7A0E8A7} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/Controller/VRController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using ProtoBuf; 4 | using VRE.Vridge.API.Client.Messages.BasicTypes; 5 | using VRE.Vridge.API.Client.Messages.OpenVR; 6 | 7 | namespace VRE.Vridge.API.Client.Messages.v3.Controller 8 | { 9 | [ProtoContract] 10 | public struct VRController 11 | { 12 | [ProtoMember(1)] 13 | public int ControllerId; 14 | 15 | 16 | /// 17 | /// 18 | /// 19 | [ProtoMember(2)] 20 | public int Status; 21 | 22 | [ProtoMember(9)] 23 | public double Timestamp; 24 | 25 | [ProtoMember(3), Obsolete("Use Position + Orientation instead.")] 26 | public float[] OrientationMatrix; 27 | 28 | [ProtoMember(4)] 29 | public VRControllerState_t ButtonState; 30 | 31 | [ProtoMember(5)] 32 | public double[] Acceleration; 33 | 34 | [ProtoMember(6)] 35 | public double[] Velocity; 36 | 37 | [ProtoMember(7)] // TODO: GitHub API docs 38 | public HeadRelation HeadRelation; 39 | 40 | 41 | [ProtoMember(8)] // TODO: GitHub API docs 42 | public HandType SuggestedHand; 43 | 44 | [ProtoMember(11)] 45 | public string Name; 46 | 47 | /// 48 | /// XYZ vector. 49 | /// 50 | [ProtoMember(12)] 51 | public float[] Position; 52 | 53 | 54 | /// 55 | /// XYZW quaternion. 56 | /// 57 | [ProtoMember(13)] 58 | public float[] Orientation; 59 | 60 | // [ProtoMember(14)] 61 | 62 | public bool ShouldRemap3To6Dof => OrientationMatrix == null && Position == null; 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/View/LabeledSlider.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 34 | 35 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/HeadTracking/Responses/HeadTrackingResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using ProtoBuf; 4 | 5 | namespace VRE.Vridge.API.Client.Messages.v3.HeadTracking.Responses 6 | { 7 | [ProtoContract] 8 | public struct HeadTrackingResponse 9 | { 10 | [ProtoMember(1)] 11 | public int Version; 12 | 13 | [ProtoMember(2)] 14 | public byte ReplyCode; 15 | 16 | [ProtoMember(3)] 17 | public float[] Data; 18 | 19 | [ProtoMember(4)] 20 | public TrackedPose TrackedPose; 21 | 22 | public enum Response 23 | { 24 | /// 25 | /// In response to disconnect request. 26 | /// 27 | Disconnecting = 255, 28 | 29 | /// 30 | /// When request was not understood 31 | /// 32 | BadRequest = 254, 33 | 34 | /// 35 | /// No new data was received from the phone for 5 seconds, possibly phone lost connection 36 | /// 37 | PhoneDataTimeout = 253, 38 | 39 | 40 | AcceptedYourData = 0, 41 | 42 | /// 43 | /// Pose is contained in TrackedPose field. 44 | /// 45 | SendingCurrentTrackedPose = 2, 46 | 47 | /// 48 | /// Data contains float[6] (24 bytes total): 49 | /// pitch(+up), yaw (+to left), roll(+left), X, Y, Z 50 | /// X Y Z position is always zero because no phone-side positional 51 | /// tracking exists currently. This may change in the future. 52 | /// 53 | [Obsolete] 54 | SendingCurrentPhonePose = 1, 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/v3/Controller/Requests/ControllerStateRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using ProtoBuf; 3 | using VRE.Vridge.API.Client.Messages.v3.Controller.Responses; 4 | 5 | 6 | namespace VRE.Vridge.API.Client.Messages.v3.Controller.Requests 7 | { 8 | /// 9 | /// Request to a VRidge API server that contains current motion controller state. 10 | /// will be returned as a response. 11 | /// 12 | [ProtoContract] 13 | public struct ControllerStateRequest 14 | { 15 | public const int CurrentVersion = 3; 16 | 17 | [ProtoMember(1)] 18 | public int Version; 19 | 20 | /// 21 | /// Describes how API should handle the incoming data, see . 22 | /// 23 | [ProtoMember(2)] 24 | public byte TaskType; 25 | 26 | 27 | // Origin is removed, was never used, see VRController.HeadRelation for replacement 28 | // [ProtoMember(3)] public byte Origin; 29 | 30 | 31 | [ProtoMember(4)] 32 | public VRController ControllerState; 33 | } 34 | 35 | public enum ControllerTask 36 | { 37 | /// 38 | /// Packet closes your controller API connection and lets other clients use it. 39 | /// 40 | Disconnect = 255, 41 | 42 | /// 43 | /// Packet contains full controller state as defined in struct. 44 | /// It will be used immediately. 45 | /// 46 | SendFullState = 1, 47 | 48 | /// 49 | /// Recenter head tracking. Works the same as pressing recenter hotkey as configured in VRidge settings. 50 | /// 51 | RecenterHead = 2 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Messages/OpenVR/EVRButtonId.cs: -------------------------------------------------------------------------------- 1 | namespace VRE.Vridge.API.Client.Messages.OpenVR 2 | { 3 | public enum EVRButtonId 4 | { 5 | k_EButton_System = 0, // 0 6 | k_EButton_ApplicationMenu = 1, // 1 7 | k_EButton_Grip = 2, // 01 8 | k_EButton_DPad_Left = 3, // 11 9 | k_EButton_DPad_Up = 4, 10 | k_EButton_DPad_Right = 5, 11 | k_EButton_DPad_Down = 6, 12 | k_EButton_A = 7, 13 | 14 | k_EButton_ProximitySensor = 31, 15 | 16 | k_EButton_Axis0 = 32, 17 | k_EButton_Axis1 = 33, 18 | k_EButton_Axis2 = 34, 19 | k_EButton_Axis3 = 35, 20 | k_EButton_Axis4 = 36, 21 | 22 | // aliases for well known controllers 23 | k_EButton_SteamVR_Touchpad = k_EButton_Axis0, 24 | k_EButton_SteamVR_Trigger = k_EButton_Axis1, 25 | 26 | k_EButton_Dashboard_Back = k_EButton_Grip, 27 | 28 | k_EButton_Max = 64 29 | }; 30 | 31 | public class ButtonMask 32 | { 33 | public const ulong System = (1ul << (int)EVRButtonId.k_EButton_System); 34 | public const ulong ApplicationMenu = (1ul << (int)EVRButtonId.k_EButton_ApplicationMenu); 35 | public const ulong Grip = (1ul << (int)EVRButtonId.k_EButton_Grip); 36 | public const ulong Axis0 = (1ul << (int)EVRButtonId.k_EButton_Axis0); 37 | public const ulong Axis1 = (1ul << (int)EVRButtonId.k_EButton_Axis1); 38 | public const ulong Axis2 = (1ul << (int)EVRButtonId.k_EButton_Axis2); 39 | public const ulong Axis3 = (1ul << (int)EVRButtonId.k_EButton_Axis3); 40 | public const ulong Axis4 = (1ul << (int)EVRButtonId.k_EButton_Axis4); 41 | public const ulong Touchpad = (1ul << (int)EVRButtonId.k_EButton_SteamVR_Touchpad); 42 | public const ulong Trigger = (1ul << (int)EVRButtonId.k_EButton_SteamVR_Trigger); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/utils/ButtonMask.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.utils; 2 | 3 | public class ButtonMask{ 4 | private static int k_EButton_System = 0; 5 | private static int k_EButton_ApplicationMenu = 1; 6 | private static int k_EButton_Grip = 2; 7 | private static int k_EButton_DPad_Left = 3; 8 | private static int k_EButton_DPad_Up = 4; 9 | private static int k_EButton_DPad_Right = 5; 10 | private static int k_EButton_DPad_Down = 6; 11 | private static int k_EButton_A = 7; 12 | 13 | private static int k_EButton_ProximitySensor = 31; 14 | 15 | private static int k_EButton_Axis0 = 32; 16 | private static int k_EButton_Axis1 = 33; 17 | private static int k_EButton_Axis2 = 34; 18 | private static int k_EButton_Axis3 = 35; 19 | private static int k_EButton_Axis4 = 36; 20 | 21 | // aliases for well known controllers 22 | private static int k_EButton_SteamVR_Touchpad = k_EButton_Axis0; 23 | private static int k_EButton_SteamVR_Trigger = k_EButton_Axis1; 24 | private static int k_EButton_Dashboard_Back = k_EButton_Grip; 25 | private static int k_EButton_Max = 64; 26 | 27 | public static long System = (1L << (int)k_EButton_System); 28 | public static long ApplicationMenu = (1L << (int)k_EButton_ApplicationMenu); 29 | public static long Grip = (1L << (int)k_EButton_Grip); 30 | public static long Axis0 = (1L << (int)k_EButton_Axis0); 31 | public static long Axis1 = (1L << (int)k_EButton_Axis1); 32 | public static long Axis2 = (1L << (int)k_EButton_Axis2); 33 | public static long Axis3 = (1L << (int)k_EButton_Axis3); 34 | public static long Axis4 = (1L << (int)k_EButton_Axis4); 35 | public static long Touchpad = (1L << (int)k_EButton_SteamVR_Touchpad); 36 | public static long Trigger = (1L << (int)k_EButton_SteamVR_Trigger); 37 | } 38 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/RemoteBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using VRE.Vridge.API.Client.Proxy; 5 | 6 | namespace VRE.Vridge.API.Client.Remotes 7 | { 8 | public abstract class RemoteBase where T : ClientProxyBasePB 9 | { 10 | public bool IsDisposed { get; private set; } 11 | protected T Proxy; 12 | 13 | protected RemoteBase(T proxy) 14 | { 15 | this.Proxy = proxy; 16 | //EnsureSocketExists(); 17 | } 18 | 19 | internal virtual void Dispose() 20 | { 21 | IsDisposed = true; 22 | Proxy?.Dispose(); 23 | } 24 | 25 | protected T WrapTimeouts(Func action) 26 | { 27 | if (!Proxy.IsSocketOpen) 28 | { 29 | Dispose(); 30 | return default(T); 31 | } 32 | 33 | try 34 | { 35 | return action(); 36 | } 37 | catch (Exception x) 38 | { 39 | Dispose(); 40 | return default(T); 41 | } 42 | } 43 | 44 | protected void WrapTimeouts(Action action) 45 | { 46 | try 47 | { 48 | action(); 49 | } 50 | catch (Exception x) 51 | { 52 | Dispose(); 53 | } 54 | } 55 | 56 | 57 | 58 | /*internal void EnsureSocketExists() 59 | { 60 | // This can't fail because opening socket doesn't actually open the socket but only creates abstraction. 61 | // It is created on first send. 62 | if(!Proxy.IsSocketOpen) 63 | { 64 | Proxy?.Dispose(); 65 | Proxy = (T)Activator.CreateInstance(typeof(T), endpoint, true); 66 | } 67 | } */ 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/View/LabeledSlider.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace VRE.Vridge.API.DesktopTester.View 5 | { 6 | /// 7 | /// Interaction logic for LabeledSlider.xaml 8 | /// 9 | public partial class LabeledSlider : UserControl 10 | { 11 | 12 | public static readonly DependencyProperty SliderValueProperty = DependencyProperty.Register("SliderValue", typeof(double), typeof(LabeledSlider), new PropertyMetadata(default(double))); 13 | public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(LabeledSlider), new PropertyMetadata(default(string))); 14 | public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(LabeledSlider), new PropertyMetadata(default(double))); 15 | public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(LabeledSlider), new PropertyMetadata(default(double))); 16 | 17 | public string Label 18 | { 19 | get { return (string) GetValue(LabelProperty); } 20 | set { SetValue(LabelProperty, value); } 21 | } 22 | public double SliderValue 23 | { 24 | get { return (double)GetValue(SliderValueProperty); } 25 | set { SetValue(SliderValueProperty, value); } 26 | } 27 | 28 | public double Minimum 29 | { 30 | get { return (double) GetValue(MinimumProperty); } 31 | set { SetValue(MinimumProperty, value); } 32 | } 33 | 34 | public double Maximum 35 | { 36 | get { return (double) GetValue(MaximumProperty); } 37 | set { SetValue(MaximumProperty, value); } 38 | } 39 | 40 | public LabeledSlider() 41 | { 42 | InitializeComponent(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/DiscoveryClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using NetMQ; 6 | using ProtoBuf; 7 | using VRE.Vridge.API.Client.Messages.v3.Discovery; 8 | using VRE.Vridge.API.Client.Remotes.Beacons; 9 | 10 | namespace VRE.Vridge.API.Client.Remotes 11 | { 12 | internal class DiscoveryClient 13 | { 14 | private readonly VridgeServerBeaconList beaconList; 15 | private readonly NetMQBeacon beaconClient; 16 | private readonly NetMQPoller beaconPoller; 17 | 18 | public DiscoveryClient() 19 | { 20 | beaconList = new VridgeServerBeaconList(); 21 | beaconClient = new NetMQBeacon(); 22 | beaconClient.ConfigureAllInterfaces(38219); 23 | beaconClient.Subscribe(""); 24 | beaconClient.ReceiveReady += OnBeaconReceived; 25 | beaconPoller = new NetMQPoller() { beaconClient }; 26 | beaconPoller.RunAsync(); 27 | } 28 | 29 | public List ActiveVridgeServers => beaconList.FreshServers; 30 | 31 | public void Dispose() 32 | { 33 | if(beaconPoller != null && beaconPoller.IsRunning) beaconPoller.Stop(); 34 | beaconClient?.Dispose(); 35 | } 36 | 37 | private void OnBeaconReceived(object sender, NetMQBeaconEventArgs e) 38 | { 39 | var beaconMessage = e.Beacon.Receive(); 40 | var hostname = beaconMessage.PeerHost; 41 | var buffer = beaconMessage.Bytes; 42 | using (var ms = new MemoryStream(buffer)) 43 | { 44 | try 45 | { 46 | var beacon = Serializer.Deserialize(ms); 47 | beaconList.Add(beacon, hostname); 48 | } 49 | catch (Exception) 50 | { 51 | // Ignore stray packets 52 | } 53 | } 54 | 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/View/ControlWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Input; 3 | using VRE.Vridge.API.DesktopTester.ViewModel; 4 | 5 | namespace VRE.Vridge.API.DesktopTester.View 6 | { 7 | /// 8 | /// Interaction logic for MainWindow.xaml 9 | /// 10 | public partial class ControlWindow : Window 11 | { 12 | 13 | private bool isMarkerBeingDragged = false; 14 | private Point lastMousePosition; 15 | 16 | public ControlWindow() 17 | { 18 | InitializeComponent(); 19 | this.DataContext = new ControlViewModel(); 20 | } 21 | private void FrameworkElement_OnSizeChanged(object sender, SizeChangedEventArgs e) 22 | { 23 | (DataContext as ControlViewModel)?.UpdateDrawingBounds(e); 24 | } 25 | 26 | private void Marker_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 27 | { 28 | Mouse.Capture((UIElement) sender); 29 | isMarkerBeingDragged = true; 30 | lastMousePosition = e.GetPosition(Canvas); 31 | } 32 | 33 | private void Marker_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) 34 | { 35 | ((UIElement) sender).ReleaseMouseCapture(); 36 | isMarkerBeingDragged = false; 37 | } 38 | 39 | private void Canvas_OnMouseMove(object sender, MouseEventArgs e) 40 | { 41 | if (!isMarkerBeingDragged) 42 | return; 43 | 44 | // If mouse release event was skipped because window focus was taken by something else 45 | if (Mouse.LeftButton == MouseButtonState.Released) 46 | { 47 | isMarkerBeingDragged = false; 48 | return; 49 | } 50 | 51 | var delta = e.GetPosition(Canvas) - lastMousePosition; 52 | (DataContext as ControlViewModel)?.NotifyCanvasDrag(delta); 53 | 54 | lastMousePosition = e.GetPosition(Canvas); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Proxy/Broadcasts/BroadcastProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Timers; 7 | using NetMQ; 8 | using NetMQ.Sockets; 9 | using VRE.Vridge.API.Client.Messages.v3.Broadcast; 10 | 11 | namespace VRE.Vridge.API.Client.Proxy.Broadcasts 12 | { 13 | public class BroadcastProxy : IDisposable 14 | { 15 | private readonly SubscriberSocket socket; 16 | private readonly NetMQPoller poller; 17 | private bool isDisposed = false; 18 | 19 | public BroadcastProxy(string endpointAddr) 20 | { 21 | socket = new SubscriberSocket(endpointAddr); 22 | socket.Subscribe("haptic"); 23 | socket.Options.Linger = TimeSpan.FromSeconds(1); 24 | socket.ReceiveReady += BroadcastReceived; 25 | 26 | poller = new NetMQPoller {socket}; 27 | poller.RunAsync(); 28 | } 29 | 30 | public event EventHandler HapticPulseReceived; 31 | 32 | public void Disconnect() 33 | { 34 | if(poller != null && poller.IsRunning) poller.Stop(); 35 | socket?.Close(); 36 | 37 | HapticPulseReceived = null; 38 | } 39 | 40 | private void BroadcastReceived(object sender, NetMQSocketEventArgs ev) 41 | { 42 | var topic = ev.Socket.ReceiveFrameString(); 43 | var msg = ev.Socket.ReceiveFrameBytes(); 44 | 45 | if (topic == "haptic") 46 | { 47 | var hapticPulse = Helpers.SerializationHelpers.ProtoDeserialize(msg); 48 | HapticPulseReceived?.Invoke(this, hapticPulse); 49 | } 50 | } 51 | 52 | public void Dispose() 53 | { 54 | lock (this) 55 | { 56 | if (!isDisposed) 57 | { 58 | isDisposed = true; 59 | HapticPulseReceived = null; 60 | Disconnect(); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/HeadRemote.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using VRE.Vridge.API.Client.Proxy.HeadTracking; 5 | 6 | namespace VRE.Vridge.API.Client.Remotes 7 | { 8 | public class HeadRemote : RemoteBase 9 | { 10 | internal HeadRemote(HeadTrackingProxy proxy) : base(proxy) 11 | { 12 | 13 | } 14 | 15 | /// 16 | /// Reorients tracking system and sets new center to current head direction. 17 | /// 18 | public void Recenter() 19 | { 20 | WrapTimeouts(() => Proxy.RecenterView()); 21 | } 22 | 23 | public void SetPosition(float x, float y, float z) 24 | { 25 | WrapTimeouts(() => Proxy.SetPosition(x, y, z)); 26 | } 27 | 28 | public void SetRotationAndPosition(float yaw, float pitch, float roll, float x, float y, float z) 29 | { 30 | WrapTimeouts(() => Proxy.SetRotationAndPosition(yaw, pitch, roll, x, y, z)); 31 | } 32 | 33 | public void SetAsyncOffset(float yaw) 34 | { 35 | WrapTimeouts(() => Proxy.SetAsyncOffset(yaw)); 36 | } 37 | 38 | public float[] GetCurrentPose() 39 | { 40 | return WrapTimeouts(() => Proxy.GetCurrentPhonePose()); 41 | } 42 | 43 | public void SetStatus(bool isInTrackingRange) 44 | { 45 | WrapTimeouts(() => Proxy.ChangeTrackingState(isInTrackingRange)); 46 | } 47 | 48 | // Type-cast-methods 49 | public void SetAsyncOffset(double yaw) => 50 | SetAsyncOffset((float)yaw); 51 | 52 | public void SetRotationAndPosition(double yaw, double pitch, double roll, double x, double y, double z) => 53 | SetRotationAndPosition((float) yaw, (float) pitch, (float) roll, (float) x, (float) y, (float) z); 54 | 55 | public void SetPosition(double x, double y, double z) => 56 | SetPosition((float) x, (float) y, (float) z); 57 | 58 | internal override void Dispose() 59 | { 60 | Proxy?.Disconnect(); 61 | base.Dispose(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Helpers/MathHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | 4 | namespace VRE.Vridge.API.Client.Helpers 5 | { 6 | public static class MathHelpers 7 | { 8 | public static double RadToDeg(double rad) 9 | { 10 | return rad * (180.0 / Math.PI); 11 | } 12 | 13 | public static double DegToRad(double deg) 14 | { 15 | return Math.PI * deg / 180.0; 16 | } 17 | 18 | /// 19 | /// Converts 4x4 matrix into flat array with column-major layout. 20 | /// https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry/row-major-vs-column-major-vector 21 | /// 22 | public static float[] FlattenAsColumnMajor(this Matrix4x4 matrix) 23 | { 24 | var array = new float[16]; 25 | array[0] = (float)matrix.M11; 26 | array[1] = (float)matrix.M21; 27 | array[2] = (float)matrix.M31; 28 | array[3] = (float)matrix.M41; 29 | array[4] = (float)matrix.M12; 30 | array[5] = (float)matrix.M22; 31 | array[6] = (float)matrix.M32; 32 | array[7] = (float)matrix.M42; 33 | array[8] = (float)matrix.M13; 34 | array[9] = (float)matrix.M23; 35 | array[10] = (float)matrix.M33; 36 | array[11] = (float)matrix.M43; 37 | array[12] = (float)matrix.M14; 38 | array[13] = (float)matrix.M24; 39 | array[14] = (float)matrix.M34; 40 | array[15] = (float)matrix.M44; 41 | 42 | return array; 43 | } 44 | 45 | public static float[] Flatten(this Matrix4x4 matrix) 46 | { 47 | var array = new float[16]; 48 | array[0] = (float)matrix.M11; 49 | array[1] = (float)matrix.M12; 50 | array[2] = (float)matrix.M13; 51 | array[3] = (float)matrix.M14; 52 | array[4] = (float)matrix.M21; 53 | array[5] = (float)matrix.M22; 54 | array[6] = (float)matrix.M23; 55 | array[7] = (float)matrix.M24; 56 | array[8] = (float)matrix.M31; 57 | array[9] = (float)matrix.M32; 58 | array[10] = (float)matrix.M33; 59 | array[11] = (float)matrix.M34; 60 | array[12] = (float)matrix.M41; 61 | array[13] = (float)matrix.M42; 62 | array[14] = (float)matrix.M43; 63 | array[15] = (float)matrix.M44; 64 | 65 | return array; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("VRE.Vridge.API.DesktopClient")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("VRE.Vridge.API.DesktopClient")] 15 | [assembly: AssemblyCopyright("Copyright © 2017")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/proto/VridgeApiProtoDefinition.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | option java_multiple_files = true; 3 | option optimize_for = LITE_RUNTIME; 4 | 5 | package com.riftcat.vridge.api.client.java.proto; 6 | message Beacon { 7 | optional BeaconOrigin Role = 1 [default = Server]; 8 | optional string MachineName = 2; 9 | optional string UserName = 3; 10 | optional string HumanReadableVersion = 4; 11 | } 12 | enum BeaconOrigin { 13 | Server = 0; 14 | Client = 1; 15 | } 16 | message ControllerStateRequest { 17 | optional int32 Version = 1 [default = 0]; 18 | optional uint32 TaskType = 2 [default = 0]; 19 | optional VRController ControllerState = 4; 20 | } 21 | message ControllerStateResponse { 22 | optional int32 Version = 1 [default = 0]; 23 | optional uint32 ReplyCode = 2 [default = 0]; 24 | } 25 | enum HandType { 26 | Left = 0; 27 | Right = 1; 28 | } 29 | message HapticPulse { 30 | optional int32 ControllerId = 1 [default = 0]; 31 | optional uint32 LengthUs = 2 [default = 0]; 32 | optional uint32 TimestampUs = 3 [default = 0]; 33 | } 34 | enum HeadRelation { 35 | Unrelated = 0; 36 | IsInHeadSpace = 1; 37 | SticksToHead = 2; 38 | } 39 | message HeadTrackingRequest { 40 | optional int32 Version = 1 [default = 0]; 41 | optional uint32 TaskType = 2 [default = 0]; 42 | optional bytes Data = 3; 43 | } 44 | message HeadTrackingResponse { 45 | optional int32 Version = 1 [default = 0]; 46 | optional uint32 ReplyCode = 2 [default = 0]; 47 | repeated float Data = 3; 48 | optional TrackedPose TrackedPose = 4; 49 | } 50 | message TrackedPose { 51 | repeated float HeadOrientation = 1; 52 | repeated float HeadPosition = 2; 53 | optional float RecenterYawOffset = 3 [default = 0]; 54 | optional float ApiYawOffset = 4 [default = 0]; 55 | } 56 | message VRController { 57 | optional int32 ControllerId = 1 [default = 0]; 58 | optional int32 Status = 2 [default = 0]; 59 | repeated float OrientationMatrix = 3; 60 | optional VRControllerState_t ButtonState = 4; 61 | repeated double Acceleration = 5; 62 | repeated double Velocity = 6; 63 | optional HeadRelation HeadRelation = 7 [default = Unrelated]; 64 | optional HandType SuggestedHand = 8 [default = Left]; 65 | optional double Timestamp = 9 [default = 0]; 66 | optional string Name = 11; 67 | repeated float Position = 12; 68 | repeated float Orientation = 13; 69 | } 70 | message VRControllerAxis_t { 71 | optional float x = 1 [default = 0]; 72 | optional float y = 2 [default = 0]; 73 | } 74 | message VRControllerState_t { 75 | optional uint32 unPacketNum = 1 [default = 0]; 76 | optional uint64 ulButtonPressed = 2 [default = 0]; 77 | optional uint64 ulButtonTouched = 3 [default = 0]; 78 | optional VRControllerAxis_t rAxis0 = 4; 79 | optional VRControllerAxis_t rAxis1 = 5; 80 | optional VRControllerAxis_t rAxis2 = 7; 81 | optional VRControllerAxis_t rAxis3 = 8; 82 | optional VRControllerAxis_t rAxis4 = 9; 83 | } 84 | -------------------------------------------------------------------------------- /src/vridge-api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | option java_multiple_files = true; 4 | option java_package = "com.riftcat.vridge.api.client.java"; 5 | option optimize_for = LITE_RUNTIME; 6 | 7 | package com.riftcat.vridge.api.client.java.proto; 8 | message Beacon { 9 | optional BeaconOrigin Role = 1 [default = Server]; 10 | optional string MachineName = 2; 11 | optional string UserName = 3; 12 | optional string HumanReadableVersion = 4; 13 | } 14 | enum BeaconOrigin { 15 | Server = 0; 16 | Client = 1; 17 | } 18 | message ControllerStateRequest { 19 | optional int32 Version = 1 [default = 0]; 20 | optional uint32 TaskType = 2 [default = 0]; 21 | optional VRController ControllerState = 4; 22 | } 23 | message ControllerStateResponse { 24 | optional int32 Version = 1 [default = 0]; 25 | optional uint32 ReplyCode = 2 [default = 0]; 26 | } 27 | enum HandType { 28 | Left = 0; 29 | Right = 1; 30 | } 31 | message HapticPulse { 32 | optional int32 ControllerId = 1 [default = 0]; 33 | optional uint32 LengthUs = 2 [default = 0]; 34 | optional uint32 TimestampUs = 3 [default = 0]; 35 | } 36 | enum HeadRelation { 37 | Unrelated = 0; 38 | IsInHeadSpace = 1; 39 | SticksToHead = 2; 40 | } 41 | message HeadTrackingRequest { 42 | optional int32 Version = 1 [default = 0]; 43 | optional uint32 TaskType = 2 [default = 0]; 44 | optional bytes Data = 3; 45 | } 46 | message HeadTrackingResponse { 47 | optional int32 Version = 1 [default = 0]; 48 | optional uint32 ReplyCode = 2 [default = 0]; 49 | repeated float Data = 3; 50 | optional TrackedPose TrackedPose = 4; 51 | } 52 | message TrackedPose { 53 | repeated float HeadOrientation = 1; 54 | repeated float HeadPosition = 2; 55 | optional float RecenterYawOffset = 3 [default = 0]; 56 | optional float ApiYawOffset = 4 [default = 0]; 57 | } 58 | message VRController { 59 | optional int32 ControllerId = 1 [default = 0]; 60 | optional int32 Status = 2 [default = 0]; 61 | repeated float OrientationMatrix = 3; 62 | optional VRControllerState_t ButtonState = 4; 63 | repeated double Acceleration = 5; 64 | repeated double Velocity = 6; 65 | optional HeadRelation HeadRelation = 7 [default = Unrelated]; 66 | optional HandType SuggestedHand = 8 [default = Left]; 67 | optional double Timestamp = 9 [default = 0]; 68 | optional string Name = 11; 69 | repeated float Position = 12; 70 | repeated float Orientation = 13; 71 | } 72 | message VRControllerAxis_t { 73 | optional float x = 1 [default = 0]; 74 | optional float y = 2 [default = 0]; 75 | } 76 | message VRControllerState_t { 77 | optional uint32 unPacketNum = 1 [default = 0]; 78 | optional uint64 ulButtonPressed = 2 [default = 0]; 79 | optional uint64 ulButtonTouched = 3 [default = 0]; 80 | optional VRControllerAxis_t rAxis0 = 4; 81 | optional VRControllerAxis_t rAxis1 = 5; 82 | optional VRControllerAxis_t rAxis2 = 7; 83 | optional VRControllerAxis_t rAxis3 = 8; 84 | optional VRControllerAxis_t rAxis4 = 9; 85 | } 86 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/remotes/DiscoveryClient.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.remotes; 2 | 3 | import com.google.protobuf.InvalidProtocolBufferException; 4 | import com.riftcat.vridge.api.client.java.proto.Beacon; 5 | import com.riftcat.vridge.api.client.java.proto.BeaconOrigin; 6 | import com.riftcat.vridge.api.client.java.remotes.beacons.VridgeServerBeacon; 7 | import com.riftcat.vridge.api.client.java.remotes.beacons.VridgeServerBeaconList; 8 | 9 | import org.zeromq.ZBeacon; 10 | 11 | import java.net.InetAddress; 12 | import java.util.List; 13 | 14 | class DiscoveryClient implements Thread.UncaughtExceptionHandler { 15 | 16 | private final byte[] identity; 17 | private VridgeServerBeaconList beaconList; 18 | private ZBeacon beaconClient; 19 | 20 | DiscoveryClient(){ 21 | 22 | identity = Beacon.newBuilder() 23 | .setRole(BeaconOrigin.Client) 24 | // We don't use information below 25 | .setMachineName("Android") 26 | .setHumanReadableVersion("Android") 27 | .setUserName("Android") 28 | .build() 29 | .toByteArray(); 30 | 31 | beaconList = new VridgeServerBeaconList(); 32 | reset(); 33 | } 34 | 35 | public void reset(){ 36 | dispose(); 37 | beaconClient = new ZBeacon("255.255.255.255",38219, identity, true, true); 38 | beaconClient.setBroadcastInterval(1000); 39 | beaconClient.setListener(new ZBeacon.Listener() { 40 | @Override 41 | public void onBeacon(InetAddress sender, byte[] buffer) { 42 | 43 | Beacon beacon; 44 | try { 45 | beacon = Beacon.parser().parseFrom(buffer); 46 | } catch (InvalidProtocolBufferException e) { 47 | // Ignore stray packets 48 | return; 49 | } 50 | 51 | if(beacon.getRole() != BeaconOrigin.Server){ 52 | // Skip other clients 53 | return; 54 | } 55 | 56 | beaconList.add(beacon, sender); 57 | } 58 | }); 59 | beaconClient.setUncaughtExceptionHandlers(this, this); 60 | beaconClient.start(); 61 | } 62 | 63 | public List getFreshServers() { 64 | return beaconList.getFreshServers(); 65 | } 66 | 67 | public synchronized void dispose() { 68 | try { 69 | if(beaconClient != null){ 70 | beaconClient.stop(); 71 | beaconClient = null; 72 | } 73 | } catch (InterruptedException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | @Override 79 | public void uncaughtException(Thread t, Throwable e) { 80 | try { 81 | Thread.sleep(3000); 82 | } catch (InterruptedException ignored) { 83 | 84 | } 85 | 86 | reset(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace VRE.Vridge.API.DesktopTester.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VRE.Vridge.API.DesktopTester.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/proxy/ClientProxyBase.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.proxy; 2 | 3 | import com.google.protobuf.InvalidProtocolBufferException; 4 | import com.google.protobuf.MessageLite; 5 | import com.google.protobuf.Parser; 6 | import com.riftcat.vridge.api.client.java.APIClient; 7 | import com.riftcat.vridge.api.client.java.utils.APILogger; 8 | 9 | import org.zeromq.ZContext; 10 | import org.zeromq.ZMQ; 11 | 12 | import java.util.TimerTask; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.ScheduledExecutorService; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.concurrent.TimeoutException; 17 | 18 | public abstract class ClientProxyBase implements VRidgeApiProxy{ 19 | 20 | private TimerTask keepAliveTimer; 21 | private Runnable keepAlivePing; 22 | 23 | private byte[] keepAlivePacket = { 0 }; 24 | 25 | int CurrentVersion = 3; 26 | ZMQ.Socket socket; 27 | 28 | ClientProxyBase(String endpointAddress, boolean keepAlive){ 29 | 30 | if(APIClient.ZContext == null) APIClient.ZContext = new ZContext(4); 31 | socket = APIClient.ZContext.createSocket(ZMQ.REQ); 32 | socket.setLinger(1000); 33 | socket.setSendTimeOut(15000); 34 | socket.setReceiveTimeOut(15000); 35 | socket.connect(endpointAddress); 36 | socket.setHWM(1); 37 | 38 | if (!keepAlive) return; 39 | 40 | keepAlivePing = new Runnable() { 41 | @Override 42 | public void run() { 43 | sendKeepAlivePing(); 44 | } 45 | }; 46 | 47 | ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); 48 | executor.scheduleAtFixedRate(keepAlivePing, 1, 5, TimeUnit.SECONDS); 49 | } 50 | 51 | void CloseSocket(){ 52 | APIClient.ZContext.destroySocket(socket); 53 | } 54 | 55 | synchronized T SendMessage(MessageLite msg, Parser parser) throws TimeoutException { 56 | 57 | APILogger.zmq("send begin"); 58 | long timestamp = System.nanoTime(); 59 | socket.send(msg.toByteArray()); 60 | byte[] responseBytes = socket.recv(); 61 | APILogger.zmq("recv end - " + (System.nanoTime() - timestamp) / 1000000.0); 62 | 63 | if (responseBytes != null){ 64 | try { 65 | T response = parser.parseFrom(responseBytes); 66 | return response; 67 | } catch (InvalidProtocolBufferException e) { 68 | // whoops 69 | } 70 | } 71 | 72 | APILogger.zmq("timeout"); 73 | APIClient.ZContext.destroySocket(socket); 74 | throw new TimeoutException(); 75 | } 76 | 77 | public abstract void disconnect(); 78 | 79 | public synchronized boolean sendKeepAlivePing(){ 80 | boolean error = false; 81 | APILogger.zmq("ping begin: "); 82 | error = error || !socket.send(keepAlivePacket); 83 | error = error || socket.recv() == null; 84 | APILogger.zmq("ping end - error: " + error); 85 | 86 | return !error; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/remotes/HeadRemote.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.remotes; 2 | 3 | import com.riftcat.vridge.api.client.java.proto.TrackedPose; 4 | import com.riftcat.vridge.api.client.java.proxy.HeadTrackingProxy; 5 | 6 | import java.util.concurrent.TimeoutException; 7 | 8 | public class HeadRemote extends RemoteBase { 9 | private HeadTrackingProxy proxy; 10 | 11 | HeadRemote(HeadTrackingProxy proxy) { 12 | super(proxy); 13 | 14 | this.proxy = proxy; 15 | } 16 | 17 | /** 18 | * Reorients tracking system and sets new center to current head direction. 19 | */ 20 | public void recenter(){ 21 | try{ 22 | proxy.recenterView(); 23 | } 24 | catch (Exception x){ 25 | dispose(); 26 | } 27 | 28 | } 29 | 30 | /** 31 | * Gets current head pose and related offsets. May return null on connection issues. 32 | */ 33 | public TrackedPose getCurrentPose() { 34 | 35 | try{ 36 | return proxy.getCurrentPose(); 37 | } 38 | catch (Exception e){ 39 | dispose(); 40 | return null; 41 | } 42 | } 43 | 44 | /** 45 | * Sets head position to new location. 46 | */ 47 | public void setPosition(float x, float y, float z){ 48 | try{ 49 | proxy.setPosition(x, y, z); 50 | } 51 | catch (Exception e){ 52 | dispose(); 53 | } 54 | } 55 | 56 | /** 57 | * Sets head position to new location and orientation. 58 | * This won't work for headsets with reprojection enabled. 59 | */ 60 | public void setRotationAndPosition(float yaw, float pitch, float roll, float x, float y, float z){ 61 | try{ 62 | proxy.setRotationAndPosition(yaw, pitch, roll, x, y, z); 63 | } 64 | catch (Exception e){ 65 | dispose(); 66 | } 67 | } 68 | 69 | 70 | /** 71 | * Sets offsets in yaw axis to be applied to each head and controller pose processed by the system. 72 | * @param yaw Offset in radians. 73 | */ 74 | public void setYawOffset(float yaw){ 75 | try{ 76 | proxy.setYawOffset(yaw); 77 | } 78 | catch (Exception x){ 79 | dispose(); 80 | } 81 | } 82 | 83 | /** 84 | * Marks the headset as in/outside of tracking range. Setting it to false will most likely 85 | * stop rendering on SteamVR side as pose data will be considered invalid. 86 | */ 87 | public void setStatus(boolean isInTrackingRange) 88 | { 89 | try{ 90 | proxy.changeTrackingState(isInTrackingRange); 91 | } 92 | catch (Exception e){ 93 | dispose(); 94 | } 95 | } 96 | 97 | // Type-cast-methods 98 | /** 99 | * Sets head position to new location and orientation. 100 | * This won't work for headsets with reprojection enabled. 101 | */ 102 | public void setRotationAndPosition(double yaw, double pitch, double roll, double x, double y, double z){ 103 | setRotationAndPosition((float) yaw, (float) pitch, (float) roll, (float) x, (float) y, (float) z); 104 | } 105 | 106 | /** 107 | * Sets head position to new location. 108 | */ 109 | public void setPosition(double x, double y, double z) { 110 | setPosition((float) x, (float) y, (float) z); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/proxy/BroadcastProxy.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.proxy; 2 | 3 | import com.google.protobuf.InvalidProtocolBufferException; 4 | import com.riftcat.vridge.api.client.java.APIClient; 5 | import com.riftcat.vridge.api.client.java.proto.HapticPulse; 6 | import com.riftcat.vridge.api.client.java.utils.APILogger; 7 | 8 | import org.zeromq.ZMQ; 9 | import java.nio.charset.Charset; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | 13 | 14 | public class BroadcastProxy implements VRidgeApiProxy { 15 | 16 | private final String endpointAddr; 17 | private ZMQ.Socket socket; 18 | private List listeners = new LinkedList(); 19 | 20 | private Thread threadPolling; 21 | 22 | public BroadcastProxy(String endpointAddr){ 23 | this.endpointAddr = endpointAddr; 24 | } 25 | 26 | public void startPolling(){ 27 | 28 | if(threadPolling != null) threadPolling.interrupt(); 29 | 30 | threadPolling = new Thread(new Runnable() { 31 | @Override 32 | public void run() { 33 | 34 | socket = APIClient.ZContext.createSocket(ZMQ.SUB); 35 | socket.connect(endpointAddr); 36 | socket.subscribe("haptic".getBytes(Charset.forName("UTF-8"))); 37 | socket.setLinger(1000); 38 | 39 | ZMQ.Poller poller = APIClient.ZContext.createPoller(1); 40 | poller.register(socket, ZMQ.Poller.POLLIN); 41 | 42 | while(!Thread.currentThread().isInterrupted()){ 43 | 44 | int result = poller.poll(1000); 45 | if(result > 0){ 46 | 47 | // we can ignore topic here, it's filtered at socket level 48 | // but we need to consume it from socket to continue 49 | socket.recvStr(); 50 | 51 | byte[] bufMsg = socket.recv(); 52 | 53 | try { 54 | 55 | // Deserialize 56 | HapticPulse pulse = HapticPulse.parseFrom(bufMsg); 57 | 58 | // Notify listeners 59 | for(IBroadcastListener listener : listeners){ 60 | listener.onHapticPulse(pulse); 61 | } 62 | 63 | } catch (InvalidProtocolBufferException e) { 64 | // Invalid data - could not be deserialized 65 | } 66 | } 67 | } // while(!Thread.currentThread().isInterrupted()) 68 | 69 | poller.close(); 70 | } 71 | }); 72 | 73 | threadPolling.start(); 74 | } 75 | 76 | public void disconnect(){ 77 | try { 78 | if(threadPolling != null){ 79 | threadPolling.interrupt(); 80 | threadPolling.join(); 81 | } 82 | 83 | if(socket != null){ 84 | socket.close(); 85 | } 86 | 87 | listeners.clear(); 88 | } catch (InterruptedException e) { 89 | APILogger.error("Can't close Broadcast endpoint."); 90 | e.printStackTrace(); 91 | } 92 | 93 | } 94 | 95 | public void addListener(IBroadcastListener listener){ 96 | listeners.add(listener); 97 | } 98 | 99 | public void removeListener(IBroadcastListener listener){ 100 | listeners.remove(listener); 101 | } 102 | 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Proxy/Controller/ControllerProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using NetMQ; 6 | using VRE.Vridge.API.Client.Messages.OpenVR; 7 | using VRE.Vridge.API.Client.Messages.v3.Controller; 8 | using VRE.Vridge.API.Client.Messages.v3.Controller.Requests; 9 | using VRE.Vridge.API.Client.Messages.v3.Controller.Responses; 10 | using VRController = VRE.Vridge.API.Client.Messages.v3.Controller.VRController; 11 | using VRE.Vridge.API.Client.Remotes; 12 | 13 | 14 | namespace VRE.Vridge.API.Client.Proxy.Controller 15 | { 16 | /// 17 | /// Lower level access method for sending controller data to VRidge. Consider using 18 | /// with for easier operation. 19 | /// 20 | public class ControllerProxy : ClientProxyBasePB 21 | { 22 | private ControllerStateRequest controller; 23 | 24 | /// 25 | /// Creates controller proxy and establishes connection. 26 | /// 27 | /// 28 | /// Endpoint address (ip:port). Should be requested from control connection. 29 | /// 30 | /// 31 | /// True if automatic pings should keep connection alive even if caller doesn't send data. 32 | /// 33 | public ControllerProxy(string endpointAddress, bool keepAlive = false) 34 | : base(endpointAddress, keepAlive) 35 | { 36 | controller = new ControllerStateRequest() 37 | { 38 | TaskType = (byte) ControllerTask.SendFullState, 39 | Version = ControllerStateRequest.CurrentVersion 40 | }; 41 | } 42 | 43 | /// 44 | /// Send full single VR controller state to VR. 45 | /// 46 | [MethodImpl(MethodImplOptions.Synchronized)] 47 | public void SendControllerData(VRController state) 48 | { 49 | controller.ControllerState = state; 50 | controller.Version = ControllerStateRequest.CurrentVersion; 51 | SendMessage(controller); 52 | } 53 | 54 | /// 55 | /// Recenter head tracking. Works the same as pressing recenter hotkey as configured in VRidge settings. 56 | /// 57 | public void RecenterHead() 58 | { 59 | SendMessage(new ControllerStateRequest() 60 | { 61 | ControllerState = default(VRController), 62 | TaskType = (byte) ControllerTask.RecenterHead, 63 | Version = ControllerStateRequest.CurrentVersion 64 | }); 65 | } 66 | 67 | /// 68 | /// Disconnected from controller API and frees the API for other clients. 69 | /// 70 | public void Disconnect() 71 | { 72 | var disconnectRequest = new ControllerStateRequest() 73 | { 74 | TaskType = (byte)ControllerTask.Disconnect 75 | }; 76 | 77 | try 78 | { 79 | SendMessage(disconnectRequest); 80 | } 81 | catch (TimeoutException) 82 | { 83 | // Connection probably dropped another way, ignoring 84 | } 85 | catch (FiniteStateMachineException) 86 | { 87 | // Connection state invalid, close anyway 88 | } 89 | CloseSocket(); 90 | } 91 | 92 | private ControllerStateResponse SendMessage(ControllerStateRequest obj) => SendMessage(obj); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/proxy/ControllerProxy.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.proxy; 2 | 3 | import com.riftcat.vridge.api.client.java.codes.ControllerStateRequestCodes; 4 | import com.riftcat.vridge.api.client.java.proto.ControllerStateRequest; 5 | import com.riftcat.vridge.api.client.java.proto.ControllerStateResponse; 6 | import com.riftcat.vridge.api.client.java.proto.HandType; 7 | import com.riftcat.vridge.api.client.java.proto.VRController; 8 | import com.riftcat.vridge.api.client.java.proto.VRControllerAxis_t; 9 | import com.riftcat.vridge.api.client.java.proto.VRControllerState_t; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.LinkedList; 14 | import java.util.List; 15 | import java.util.concurrent.TimeoutException; 16 | 17 | public class ControllerProxy extends ClientProxyBase { 18 | 19 | private int packetNum = 0; 20 | 21 | public ControllerProxy(String endpointAddress){ 22 | super(endpointAddress, true); 23 | } 24 | 25 | /// 26 | /// Send full single VR controller state to VR. 27 | /// 28 | public synchronized void sendControllerState(VRController state) throws TimeoutException { 29 | ControllerStateRequest data = ControllerStateRequest 30 | .newBuilder() 31 | .setTaskType(ControllerStateRequestCodes.SendFullState) 32 | .setControllerState(state) 33 | .build(); 34 | 35 | sendMessage(data); 36 | } 37 | 38 | /** 39 | * Send full single VR controller state to VR. 40 | */ 41 | public synchronized void sendControllerState(int controllerId, long touchedMask, long pressedMask, 42 | List orientationMatrix, 43 | float triggerValue, 44 | float analogX, float analogY, float[] velocity, 45 | HandType hand) throws TimeoutException { 46 | 47 | VRControllerState_t.Builder buttonState = VRControllerState_t.newBuilder() 48 | .setRAxis0(VRControllerAxis_t.newBuilder() 49 | .setX(analogX) 50 | .setY(analogY)) 51 | .setRAxis1(VRControllerAxis_t.newBuilder() 52 | .setX(triggerValue)) 53 | .setUlButtonPressed(pressedMask) 54 | .setUlButtonTouched(touchedMask) 55 | .setUnPacketNum(++packetNum); 56 | 57 | 58 | 59 | VRController.Builder controllerState = VRController.newBuilder() 60 | .setControllerId(controllerId) 61 | .addAllOrientationMatrix(orientationMatrix) 62 | .setStatus(0) 63 | .setSuggestedHand(hand) 64 | .setButtonState(buttonState); 65 | 66 | if(velocity != null){ 67 | controllerState 68 | .addVelocity(velocity[0]) 69 | .addVelocity(velocity[1]) 70 | .addVelocity(velocity[2]); 71 | } 72 | 73 | ControllerStateRequest request = ControllerStateRequest.newBuilder() 74 | .setTaskType(ControllerStateRequestCodes.SendFullState) 75 | .setControllerState(controllerState) 76 | .build(); 77 | 78 | sendMessage(request); 79 | } 80 | 81 | /** Recenter head tracking. Works the same as pressing recenter hotkey as configured in VRidge settings. */ 82 | public void recenterHead() throws TimeoutException{ 83 | ControllerStateRequest request = ControllerStateRequest 84 | .newBuilder() 85 | .setVersion(CurrentVersion) 86 | .setTaskType(ControllerStateRequestCodes.RecenterHead) 87 | .build(); 88 | sendMessage(request); 89 | } 90 | 91 | /** 92 | * Disconnected from controller API and frees the API for other clients. 93 | */ 94 | public void disconnect(){ 95 | ControllerStateRequest disconnectRequest = ControllerStateRequest 96 | .newBuilder() 97 | .setVersion(CurrentVersion) 98 | .setTaskType(ControllerStateRequestCodes.Disconnect) 99 | .build(); 100 | 101 | try{ 102 | sendMessage(disconnectRequest); 103 | } 104 | catch (Exception x){ 105 | // ignored 106 | } 107 | CloseSocket(); 108 | } 109 | 110 | private ControllerStateResponse sendMessage(ControllerStateRequest req) throws TimeoutException { 111 | return SendMessage(req, ControllerStateResponse.parser()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Helpers/SerializationHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using NetMQ; 5 | using Newtonsoft.Json; 6 | using ProtoBuf; 7 | 8 | namespace VRE.Vridge.API.Client.Helpers 9 | { 10 | public static class SerializationHelpers 11 | { 12 | /// 13 | /// Sends object as JSON-serialized data through given socket. 14 | /// 15 | public static void SendAsJson(this IOutgoingSocket socket, object obj) 16 | { 17 | var json = JsonConvert.SerializeObject(obj); 18 | socket.SendFrame(json); 19 | } 20 | 21 | /// 22 | /// Tries to send object as JSON-serialized data through given socket. 23 | /// Returns false if operation times out. 24 | /// 25 | public static bool TrySendAsJson(this IOutgoingSocket socket, object obj, int timeoutMs) 26 | { 27 | var json = JsonConvert.SerializeObject(obj); 28 | return socket.TrySendFrame(TimeSpan.FromMilliseconds(timeoutMs), json); 29 | } 30 | 31 | /// 32 | /// Awaits T object as given socket received as JSON. 33 | /// 34 | public static T ReceiveJson(this IReceivingSocket socket) 35 | { 36 | var json = socket.ReceiveFrameString(); 37 | return JsonConvert.DeserializeObject(json); 38 | } 39 | 40 | /// 41 | /// Awaits T object as given socket received as JSON. 42 | /// Returns default(T) if operation times out. 43 | /// 44 | public static bool TryReceiveJson(this IReceivingSocket socket, out T response, int timeoutMs) 45 | { 46 | string responseStr; 47 | var success = socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(timeoutMs), out responseStr); 48 | 49 | if (string.IsNullOrEmpty(responseStr)) 50 | { 51 | response = default(T); 52 | return false; 53 | } 54 | 55 | response = JsonConvert.DeserializeObject(responseStr); 56 | return success; 57 | } 58 | 59 | /// 60 | /// Converts given structure to byte array. 61 | /// 62 | public static byte[] ProtoSerialize(object obj) 63 | { 64 | using (var ms = new MemoryStream()) 65 | { 66 | Serializer.Serialize(ms, obj); 67 | return ms.ToArray(); 68 | } 69 | } 70 | 71 | /// 72 | /// Converts given byte array to structure T. 73 | /// 74 | public static T ProtoDeserialize(byte[] data) 75 | { 76 | using (var ms = new MemoryStream(data)) 77 | { 78 | return Serializer.Deserialize(ms); 79 | } 80 | } 81 | 82 | /// 83 | /// Converts structure to pointer. Caller needs to free IntPtr. 84 | /// 85 | public static IntPtr StructureToIntPtr(object str, out int size) 86 | { 87 | size = Marshal.SizeOf(str); 88 | IntPtr ptr = Marshal.AllocHGlobal(size); 89 | Marshal.StructureToPtr(str, ptr, false); 90 | return ptr; 91 | } 92 | 93 | /// 94 | /// Converts array to pointer. Caller needs to free IntPtr. 95 | /// 96 | public static IntPtr ArrayToIntPtr(byte[] array) 97 | { 98 | IntPtr ptr = Marshal.AllocHGlobal(array.Length); 99 | Marshal.Copy(array, 0, ptr, array.Length); 100 | return ptr; 101 | } 102 | 103 | /// 104 | /// Converts given structure to byte array. 105 | /// 106 | public static byte[] StructureToByteArray(object str) 107 | { 108 | int size = Marshal.SizeOf(str); 109 | byte[] arr = new byte[size]; 110 | 111 | IntPtr ptr = Marshal.AllocHGlobal(size); 112 | Marshal.StructureToPtr(str, ptr, false); 113 | Marshal.Copy(ptr, arr, 0, size); 114 | Marshal.FreeHGlobal(ptr); 115 | return arr; 116 | } 117 | 118 | /// 119 | /// Converts given byte array to structure T. 120 | /// 121 | public static T ByteArrayToStructure(byte[] data) 122 | { 123 | GCHandle pin = GCHandle.Alloc(data, GCHandleType.Pinned); 124 | T packet = (T)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(T)); 125 | pin.Free(); 126 | 127 | return packet; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Proxy/ClientProxyBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Timers; 4 | using NetMQ; 5 | using NetMQ.Sockets; 6 | using VRE.Vridge.API.Client.Helpers; 7 | 8 | namespace VRE.Vridge.API.Client.Proxy 9 | { 10 | public class ClientProxyBase : IDisposable 11 | { 12 | protected bool AutoRestartOnTimeout = false; 13 | 14 | private readonly string endpointAddress; 15 | private readonly bool shouldKeepAlive; 16 | 17 | private Timer keepAliveTimer; 18 | 19 | private RequestSocket socket; 20 | 21 | 22 | protected ClientProxyBase(string endpointAddress, bool keepAlive = false) 23 | { 24 | this.endpointAddress = endpointAddress; 25 | this.shouldKeepAlive = keepAlive; 26 | 27 | CreateSocket(); 28 | } 29 | 30 | internal bool IsSocketOpen => socket != null; 31 | 32 | /// 33 | /// Sends a struct message and deserializes response bytes into response struct. 34 | /// No timeout handling. 35 | /// 36 | public T OptimisticSendMessage(object msg) 37 | { 38 | socket.SendFrame(Serialize(msg)); 39 | var reply = socket.ReceiveFrameBytes(); 40 | 41 | return Deserialize(reply); 42 | } 43 | 44 | /// 45 | /// Sends a struct message and deserializes response bytes into response struct. 46 | /// Throws System.TimeoutException if timeout occurs and closes socket. 47 | /// 48 | [MethodImpl(MethodImplOptions.Synchronized)] 49 | public T SendMessage(object msg, int timeoutMs = 1000) 50 | { 51 | if (socket == null) 52 | { 53 | throw Timeout(); 54 | } 55 | 56 | var stillAlive = socket.TrySendFrame(TimeSpan.FromMilliseconds(timeoutMs), Serialize(msg)); 57 | 58 | if (!stillAlive) 59 | { 60 | throw Timeout(); 61 | } 62 | 63 | stillAlive = socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(timeoutMs), out var reply); 64 | 65 | if (!stillAlive) 66 | { 67 | throw Timeout(); 68 | } 69 | 70 | return Deserialize(reply); 71 | } 72 | 73 | /// 74 | /// Sends a struct message and deserializes response bytes into response struct. 75 | /// Throws System.TimeoutException if timeout occurs and closes socket. 76 | /// Skips inputserialization. 77 | /// 78 | [MethodImpl(MethodImplOptions.Synchronized)] 79 | public T SendRawFrame(byte[] msgFrame, int timeoutMs = 1000) 80 | { 81 | byte[] reply; 82 | 83 | socket.SendFrame(msgFrame); 84 | var success = socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(timeoutMs), out reply); 85 | 86 | if (success) 87 | { 88 | return Deserialize(reply); 89 | } 90 | 91 | throw Timeout(); 92 | } 93 | 94 | /// 95 | /// Sends a zero byte to prevent connection from timing out. 96 | /// 97 | [MethodImpl(MethodImplOptions.Synchronized)] 98 | public void SendKeepAlivePing() 99 | { 100 | var isStillAlive = socket.TrySendFrame(TimeSpan.FromMilliseconds(30), new byte[]{0}); 101 | 102 | if (!isStillAlive) 103 | { 104 | Timeout(); 105 | return; 106 | } 107 | 108 | isStillAlive = socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(30), out var bytes); 109 | 110 | if (!isStillAlive) 111 | { 112 | Timeout(); 113 | } 114 | 115 | } 116 | 117 | private void CreateSocket() 118 | { 119 | socket = new RequestSocket(); 120 | socket.Options.Linger = TimeSpan.FromSeconds(1); 121 | socket.Connect(endpointAddress); 122 | 123 | if (!shouldKeepAlive) return; 124 | 125 | /* Very basic keep alive mechanism 126 | * generally you should provide a steady stream of data yourself 127 | * or reconnect after a period if inactivity */ 128 | keepAliveTimer = new Timer(5000); 129 | keepAliveTimer.Elapsed += (s, e) => SendKeepAlivePing(); 130 | keepAliveTimer.Start(); 131 | } 132 | 133 | private TimeoutException Timeout() 134 | { 135 | CloseSocket(); 136 | 137 | if (AutoRestartOnTimeout) 138 | { 139 | CreateSocket(); 140 | } 141 | 142 | return new TimeoutException(); 143 | } 144 | 145 | 146 | protected void CloseSocket() 147 | { 148 | keepAliveTimer?.Stop(); 149 | socket?.Dispose(); 150 | socket = null; 151 | } 152 | 153 | protected virtual byte[] Serialize(object o) 154 | { 155 | return SerializationHelpers.StructureToByteArray(o); 156 | } 157 | 158 | protected virtual T Deserialize(byte[] array) 159 | { 160 | return SerializationHelpers.ByteArrayToStructure(array); 161 | } 162 | 163 | public void Dispose() 164 | { 165 | CloseSocket(); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Proxy/APIClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NetMQ.Sockets; 4 | using VRE.Vridge.API.Client.Helpers; 5 | using VRE.Vridge.API.Client.Messages; 6 | using VRE.Vridge.API.Client.Messages.Control; 7 | using VRE.Vridge.API.Client.Messages.Control.Requests; 8 | using VRE.Vridge.API.Client.Messages.Control.Responses; 9 | using VRE.Vridge.API.Client.Proxy.Broadcasts; 10 | using VRE.Vridge.API.Client.Proxy.Controller; 11 | using VRE.Vridge.API.Client.Proxy.HeadTracking; 12 | 13 | namespace VRE.Vridge.API.Client.Proxy 14 | { 15 | public class APIClient 16 | { 17 | public enum Endpoints 18 | { 19 | HeadTracking, 20 | Controller, 21 | Broadcast 22 | } 23 | 24 | private RequestSocket controlSocket; 25 | 26 | private readonly string serverAddress = "localhost"; 27 | private readonly string appName = "default"; 28 | 29 | public APIClient(string appName) 30 | { 31 | this.appName = appName; 32 | } 33 | 34 | public APIClient(string ip, string appName) 35 | { 36 | serverAddress = ip; 37 | this.appName = appName; 38 | } 39 | 40 | /// 41 | /// Sends control request to see what APIs are available. 42 | /// May return null if control connection dies (automatic reconnect will follow). 43 | /// 44 | public APIStatus GetStatus(int timeoutMs = 500) 45 | { 46 | APIStatus status; 47 | 48 | ConnectToControlSocket(); 49 | 50 | controlSocket.SendAsJson(new ControlRequestHeader(appName, ControlRequestCode.RequestStatus)); 51 | var success = controlSocket.TryReceiveJson(out status, timeoutMs); 52 | 53 | controlSocket.Close(); 54 | 55 | if (success) 56 | { 57 | return status; 58 | } 59 | 60 | return null; 61 | } 62 | 63 | public T CreateProxy(bool keepAlive = true, int timeoutMs = 100) 64 | { 65 | // Find out which endpoint name should be used 66 | string endpointName; 67 | if (typeof(T) == typeof(HeadTrackingProxy)) 68 | { 69 | endpointName = EndpointNames.HeadTracking; 70 | } 71 | else if (typeof(T) == typeof(ControllerProxy)) 72 | { 73 | endpointName = EndpointNames.Controller; 74 | } 75 | else if (typeof(T) == typeof(BroadcastProxy)) 76 | { 77 | endpointName = EndpointNames.Broadcast; 78 | } 79 | else 80 | { 81 | throw new ArgumentException("Invalid proxy requested."); 82 | } 83 | 84 | ConnectToControlSocket(); 85 | 86 | // Try requesting given endpoint 87 | bool success = controlSocket.TrySendAsJson(new RequestEndpoint(endpointName, appName), timeoutMs); 88 | 89 | if (!success) 90 | { 91 | HandleControlConnectionException(new Exception("API server timeout.")); 92 | } 93 | 94 | 95 | EndpointCreated response; 96 | success = controlSocket.TryReceiveJson(out response, timeoutMs); 97 | 98 | controlSocket.Close(); 99 | 100 | if (!success) 101 | { 102 | HandleControlConnectionException(new Exception("API server timeout.")); 103 | } 104 | 105 | if (response.Code == (int) ControlResponseCode.InUse) 106 | { 107 | HandleControlConnectionException(new Exception("API already in use by another client.")); 108 | } 109 | 110 | // Initialize the proxy of requested type 111 | var connectionString = $"tcp://{serverAddress}:{response.Port}"; 112 | if (typeof(T) == typeof(HeadTrackingProxy)) 113 | { 114 | return (T) Convert.ChangeType(new HeadTrackingProxy(connectionString, keepAlive), typeof(T)); 115 | } 116 | if (typeof(T) == typeof(ControllerProxy)) 117 | { 118 | return (T) Convert.ChangeType(new ControllerProxy(connectionString, keepAlive), typeof(T)); 119 | } 120 | if (typeof(T) == typeof(BroadcastProxy)) 121 | { 122 | return (T) Convert.ChangeType(new BroadcastProxy(connectionString), typeof(T)); 123 | } 124 | 125 | throw new ArgumentException("Invalid proxy requested."); 126 | } 127 | 128 | private void ConnectToControlSocket() 129 | { 130 | controlSocket = new RequestSocket(); 131 | controlSocket.Connect(ControlEndpoint); 132 | } 133 | 134 | protected void HandleControlConnectionException(Exception x) 135 | { 136 | // Reset socket state to continue accepting new requests* 137 | controlSocket.Close(); 138 | controlSocket = new RequestSocket(); 139 | controlSocket.Connect(ControlEndpoint); 140 | 141 | // Let client know that something went wrong 142 | throw x; 143 | 144 | /* *This can be also done with ZMQ_REQ_RELAXED + ZMQ_REQ_CORRELATE 145 | options instead of brute-force-like restarting the socket 146 | but NetMQ does not seem to support these options */ 147 | } 148 | 149 | private string ControlEndpoint => $"tcp://{serverAddress}:38219"; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/remotes/ControllerRemote.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.remotes; 2 | 3 | import com.riftcat.vridge.api.client.java.proto.HandType; 4 | import com.riftcat.vridge.api.client.java.proto.HeadRelation; 5 | import com.riftcat.vridge.api.client.java.proto.VRController; 6 | import com.riftcat.vridge.api.client.java.proto.VRControllerAxis_t; 7 | import com.riftcat.vridge.api.client.java.proto.VRControllerState_t; 8 | import com.riftcat.vridge.api.client.java.proto.VRControllerState_tOrBuilder; 9 | import com.riftcat.vridge.api.client.java.proxy.ControllerProxy; 10 | import com.riftcat.vridge.api.client.java.utils.ButtonMask; 11 | 12 | import java.util.concurrent.TimeoutException; 13 | 14 | public class ControllerRemote extends RemoteBase { 15 | 16 | private static int packetNum = 0; 17 | private ControllerProxy proxy; 18 | 19 | ControllerRemote(ControllerProxy proxy) { 20 | super(proxy); 21 | this.proxy = proxy; 22 | } 23 | 24 | /** 25 | * Sets VR controller to a new state 26 | * @param controllerId Unique ID of given controller 27 | * @param headRelation How given pose relates to the head. Usually unrelated is the best pick. 28 | * @param orientation Orientation as XYZW float[4]. 29 | * @param position Position as XYZ float[3]. 30 | * @param analogX (-1,1) horizontal touchpad position. 31 | * @param analogY (-1,1) vertical touchpad position. 32 | * @param analogTrigger (0,1) trigger state (1 is fully pulled) 33 | * @param isMenuPressed 34 | * @param isSystemPressed 35 | * @param isTriggerPressed 36 | * @param isGripPressed 37 | * @param isTouchpadPressed 38 | * @param isTouchpadTouched 39 | */ 40 | public void setControllerState( 41 | // Controller ID 42 | int controllerId, 43 | HandType handType, 44 | boolean disableController, 45 | // Pose data 46 | HeadRelation headRelation, 47 | float[] orientation, 48 | float[] position, 49 | // Touchpad state [-1,1] 50 | double analogX, 51 | double analogY, 52 | 53 | // Trigger state 54 | double analogTrigger, 55 | 56 | // Button states 57 | boolean isMenuPressed, 58 | boolean isSystemPressed, 59 | boolean isTriggerPressed, 60 | boolean isGripPressed, 61 | boolean isTouchpadPressed, 62 | boolean isTouchpadTouched){ 63 | 64 | // See openvr.h in OpenVR SDK for mappings and masks 65 | // https://github.com/ValveSoftware/openvr/blob/master/headers/openvr.h 66 | 67 | long pressedMask = buildPressedMask(isMenuPressed, isSystemPressed, isTriggerPressed, isGripPressed, isTouchpadPressed); 68 | long touchedMask = buildTouchedMask(isTouchpadTouched, isTriggerPressed); 69 | 70 | int controllerStatus = 0; 71 | if(disableController == true) { 72 | controllerStatus = 2; 73 | } 74 | 75 | VRControllerState_tOrBuilder buttons = VRControllerState_t.newBuilder() 76 | .setRAxis0(VRControllerAxis_t.newBuilder() 77 | .setX((float) analogX) 78 | .setY((float) analogY)) 79 | .setRAxis1(VRControllerAxis_t.newBuilder() 80 | .setX((float) analogTrigger)) 81 | .setUlButtonPressed(pressedMask) 82 | .setUlButtonTouched(touchedMask) 83 | .setUnPacketNum(++packetNum); // Touchpad 84 | 85 | 86 | VRController.Builder controllerState = VRController.newBuilder() 87 | .setControllerId(controllerId) 88 | .addOrientation(orientation[0]) 89 | .addOrientation(orientation[1]) 90 | .addOrientation(orientation[2]) 91 | .addOrientation(orientation[3]) 92 | .setStatus(controllerStatus) 93 | .setSuggestedHand(handType) 94 | .setHeadRelation(headRelation) 95 | .setButtonState((VRControllerState_t.Builder) buttons); 96 | 97 | if(position != null){ 98 | controllerState.addPosition(position[0]); 99 | controllerState.addPosition(position[1]); 100 | controllerState.addPosition(position[2]); 101 | } 102 | 103 | try { 104 | proxy.sendControllerState(controllerState.build()); 105 | } catch (Exception e) { 106 | dispose(); 107 | } 108 | } 109 | 110 | /** Recenter head tracking. Works the same as pressing recenter hotkey as configured in VRidge settings. */ 111 | public void recenterHead(){ 112 | try{ 113 | proxy.recenterHead(); 114 | } 115 | catch (Exception e){ 116 | dispose(); 117 | } 118 | } 119 | 120 | private long buildTouchedMask(boolean isTouchpadTouched, boolean isTriggerTouched) { 121 | long mask = 0; 122 | if (isTouchpadTouched) mask |= ButtonMask.Touchpad; 123 | if (isTriggerTouched) mask |= ButtonMask.Trigger; 124 | return mask; 125 | } 126 | 127 | private long buildPressedMask( 128 | boolean isMenuPressed, 129 | boolean isSystemPressed, 130 | boolean isTriggerPressed, 131 | boolean isGripPressed, 132 | boolean isTouchpadPressed) { 133 | 134 | long mask = 0; 135 | if (isMenuPressed) mask |= ButtonMask.ApplicationMenu; 136 | if (isSystemPressed) mask |= ButtonMask.System; 137 | if (isTriggerPressed) mask |= ButtonMask.Trigger; 138 | if (isGripPressed) mask |= ButtonMask.Grip; 139 | if (isTouchpadPressed) mask |= ButtonMask.Touchpad; 140 | return mask; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Remotes/ControllerRemote.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Numerics; 5 | using System.Text; 6 | using VRE.Vridge.API.Client.Helpers; 7 | using VRE.Vridge.API.Client.Messages.BasicTypes; 8 | using VRE.Vridge.API.Client.Messages.OpenVR; 9 | using VRE.Vridge.API.Client.Messages.v3.Broadcast; 10 | using VRE.Vridge.API.Client.Messages.v3.Controller; 11 | using VRE.Vridge.API.Client.Proxy.Controller; 12 | using VRController = VRE.Vridge.API.Client.Messages.v3.Controller.VRController; 13 | 14 | namespace VRE.Vridge.API.Client.Remotes 15 | { 16 | public class ControllerRemote : RemoteBase 17 | { 18 | private uint packetNum = 0; 19 | 20 | internal ControllerRemote(ControllerProxy proxy) : base(proxy) 21 | { 22 | } 23 | 24 | /// 25 | /// Sends full controller state as defined by arguments. 26 | /// 27 | public void SetControllerState( 28 | // Controller ID 29 | int controllerId, 30 | 31 | // Pose data 32 | HeadRelation headRelation, 33 | HandType suggestedHand, 34 | Quaternion orientation, 35 | Vector3? position, 36 | 37 | // Touchpad state [-1,1] 38 | double analogX, 39 | double analogY, 40 | 41 | // Trigger state 42 | double analogTrigger, 43 | 44 | // Button states 45 | bool isMenuPressed, 46 | bool isSystemPressed, 47 | bool isTriggerPressed, 48 | bool isGripPressed, 49 | bool isTouchpadPressed, 50 | bool isTouchpadTouched) 51 | { 52 | 53 | // See openvr.h in OpenVR SDK for mappings and masks 54 | // https://github.com/ValveSoftware/openvr/blob/master/headers/openvr.h 55 | 56 | var buttons = new VRControllerState_t() 57 | { 58 | rAxis0 = new VRControllerAxis_t((float)analogX, (float)analogY), // Touchpad 59 | rAxis1 = new VRControllerAxis_t((float)analogTrigger, 0), // Trigger 60 | rAxis2 = new VRControllerAxis_t(0, 0), 61 | rAxis3 = new VRControllerAxis_t(0, 0), 62 | rAxis4 = new VRControllerAxis_t(0, 0), 63 | ulButtonPressed = BuildButtonPressedMask(isMenuPressed, isSystemPressed, isTriggerPressed, isGripPressed, isTouchpadPressed), 64 | ulButtonTouched = BuildButtonTouchedMask(isTouchpadTouched, true), 65 | unPacketNum = ++packetNum 66 | 67 | }; 68 | 69 | var controllerState = new VRController() 70 | { 71 | ButtonState = buttons, 72 | Status = 0, 73 | ControllerId = controllerId, 74 | Position = position.HasValue ? new[] { position.Value.X, position.Value.Y, position.Value.Z } : null, 75 | Orientation = new [] {orientation.X, orientation.Y, orientation.Z, orientation.W }, 76 | HeadRelation = headRelation, 77 | SuggestedHand = suggestedHand 78 | }; 79 | 80 | WrapTimeouts(() => { Proxy.SendControllerData(controllerState); }); 81 | } 82 | 83 | /// 84 | /// Recenter head tracking. Works the same as pressing recenter hotkey as configured in VRidge settings. 85 | /// 86 | public void RecenterHead() 87 | { 88 | 89 | WrapTimeouts(() => { Proxy?.RecenterHead(); }); 90 | 91 | } 92 | 93 | internal override void Dispose() 94 | { 95 | Proxy?.Disconnect(); 96 | base.Dispose(); 97 | } 98 | 99 | /// 100 | /// Maps button pressed state into OpenVR packed ulong. 101 | /// 102 | private ulong BuildButtonPressedMask( 103 | bool isMenuPressed, 104 | bool isSystemPressed, 105 | bool isTriggerPressed, 106 | bool isGripPressed, 107 | bool isTouchpadPressed) 108 | { 109 | ulong mask = 0; 110 | 111 | if (isMenuPressed) mask |= ButtonMask.ApplicationMenu; 112 | if (isSystemPressed) mask |= ButtonMask.System; 113 | if (isTriggerPressed) mask |= ButtonMask.Trigger; 114 | if (isGripPressed) mask |= ButtonMask.Grip; 115 | if (isTouchpadPressed) mask |= ButtonMask.Touchpad; 116 | 117 | return mask; 118 | } 119 | 120 | 121 | /// 122 | /// Maps button touched state into OpenVR packed ulong. 123 | /// 124 | private ulong BuildButtonTouchedMask(bool isTouchpadTouched, bool isTriggerTouched) 125 | { 126 | ulong mask = 0; 127 | 128 | if (isTouchpadTouched) mask |= ButtonMask.Touchpad; 129 | if (isTriggerTouched) mask |= ButtonMask.Trigger; 130 | 131 | return mask; 132 | } 133 | 134 | /// 135 | /// Builds 4x4 combined matrix for given translation and rotation. 136 | /// Not used and not recommended anymore. Send orientation[+position] instead of matrix. 137 | /// 138 | private Matrix4x4 BuildControllerMatrix(double x, double y, double z, double yaw, double pitch, double roll) 139 | { 140 | Matrix4x4 m = Matrix4x4.Identity; 141 | 142 | 143 | m *= Matrix4x4.CreateFromYawPitchRoll( 144 | (float)MathHelpers.DegToRad(yaw), 145 | (float)MathHelpers.DegToRad(pitch), 146 | (float)MathHelpers.DegToRad(roll)); 147 | 148 | m*= Matrix4x4.CreateTranslation((float)x, (float)y, (float)z); 149 | 150 | return m; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/APIClient.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java; 2 | 3 | import com.riftcat.vridge.api.client.java.control.ControlRequestCode; 4 | import com.riftcat.vridge.api.client.java.control.ControlResponseCode; 5 | import com.riftcat.vridge.api.client.java.control.requests.ControlRequestHeader; 6 | import com.riftcat.vridge.api.client.java.control.requests.RequestEndpoint; 7 | import com.riftcat.vridge.api.client.java.control.responses.APIStatus; 8 | import com.riftcat.vridge.api.client.java.control.responses.EndpointCreated; 9 | import com.riftcat.vridge.api.client.java.proxy.BroadcastProxy; 10 | import com.riftcat.vridge.api.client.java.proxy.ClientProxyBase; 11 | import com.riftcat.vridge.api.client.java.proxy.ControllerProxy; 12 | import com.riftcat.vridge.api.client.java.proxy.HeadTrackingProxy; 13 | import com.riftcat.vridge.api.client.java.proxy.VRidgeApiProxy; 14 | import com.riftcat.vridge.api.client.java.utils.ILog; 15 | import com.riftcat.vridge.api.client.java.utils.SocketHelpers; 16 | 17 | import org.zeromq.ZContext; 18 | import org.zeromq.ZMQ; 19 | 20 | import java.util.HashMap; 21 | import java.util.LinkedList; 22 | import java.util.concurrent.TimeoutException; 23 | 24 | public class APIClient { 25 | 26 | public final static int HEADTRACKING = 0; 27 | public final static int CONTROLLER = 1; 28 | public final static int BROADCASTS = 2; 29 | 30 | public static ZContext ZContext; 31 | 32 | private HashMap proxies; 33 | private String serverAddress = "tcp://localhost"; 34 | 35 | // Connections with same app name will not result in "endpoint in use" response 36 | private String appName = ""; 37 | 38 | public APIClient(String appName){ 39 | ZContext = new ZContext(4); 40 | proxies = new HashMap(); 41 | 42 | this.appName = appName; 43 | } 44 | 45 | public APIClient(String ip, String appName){ 46 | this(appName); 47 | serverAddress = ip; 48 | } 49 | 50 | /// 51 | /// Sends control request to see what APIs are available. 52 | /// May return null if control connection dies (automatic reconnect will follow). 53 | /// 54 | public APIStatus GetStatus() throws Exception { 55 | 56 | ZMQ.Socket controlSocket = createControlSocket(); 57 | if (controlSocket == null) 58 | { 59 | return null; 60 | } 61 | 62 | SocketHelpers.SendAsJson(controlSocket, new ControlRequestHeader(ControlRequestCode.RequestStatus)); 63 | APIStatus status = SocketHelpers.ReceiveByJson(controlSocket, APIStatus.class); 64 | APIClient.ZContext.destroySocket(controlSocket); 65 | 66 | if (status != null){ 67 | return status; 68 | } 69 | 70 | throw new Exception("Could not read API status."); 71 | } 72 | 73 | public T getProxy(int proxyType) throws Exception { 74 | 75 | VRidgeApiProxy proxy = proxies.get(proxyType); 76 | ZMQ.Socket controlSocket = createControlSocket(); 77 | 78 | if(proxy == null){ 79 | 80 | String endpointName = null; 81 | switch (proxyType) 82 | { 83 | case HEADTRACKING: 84 | endpointName = EndpointNames.HeadTracking; 85 | break; 86 | case CONTROLLER: 87 | endpointName = EndpointNames.Controller; 88 | break; 89 | case BROADCASTS: 90 | endpointName = EndpointNames.Broadcast; 91 | break; 92 | } 93 | 94 | if(endpointName == null){{ 95 | throw new IllegalArgumentException("Invalid proxy type was requested."); 96 | }} 97 | 98 | SocketHelpers.SendAsJson(controlSocket, new RequestEndpoint(endpointName, appName)); 99 | EndpointCreated response = SocketHelpers.ReceiveByJson(controlSocket, EndpointCreated.class); 100 | APIClient.ZContext.destroySocket(controlSocket); 101 | 102 | if(response == null ){ 103 | throw new TimeoutException("API server timeout"); 104 | } 105 | 106 | if(response.Code == ControlResponseCode.InUse){ 107 | throw new Exception("API endpoint in use."); 108 | } 109 | 110 | switch (proxyType){ 111 | case HEADTRACKING: 112 | proxies.put(proxyType, new HeadTrackingProxy("tcp://" + serverAddress + ":" + response.Port, true)); 113 | break; 114 | case CONTROLLER: 115 | proxies.put(proxyType, new ControllerProxy("tcp://" + serverAddress + ":" + response.Port)); 116 | break; 117 | case BROADCASTS: 118 | proxies.put(proxyType, new BroadcastProxy("tcp://" + serverAddress + ":" + response.Port)); 119 | break; 120 | } 121 | } 122 | 123 | return (T) proxies.get(proxyType); 124 | } 125 | 126 | public void disconnectProxy(int proxyType) 127 | { 128 | VRidgeApiProxy proxy = proxies.get(proxyType); 129 | 130 | if (proxy == null) return; 131 | 132 | proxy.disconnect(); 133 | proxies.put(proxyType, null); 134 | } 135 | 136 | public void disconnectAll() { 137 | for(int proxyId : proxies.keySet()){ 138 | disconnectProxy(proxyId); 139 | } 140 | } 141 | 142 | private String getEndpointAddress() { 143 | 144 | return "tcp://" + serverAddress + ":38219"; 145 | } 146 | 147 | private ZMQ.Socket createControlSocket(){ 148 | String ctrlAddress = getEndpointAddress(); 149 | 150 | ZMQ.Socket controlSocket = ZContext.createSocket(ZMQ.REQ); 151 | controlSocket.connect(ctrlAddress); 152 | controlSocket.setSendTimeOut(1000); 153 | controlSocket.setReceiveTimeOut(1000); 154 | return controlSocket; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quick description 2 | 3 | VRidge API is a way to interact with VRidge in any language of your choice. We use ZeroMQ sockets as a low latency transport. It can work both locally and remotely through network. 4 | 5 | VRidge API allows you to write full or partial head tracking data, read mobile tracking data and set offsets to correct drift or sync it to your coordinate system. You can also send controller states (buttons, triggers, touchpads), and transforms (motion controller orientation and position in 3D space). 6 | 7 | Everything is loaded as single VRidge OpenVR driver. Some of the above things can be done through writing separate OpenVR drivers but VRidge API has benefits of two-way communications with an ability to mix coordinate systems in a more elastic way. It is also more stable than separate OpenVR drivers potentially providing conflicting data. 8 | 9 | # Getting started 10 | 11 | ## General requirements 12 | * ZeroMQ (or any lib that communicate with ZMQ sockets) 13 | * JSON serializer 14 | 15 | VRidge API is accessible over TCP, abstracted by ZeroMQ sockets. This allows you to use API any language that you choose. See [ZeroMQ language bindings](http://zeromq.org/bindings:_start) page and get library for language of your choice. 16 | 17 | ## Requirements for this example 18 | 19 | This repository contains example implementation of API client in .NET. You can use it in your .NET projects by simply building and referencing the APIClient project. 20 | 21 | C# projects require: 22 | 23 | * NetMQ 24 | * Newtonsoft.Json 25 | * .NET Standard 2.0 compatible project - one of the following 26 | * .NET Framework 4.6.1 + 27 | * .NET Core 2.0 + 28 | * Mono 5.4 + 29 | * UWP 10.0.16299 + 30 | * Xamarin 31 | * iOS 10.14 + 32 | * Mac 3.8 33 | * Android 8.0 + 34 | * MvvmLight (only for WPF example project - /src/examples/VRE.Vridge.API.DesktopClient) 35 | 36 | Both are referenced as NuGet packages in .csproj and will be restored on pre-build. 37 | 38 | # API usage 39 | 40 | ## Simple fire-and-forget access 41 | If you don't want to manage connection state, you can use fire-and-forget access layer which manages the connection state for you. See [VridgeRemote](https://github.com/RiftCat/vridge-api/wiki/VridgeRemote-(fire-and-forget-layer)) for more details. 42 | 43 | ## Advanced usage 44 | 45 | ### Control endpoint 46 | 47 | This endpoint provides two functions: 48 | 49 | - You can find out what API endpoints are available and wether they are currently in use or not. 50 | 51 | - You can connect to specific API endpoint by sending a packet with requested endpoint name and your version number. This will open a listener on the server side compatible with your requested version. Response message will contain ip:port endpoint that can be connected. 52 | 53 | See [control channel wiki page](https://github.com/RiftCat/vridge-api/wiki/Control-channel) for details. 54 | 55 | ### Data endpoints 56 | 57 | Data channels are used to interact with VRidge and send/receive actual data. Currently we have two endpoints. See pages below for details. 58 | 59 | Controller API allows you to send VR motion controller state without writing a full OpenVR driver. This allows a more stable experience since only one driver is loaded in SteamVR. 60 | 61 | See [Controller API](https://github.com/RiftCat/vridge-api/wiki/Controller-API) for protocol and details. 62 | 63 | Head tracking endpoint allows you to control head tracking in a variety of modes. You can use it to provide positional, rotational or combined data. You can also read mobile sensor data and provide an offset. You can also modify phone tracking data in real time before it's used for rendering in VR. 64 | 65 | See [Head Tracking API](https://github.com/RiftCat/vridge-api/wiki/Head-Tracking-API) for protocol and available modes 66 | 67 | Broadcast endpoint carries one-way notifications. Currently only haptic pulses are propagated through broadcast channel. It is implemented as ZMQ PUB-SUB sockets. 68 | 69 | See [Listening to broadcasts](https://github.com/RiftCat/vridge-api/wiki/Listening-to-haptic-feedback). 70 | 71 | ## Changelog 72 | 73 | ### API v3.1 (VRidge 2.3+) 74 | 75 | #### Additions 76 | * Added Java API client. 77 | * Added VridgeRemote class as a fire-and-forget access layer to use API without managing connection state. 78 | * Added a way to remap 3DOF controllers into 6DOF controllers, attached to head. 79 | * Added HeadRelation which configures how the pose is affected by recenter calls. Default "Unrelated" should cover most cases. 80 | * Added discovery UDP broadcast which lets you find active VRidge servers on the local network. 81 | 82 | 83 | #### Changes 84 | 85 | * Removed async offset's pitch and roll axes. Old calls will discard pitch and roll data and only use yaw offset. [Discussion](https://github.com/RiftCat/vridge-api/issues/15). 86 | * When using reprojection-enabled device, rotational head tracking input will be discarded. External rotational data doesn't play well with devices expecting to use late reprojection based on their own sensor data. 87 | * Controllers will now disappear after 5 seconds without new data. 88 | * Deprecated OrientationMatrix as pose format. Added separate rotation quaternion and position vector instead. Removes ambiguity of pose matrix format and byte layout. Old format still works. 89 | * C# API client now multi-targets .NET Standard 2.0 and .NET Framework 4.7. 90 | 91 | 92 | 93 | 94 | 95 | 96 | ### API v3 (VRidge 2.0+) 97 | * Added velocity and acceleration to controller data packets. 98 | * Migrated protocol to Protobuf serialization scheme to make it easier to write cross-platform code. 99 | * Migrated client library to .NET Standard 2.0 to make the SDK cross-platform. 100 | * Updated WPF example to use v3. 101 | * Fixed a bug that prevented remote connections without the issue #6 workaround. 102 | * Removed v1 and v2 definitions from the project. VRidge runtime still supports projects using those versions but we recommend using v3 for new projects. 103 | 104 | ### API v2 (VRidge 1.5) 105 | 106 | * Added haptic pulses as PUB-SUB sockets. 107 | * Added recenter head tracking call to mirror recenter hotkey function. 108 | * Added ChangeState to flag HMD as inside/outside of tracking range. 109 | -------------------------------------------------------------------------------- /src/java/VRE.Vridge.API/VRE.Vridge.API.Client/src/main/java/com/riftcat/vridge/api/client/java/proxy/HeadTrackingProxy.java: -------------------------------------------------------------------------------- 1 | package com.riftcat.vridge.api.client.java.proxy; 2 | 3 | import com.google.protobuf.ByteString; 4 | import com.riftcat.vridge.api.client.java.codes.HeadTrackingRequestCodes; 5 | import com.riftcat.vridge.api.client.java.codes.HeadTrackingResponseCodes; 6 | import com.riftcat.vridge.api.client.java.codes.TrackedDeviceStatus; 7 | import com.riftcat.vridge.api.client.java.proto.HeadTrackingRequest; 8 | import com.riftcat.vridge.api.client.java.proto.HeadTrackingResponse; 9 | import com.riftcat.vridge.api.client.java.proto.TrackedPose; 10 | import com.riftcat.vridge.api.client.java.utils.APILogger; 11 | import com.riftcat.vridge.api.client.java.utils.SerializationUtils; 12 | 13 | import java.nio.ByteBuffer; 14 | import java.nio.ByteOrder; 15 | import java.util.concurrent.TimeoutException; 16 | 17 | public class HeadTrackingProxy extends ClientProxyBase { 18 | 19 | // This is only partial implementation of API calls 20 | 21 | public HeadTrackingProxy(String endpointAddress, boolean shouldKeepAlive){ 22 | super(endpointAddress, shouldKeepAlive); 23 | } 24 | 25 | /** 26 | * Sets head position to new location. 27 | */ 28 | public boolean setPosition(float x, float y, float z) throws TimeoutException { 29 | HeadTrackingRequest request = HeadTrackingRequest 30 | .newBuilder() 31 | .setVersion(CurrentVersion) 32 | .setTaskType(HeadTrackingRequestCodes.SendPositionOnly) 33 | .setData(SerializationUtils.byteStringFromFloats(x, y, z)) 34 | .build(); 35 | 36 | HeadTrackingResponse reply = sendMessage(request); 37 | 38 | return reply.getReplyCode() == HeadTrackingResponseCodes.AcceptedYourData; 39 | } 40 | 41 | /** 42 | Sets position and rotation and returns true if the value was accepted. 43 | This won't work for headsets with reprojection enabled. 44 | */ 45 | public boolean setRotationAndPosition(float yaw, float pitch, float roll, float x, float y, float z) throws TimeoutException { 46 | HeadTrackingRequest request = HeadTrackingRequest 47 | .newBuilder() 48 | .setVersion(CurrentVersion) 49 | .setTaskType(HeadTrackingRequestCodes.SendRadRotationAndPosition) 50 | .setData(SerializationUtils.byteStringFromFloats(pitch, yaw, roll, x, y, z)) 51 | .build(); 52 | 53 | HeadTrackingResponse reply = sendMessage(request); 54 | 55 | return reply.getReplyCode() == HeadTrackingResponseCodes.AcceptedYourData; 56 | } 57 | 58 | /** 59 | Sets position and rotation and returns true if the value was accepted. 60 | This won't work for headsets with reprojection enabled. 61 | */ 62 | public boolean setRotationAndPosition(float quatQ, float quatY, float quatZ, float quatW, float posX, float posY, float posZ) throws TimeoutException { 63 | HeadTrackingRequest request = HeadTrackingRequest 64 | .newBuilder() 65 | .setVersion(CurrentVersion) 66 | .setTaskType(HeadTrackingRequestCodes.SendQuatRotationAndPosition) 67 | .setData(SerializationUtils.byteStringFromFloats(quatQ, quatY, quatZ, quatW, posX, posY, posZ)) 68 | .build(); 69 | 70 | HeadTrackingResponse reply = sendMessage(request); 71 | 72 | return reply.getReplyCode() == HeadTrackingResponseCodes.AcceptedYourData; 73 | } 74 | 75 | /** 76 | * Reorients tracking system and sets new center to current head direction. 77 | */ 78 | public boolean recenterView() throws TimeoutException { 79 | HeadTrackingRequest request = HeadTrackingRequest 80 | .newBuilder() 81 | .setVersion(CurrentVersion) 82 | .setTaskType(HeadTrackingRequestCodes.Recenter) 83 | .build(); 84 | 85 | HeadTrackingResponse reply = sendMessage(request); 86 | 87 | return reply.getReplyCode() == HeadTrackingResponseCodes.AcceptedYourData; 88 | } 89 | 90 | 91 | /** 92 | * Gets current head pose and related offsets. 93 | */ 94 | public TrackedPose getCurrentPose() throws TimeoutException { 95 | 96 | HeadTrackingRequest request = HeadTrackingRequest 97 | .newBuilder() 98 | .setVersion(CurrentVersion) 99 | .setTaskType(HeadTrackingRequestCodes.RequestReadOnlyPose) 100 | .build(); 101 | 102 | HeadTrackingResponse reply = sendMessage(request); 103 | 104 | if(reply.getReplyCode() == HeadTrackingResponseCodes.SendingCurrentTrackedPose){ 105 | return reply.getTrackedPose(); 106 | } 107 | else{ 108 | return null; 109 | } 110 | } 111 | 112 | public void setYawOffset(float yaw) throws TimeoutException { 113 | HeadTrackingRequest request = HeadTrackingRequest 114 | .newBuilder() 115 | .setVersion(CurrentVersion) 116 | .setTaskType(HeadTrackingRequestCodes.SetYawOffset) 117 | .setData(SerializationUtils.byteStringFromFloats(yaw)) 118 | .build(); 119 | 120 | sendMessage(request); 121 | } 122 | 123 | public void changeTrackingState(boolean isInTrackingRange) throws TimeoutException { 124 | byte[] state = new byte[1]; 125 | state[0] = isInTrackingRange ? TrackedDeviceStatus.Active : TrackedDeviceStatus.TempUnavailable; 126 | HeadTrackingRequest request = HeadTrackingRequest 127 | .newBuilder() 128 | .setVersion(CurrentVersion) 129 | .setTaskType(HeadTrackingRequestCodes.ChangeState) 130 | .setData(ByteString.copyFrom(state)) 131 | .build(); 132 | 133 | sendMessage(request); 134 | } 135 | 136 | @Override 137 | public void disconnect() { 138 | HeadTrackingRequest disconnectRequest = HeadTrackingRequest.newBuilder() 139 | .setVersion(CurrentVersion) 140 | .setTaskType(HeadTrackingRequestCodes.Disconnect) 141 | .build(); 142 | 143 | try{ 144 | sendMessage(disconnectRequest); 145 | } 146 | catch (Exception x){ 147 | // ignored 148 | } 149 | 150 | CloseSocket(); 151 | } 152 | 153 | private HeadTrackingResponse sendMessage(HeadTrackingRequest req) throws TimeoutException { 154 | return SendMessage(req, HeadTrackingResponse.parser()); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/VRE.Vridge.API.DesktopTester.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {97ECBE1C-03EF-461D-A389-312C729A7EA6} 8 | WinExe 9 | Properties 10 | VRE.Vridge.API.DesktopTester 11 | Vridge API Tester 12 | v4.7 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | true 17 | SAK 18 | SAK 19 | SAK 20 | SAK 21 | 22 | 23 | 24 | AnyCPU 25 | true 26 | full 27 | false 28 | bin\Debug\ 29 | DEBUG;TRACE 30 | prompt 31 | 4 32 | 33 | 34 | AnyCPU 35 | pdbonly 36 | true 37 | bin\Release\ 38 | TRACE 39 | prompt 40 | 4 41 | 42 | 43 | icon.ico 44 | 45 | 46 | 47 | ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\GalaSoft.MvvmLight.dll 48 | True 49 | 50 | 51 | ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\GalaSoft.MvvmLight.Extras.dll 52 | True 53 | 54 | 55 | ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\GalaSoft.MvvmLight.Platform.dll 56 | True 57 | 58 | 59 | ..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll 60 | True 61 | 62 | 63 | 64 | 65 | 66 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 67 | 68 | 69 | ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\System.Windows.Interactivity.dll 70 | True 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 4.0 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | MSBuild:Compile 88 | Designer 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | LabeledSlider.xaml 98 | 99 | 100 | MSBuild:Compile 101 | Designer 102 | 103 | 104 | App.xaml 105 | Code 106 | 107 | 108 | 109 | ControlWindow.xaml 110 | Code 111 | 112 | 113 | Designer 114 | MSBuild:Compile 115 | 116 | 117 | 118 | 119 | Code 120 | 121 | 122 | True 123 | True 124 | Resources.resx 125 | 126 | 127 | True 128 | Settings.settings 129 | True 130 | 131 | 132 | ResXFileCodeGenerator 133 | Resources.Designer.cs 134 | 135 | 136 | Designer 137 | 138 | 139 | SettingsSingleFileGenerator 140 | Settings.Designer.cs 141 | 142 | 143 | 144 | 145 | 146 | Designer 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | {11fb8f50-ae40-42c2-ac82-7a8d84a7b76e} 158 | VRE.Vridge.API.Client 159 | 160 | 161 | 162 | 169 | -------------------------------------------------------------------------------- /src/csharp/examples/VRE.Vridge.API.DesktopClient/View/ControlWindow.xaml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 34 | 43 | 44 | 45 | 46 | 47 | 53 | 60 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/csharp/VRE.Vridge.API.Client/Proxy/HeadTracking/HeadTrackingProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using NetMQ; 4 | using VRE.Vridge.API.Client.Helpers; 5 | using VRE.Vridge.API.Client.Messages.v3; 6 | using VRE.Vridge.API.Client.Messages.v3.HeadTracking.Requests; 7 | using VRE.Vridge.API.Client.Messages.v3.HeadTracking.Responses; 8 | using VRE.Vridge.API.Client.Remotes; 9 | 10 | namespace VRE.Vridge.API.Client.Proxy.HeadTracking 11 | { 12 | /// 13 | /// Lower level access method for sending head tracking data to VRidge. Consider using 14 | /// with for easier operation. 15 | /// 16 | public class HeadTrackingProxy : ClientProxyBasePB 17 | { 18 | private const int CurrentVersion = 3; 19 | 20 | public Action NewSyncDataAvailable; 21 | public event EventHandler SyncModeDisconnected; 22 | 23 | private bool isProvidingOffset; 24 | private Thread offsetThread; 25 | 26 | /// 27 | /// Creates head tracking proxy and establishes connection. 28 | /// 29 | /// 30 | /// Endpoint address (ip:port). Should be requested from control connection. 31 | /// 32 | /// 33 | /// True if automatic pings should keep connection alive even if caller doesn't send data. 34 | /// 35 | public HeadTrackingProxy(string endpointAddress, bool keepAlive = false) : base(endpointAddress, keepAlive) { } 36 | 37 | /// 38 | /// Sets position only and returns true if the value was accepted. 39 | /// 40 | public bool SetPosition(float x, float y, float z) 41 | { 42 | var request = HeadTrackingRequest.CreatePositionPacket(x, y, z); 43 | var reply = SendMessage(request); 44 | 45 | return reply.ReplyCode == (int) HeadTrackingResponse.Response.AcceptedYourData; 46 | } 47 | 48 | /// 49 | /// Sets position and rotation and returns true if the value was accepted. 50 | /// 51 | public bool SetRotationAndPosition(float yaw, float pitch, float roll, float x, float y, float z) 52 | { 53 | var request = HeadTrackingRequest.CreateRotationPositionVectorPacket(yaw, pitch, roll, x, y, z); 54 | var reply = SendMessage(request); 55 | 56 | return reply.ReplyCode == (int)HeadTrackingResponse.Response.AcceptedYourData; 57 | } 58 | 59 | /// 60 | /// Sets rotational offset that will be applied to each mobile pose. Use radians. 61 | /// 62 | public bool SetAsyncOffset(float yaw) 63 | { 64 | var request = HeadTrackingRequest.CreateAsyncOffsetPacket(yaw); 65 | var reply = SendMessage(request); 66 | 67 | return reply.ReplyCode == (byte)HeadTrackingResponse.Response.AcceptedYourData; 68 | } 69 | 70 | /// 71 | /// Clear async offset that was set with 72 | /// 73 | /// 74 | public bool ResetAsyncOffset() 75 | { 76 | var request = HeadTrackingRequest.CreateEmptyPacketByType(HeadTrackingRequest.Task.ResetYawOffset); 77 | var reply = SendMessage(request); 78 | 79 | return reply.ReplyCode == (byte) HeadTrackingResponse.Response.AcceptedYourData; 80 | } 81 | 82 | /// 83 | /// Sets current orientation as new center. 84 | /// 85 | public bool RecenterView() 86 | { 87 | var request = HeadTrackingRequest.CreateRecenterPacket(); 88 | var reply = SendMessage(request); 89 | 90 | return reply.ReplyCode == (byte)HeadTrackingResponse.Response.AcceptedYourData; 91 | } 92 | 93 | /// 94 | /// Toggles HMD tracking state between "actively tracked" and "out of tracking range". 95 | /// 96 | public bool ChangeTrackingState(bool isCurrentlyBeingTracked) 97 | { 98 | var request = 99 | HeadTrackingRequest.CreateStateChangePacket(isCurrentlyBeingTracked 100 | ? TrackedDeviceStatus.Active 101 | : TrackedDeviceStatus.TempUnavailable); 102 | 103 | var reply = SendMessage(request); 104 | 105 | return reply.ReplyCode == (byte)HeadTrackingResponse.Response.AcceptedYourData; 106 | } 107 | 108 | /// 109 | /// Request latest phone pose matrix. You can use it for . 110 | /// This method will block until fresh data is received from mobile phone. 111 | /// 112 | /// 113 | /// 4x4 transformation matrix flattened as column-major array. 114 | /// 115 | public float[] GetCurrentPhonePose() 116 | { 117 | var request = HeadTrackingRequest.CreateEmptyPacketByType(HeadTrackingRequest.Task.RequestReadOnlyPhonePose); 118 | var reply = SendMessage(request); 119 | 120 | var replyData = new float[16]; 121 | 122 | Array.Copy(reply.Data, 0, replyData, 0, 16); 123 | return replyData; 124 | } 125 | 126 | /// 127 | /// Begins listening to mobile phone tracking data. You callback method will be called with modifiable tracking data. 128 | /// For example, you can correct drift by mixing phone tracking data with yours in real time. 129 | /// 130 | /// 131 | /// Method that will be called with a reference to a column-major phone 4x4 pose matrix. 132 | /// You can modify the array but you need to do it fast to keep latency low. 133 | /// 134 | public void BeginSyncOffset(Action callback) 135 | { 136 | NewSyncDataAvailable = callback; 137 | 138 | isProvidingOffset = true; 139 | offsetThread = new Thread(SyncOffsetLoop) 140 | { 141 | IsBackground = true 142 | }; 143 | offsetThread.Start(); 144 | } 145 | 146 | /// 147 | /// Stops listening to mobile phone tracking data. Use it after you no longer 148 | /// need sync offset mode started by 149 | /// 150 | public void StopSyncOffset() 151 | { 152 | NewSyncDataAvailable = null; 153 | 154 | isProvidingOffset = false; 155 | if(offsetThread != null && offsetThread.IsAlive) 156 | offsetThread.Join(); 157 | } 158 | 159 | /// 160 | /// Disconnected from head tracking API and frees the API for other clients. 161 | /// 162 | public void Disconnect() 163 | { 164 | isProvidingOffset = false; 165 | 166 | var disconnectRequest = new HeadTrackingRequest() 167 | { 168 | Version = CurrentVersion, 169 | TaskType = (byte) HeadTrackingRequest.Task.Disconnect, 170 | }; 171 | 172 | try 173 | { 174 | SendMessage(disconnectRequest); 175 | } 176 | catch (TimeoutException x) 177 | { 178 | // Connection probably dropped another way, ignoring 179 | } 180 | catch (FiniteStateMachineException x) 181 | { 182 | // Connection state invalid, close anyway 183 | } 184 | CloseSocket(); 185 | } 186 | 187 | private void SyncOffsetLoop() 188 | { 189 | HeadTrackingRequest reqModifiable = new HeadTrackingRequest() 190 | { 191 | Version = CurrentVersion, 192 | TaskType = (byte)HeadTrackingRequest.Task.RequestSyncOffset 193 | }; 194 | 195 | try 196 | { 197 | var reqFrame = SerializationHelpers.ProtoSerialize(reqModifiable); 198 | 199 | while (isProvidingOffset) 200 | { 201 | // Get current data 202 | var response = SendRawFrame(reqFrame); 203 | 204 | if (response.ReplyCode == (int) HeadTrackingResponse.Response.PhoneDataTimeout) 205 | { 206 | throw new TimeoutException("Phone is not sending new data."); 207 | } 208 | 209 | var currentPhonePose = new float[16]; 210 | Array.Copy(response.Data, currentPhonePose, 16); 211 | 212 | // Notify through callback so listeners can modify the array 213 | NewSyncDataAvailable?.Invoke(currentPhonePose); 214 | 215 | // Send updated data 216 | SendMessage(HeadTrackingRequest.CreateFullPoseMatrixPacket(currentPhonePose)); 217 | } 218 | } 219 | catch (Exception x) 220 | { 221 | // Signal it callers if anyone is listening 222 | SyncModeDisconnected?.Invoke(this, x); 223 | Disconnect(); 224 | } 225 | } 226 | 227 | private HeadTrackingResponse SendMessage(object obj) => SendMessage(obj); 228 | 229 | 230 | } 231 | } 232 | --------------------------------------------------------------------------------