Here arises a binding of the OpenVR-API for the Julia programming language. "OpenVR is a software development kit and application programming interface developed by Valve for supporting the SteamVR (HTC Vive) and other virtual reality headset devices".1 Currently the single implementation of the open OpenVR-API indeed is Valve's proprietary SteamVR which most likely supports the HTC Vive family of head mounted devices (HMD). Note that on March 18, 2019 also a pre-release of the OpenXR standard (Provisional Specification) appeared which aims to provide a "cross-platform standard, [for] VR and AR applications and engines".
The C++ based API itself comes in a single file openvr.h where also a JSON representation, a C-binding and a C#-binding are located. The C and C++ version of the API do not differ that much and especially the Julia binding shows more similarity with the OpenVR C-API. Still, the current Julia-binding was created for the C++API using CxxWrap.jl. There is an API-Documentation and samples for OpenGL, Vulkan and DX12.
The hellovr_opengl example was ported to Julia as a proof of concept. An OpenGL context is created by SDL2 in the example, but this could be also done by GLFW or others. CxxWrap.jl helped a lot for gradually porting this C++ example, function after function, allowing to use the Julia and C++ functions exchangably. Our completed port of the hellovr_opengl example does not rely on the original C++ anymore and no struct-layout compatibility needs to be maintained. Therefore it should be easier to replace the OpenVR C++API binding with a C-API binding for that ported example, removing the necessity of compiling a CxxWrap-shared-library.
The hellovr_opengl example hellovr_opengl_main.cpp is a very verbose piece of C++ and a little misleading. Nonetheless it is ported to hellovr_opengl_julia.jl and features
- rendering setup and submit of OpenGL textures to OpenVR:
- VR_Init
- GetRecommendedRenderTargetSize
- Submit
- VR_Shutdown
- getting the current transformation data for the controllers, the head mounted display and each eye:
- GetEyeToHeadTransform
- GetPoseActionData
- GetProjectionMatrix
- controller interaction setup:
- SetActionManifestPath
- GetActionHandle
- GetActionSetHandle
- GetInputSourceHandle
- GetStringTrackedDeviceProperty
- GetOriginTrackedDeviceInfo
- GetTrackedDeviceClass
- getting render models of the controllers for the purpose of rendering them (manually):
- GetRenderModelErrorNameFromEnum
- LoadRenderModel_Async
- LoadTexture_Async
- FreeRenderModel
- FreeTexture
- processing events:
- PollNextEvent
- IsInputAvailable
- UpdateActionState
- GetAnalogActionData
- GetDigitalActionData
- VREvent_TrackedDeviceDeactivated
- VREvent_TrackedDeviceUpdated
- force feedback:
- TriggerHapticVibrationAction
TODO: Currently the code contains lots of remains of the memory-layout compatible version that can be omitted by using just normal, mutable Julia structs and Julia arrays.
C++ makes use of the Resource acquisition is initialization idiom for it's objects[regions of storage] and has notions for lifetime, storage duration and copyability[POD],[non-trivial for the purposes of calls],[POD for the purpose of layout] of such.
Julia instead uses a garbage collector, has a notion of bitstype[is garbage collected], passes values of non-bitstype by reference (call by sharing) and it does not move already allocated objects[regions of storage].
This becomes a lesser issue since OpenVR - as well as most C-API's - does not frequently allocate memory. It requires already-alloced memory for output to be passed in as pointers.
The OpenVR-API makes use of two C/C++-Unions: for VREvent_Data_t and VROverlayIntersectionMaskPrimitive_Data_t. The knowledge of which data really is present in the union only arises from VREvent_t and VROverlayIntersectionMaskPrimitive_t respectively which contain these unions. A function getUnion is provided that implements an interpretation of the comments from the original header file.
C (and therefore C++) "is" row-major where Julia "is" column-major. Although one can implement any-major matrices - e.g. even the C++ hellovr_opengl example implements its own column-major matrices in C++ - the OpenVR-API makes use of this in the definition of HmdMatrix34_t, HmdMatrix33_t and HmdMatrix44_t. Therefore, when layout matching them with an SArray from StaticArrays.jl, we observe the transposed matrix in Julia.
Julia structs follow slightly different alignment rules than C structs. This lead to splitting a UInt64 into two UInt32 for layout conformance e.g. for
- RenderModel_TextureMap_t.rubTextureMapData
- RenderModel_t.rIndexData