/OpenVR.jl

OpenVR bindings for Julia

Primary LanguageJuliaOtherNOASSERTION

OpenVR.jl

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".

[1] Wikipedia

Documentation and examples

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.

hellovr_opengl example

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.

Expected issues

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.

Less known issues

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