openhmd - Generic VR interface using OpenHMD

Classes and functions for using various VR devices (HMDs, controllers, and generic spatial trackers) through the OpenHMD driver interface.

OpenHMD is a free and open-source software (FOSS) project which aims to provide a driver interface for a variety of VR headsets, controllers and trackers. This interface is under heavy development (both with PsychXR and OpenHMD itself) so expect things to change without notice between releases of PsychXR.

This module isn’t a simple wrapper around OpenHMD. The design of this OpenHMD Python API is intended to be similar (to the extent that is feasible) to that of LibOVR. Therefore, using the OpenHMD API in PsychXR is quite different than using the C API. Where the C API uses getter and setter functions for nearly everything, the PsychXR interface uses classes and functions analogous to those in the LibOVR driver library. Unlike LibOVR, OpenHMD does not come with a compositor. Therefore, the user must implement their own system to present scenes to the display (ex. using Pyglet or GLFW). This requires considerably more effort on behalf of the user than LibOVR, but may offer greater flexibility for those that need it by removing the “black box” between the application and the display.

This library is only available if PsychXR was built with the environment variable ``PSYCHXR_BUILD_OPENHMD=1`` being previously set. Be aware that this module is currently in the alpha phase of development and may be incomplete and buggy.

Overview

Classes

OHMDDeviceInfo Information class for OpenHMD devices.

Functions

OHMDDeviceInfo Information class for OpenHMD devices.
success(int result) Check if a function returned successfully.
failure(int result) Check if a function returned an error.
getVersion() Get the version of the OpenHMD library presently loaded.
create() Create a new OpenHMD context/session.
destroy() Destroy the current context/session.
probe() Probe for devices.
isContextProbed() Check if probe() was called on the current context.
getDeviceCount() Number of devices found during the last call to probe().
getError() Get the last error as a human readable string.
getDevices(int deviceClass=-1, …) Get devices found during the last call to probe().
openDevice(device) Open a device.
closeDevice(device) Close a device.
getDevicePose(device) Get the pose of a device.
lastUpdateTimeElapsed() Get the time elapsed in seconds since the last update() call.
update() Update the values for the devices handled by a context.
getDeviceParamf(device, int param) Get a floating point parameter from a device.
setDeviceParamf(device, int param, value) Set a floating point parameter for a given device.
getDeviceParami(device, int param) Get an integer parameter from a device.
getString(int stype) Get a string from OpenHMD.
getListString(int index, int type_) Get a device description string from an enumeration list index.
getListInt(int index, int type_) Get an integer value from an enumeration list index.

Details

Classes

class psychxr.drivers.openhmd.OHMDDeviceInfo

Information class for OpenHMD devices.

OpenHMD supports a wide range of devices (to varying degrees), including HMDs, controller, and trackers. This class stores data about a given device, providing the user details (e.g., type of device, vendor/product name, capabilities like tracking, etc.) about it.

Instances of this class are returned by calling getDevices(). You can then pass instances to openDevice() to open them.

deviceClass

Device class identifier (int).

deviceFlags

Device flags (int).

Examples

Check if a device has positional and orientation tracking support:

hmdInfo = getHmdInfo()
flags = OHMD_DEVICE_FLAGS_POSITIONAL_TRACKING |
    OHMD_DEVICE_FLAGS_ROTATIONAL_TRACKING

hasFullTracking = (self.c_data.deviceFlags & flags) == flags
deviceIdx

Enumerated index of the device (int).

hasOrientationTracking

True if capable of tracking orientation (bool).

hasPositionTracking

True if capable of tracking position (bool).

isController

True if this device is a controller (bool).

isDebugDevice

True if a virtual debug (null or dummy) device (bool).

isHMD

True if this device is an HMD (bool).

isOpen

Is the device open? (bool).

isTracker

True if this device is a generic tracker (bool).

manufacturer

Device manufacturer name, alias of vendorName (str)

productName

Device product name (str).

vendorName

Device vendor name (str).

Functions

psychxr.drivers.openhmd.success(int result)

Check if a function returned successfully.

Parameters:result (int) – Return value of a function.
Returns:True if the return code indicates success.
Return type:bool
psychxr.drivers.openhmd.failure(int result)

Check if a function returned an error.

Parameters:result (int) – Return value of a function.
Returns:True if the return code indicates failure.
Return type:bool
psychxr.drivers.openhmd.getVersion()

Get the version of the OpenHMD library presently loaded.

Returns:Major (int), minor (int), and patch (int) version.
Return type:tuple

Examples

Get the version for the OpenHMD library:

major_version, minor_version, patch_version = getVersion()
psychxr.drivers.openhmd.create()

Create a new OpenHMD context/session.

Calling this will create a context, enumerate devices and open them. Only the first connected HMD will be opened, all other device types that are controllers or external trackers will be opened.

At this time only a single context can be created per session. You must call this function prior to using any other API calls other than destroy().

Returns:Returns value of OHMD_S_OK if a context was created successfully, else OHMD_S_USER_RESERVED. You can check the result using the success() and failure() functions.
Return type:int

Examples

Create a new OpenHMD context, starting the session:

import sys
import psychxr.drivers.openhmd as openhmd

result = openhmd.create()  # create a context
if failure(result):  # if we failed to create a context, exit with error
    sys.exit(1)
psychxr.drivers.openhmd.destroy()

Destroy the current context/session.

psychxr.drivers.openhmd.probe()

Probe for devices.

Probes for and enumerates supported devices attached to the system. After calling this function you may use getDevices() which will return a list of descriptors representing found devices.

Returns:Number of devices found on the system.
Return type:int
psychxr.drivers.openhmd.isContextProbed()

Check if probe() was called on the current context.

Returns:True if the context was probed since :func:create was called.
Return type:bool
psychxr.drivers.openhmd.getDeviceCount()

Number of devices found during the last call to probe().

This function returns the same number that was returned by the last probe call. If referencing devices by their enumerated index, values from 0 to getDeviceCount() - 1 are valid.

Returns:Number of devices found on this system that OpenHMD can use.
Return type:int
psychxr.drivers.openhmd.getError()

Get the last error as a human readable string.

Call this after a function returns a code indicating an error to get a string describing what went wrong.

Returns:Human-readable string describing the cause of the last error.
Return type:str
psychxr.drivers.openhmd.getDevices(int deviceClass=-1, bool openOnly=False, bool nullDevices=False)

Get devices found during the last call to probe().

Parameters:
  • deviceClass (int) – Only get devices belonging to the specified device class. Values can be one of OHMD_DEVICE_CLASS_CONTROLLER, OHMD_DEVICE_CLASS_HMD, or OHMD_OHMD_DEVICE_CLASS_GENERIC_TRACKER. Set to -1 to get all devices (the default).
  • openOnly (bool) – Only get devices that are currently opened. Default is False which will return all devices found during the last probe call whether they are opened or not.
  • nullDevices (bool) – Include null/debug devices.
Returns:

List of OpenHMDDeviceInfo descriptors.

Return type:

list

Examples

Get all HMDs found on the system:

if probe():  # >0 if devices have been found
    all_devices = getDevices()
    only_hmds = [dev for dev in all_devices if dev.isHMD]

The same as above but using the deviceClass argument:

# assume we probed already
only_hmds = getDevices(deviceClass=OHMD_DEVICE_CLASS_HMD)
psychxr.drivers.openhmd.openDevice(device)

Open a device.

Parameters:device (OHMDDeviceInfo or int) – Descriptor or enumerated index of a device to open. Best practice is to pass a descriptor instead of an int.
psychxr.drivers.openhmd.closeDevice(device)

Close a device.

Parameters:device (OHMDDeviceInfo or int) – Descriptor or enumerated index of a device to close. Best practice is to pass a descriptor instead of an int.
psychxr.drivers.openhmd.getDevicePose(device)

Get the pose of a device.

Parameters:device (OHMDDeviceInfo or int) – Descriptor or enumerated index of a device to open. Best practice is to pass a descriptor instead of an int.
Returns:Object representing the pose of the device.
Return type:RigidBodyPose

Examples

Get the position (vector) and orientation (quaternion) of a device:

myDevicePose = getDevicePose(myDevice)  # HMD, controller, etc.
pos, ori = myDevicePose.posOri

You can get the eye poses from the pose (assuming its an HMD):

import psychxr.tools.vrmath as vrmath
leftEyePose, rightEyePose = vrmath.calcEyePoses(device, ipd=0.062)

These can be converted to eye view matrices:

leftViewMatrix = leftEyePose.viewMatrix
rightViewMatrix = rightViewMatrix.viewMatrix
psychxr.drivers.openhmd.lastUpdateTimeElapsed()

Get the time elapsed in seconds since the last update() call.

Returns:Elapsed time in seconds.
Return type:float
psychxr.drivers.openhmd.update()

Update the values for the devices handled by a context.

Call this once per frame. If PsychXR is running in a background thread, it is recommended that you call this every 10-20 milliseconds.

psychxr.drivers.openhmd.getDeviceParamf(device, int param)

Get a floating point parameter from a device.

This calls to ohmd_device_getf on the specified device, retrieving the value requested by param.

Parameters:
  • device (OHMDDeviceInfo or int) – Descriptor or enumerated index of a device. Best practice is to pass a descriptor instead of an int.
  • param (int) –

    Symbolic constant representing the parameter to retrieve. Parameters can be one of the following constants (return type and length if applicable in parentheses):

    • OHMD_ROTATION_QUAT (ndarray, length 4)
    • OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX (ndarray, length 16)
    • OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX (ndarray, length 16)
    • OHMD_LEFT_EYE_GL_PROJECTION_MATRIX (ndarray, length 16)
    • OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX (ndarray, length 16)
    • OHMD_POSITION_VECTOR (ndarray, length 3)
    • OHMD_SCREEN_HORIZONTAL_SIZE (float)
    • OHMD_SCREEN_VERTICAL_SIZE (float)
    • OHMD_LENS_HORIZONTAL_SEPARATION (float)
    • OHMD_LENS_VERTICAL_POSITION (float)
    • OHMD_LEFT_EYE_FOV (float)
    • OHMD_LEFT_EYE_ASPECT_RATIO (float)
    • OHMD_RIGHT_EYE_FOV (float)
    • OHMD_RIGHT_EYE_ASPECT_RATIO (float)
    • OHMD_EYE_IPD (float)
    • OHMD_PROJECTION_ZFAR (float)
    • OHMD_PROJECTION_ZNEAR (float)
    • OHMD_DISTORTION_K (ndarray, length 6)
    • OHMD_UNIVERSAL_DISTORTION_K (ndarray, length 4)
    • OHMD_UNIVERSAL_ABERRATION_K (ndarray, length 3)
    • OHMD_CONTROLS_STATE (ndarray, length is the value of OHMD_CONTROL_COUNT returned by the API)
  • value (object) – Value to set, must have the same length as the what is required by the parameter.
Returns:

Single floating-point value or array. The return value data type and size is automatically determined by the type of parameter.

Return type:

float or ndarray

Examples

Get the horizontal and vertical screen size for a device:

physical_horiz_size = ohmd.getDeviceParamf(
    hmd_device,
    ohmd.OHMD_SCREEN_HORIZONTAL_SIZE)
physical_vert_size = ohmd.getDeviceParamf(
    hmd_device,
    ohmd.OHMD_SCREEN_VERTICAL_SIZE)

Get the left eye model/view matrix:

left_modelview_matrix = ohmd.getDeviceParamf(
    hmd_device,
    ohmd.OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX)

# you may want to reshape it
left_modelview_matrix = left_modelview_matrix.reshape((4, 4))
psychxr.drivers.openhmd.setDeviceParamf(device, int param, value)

Set a floating point parameter for a given device.

This calls to ohmd_device_setf on the specified device, passing the value specified by param.

Parameters:
  • device (OHMDDeviceInfo or int) – Descriptor or enumerated index of a device. Best practice is to pass a descriptor instead of an int.
  • param (int) –

    Symbolic constant representing the parameter to set. Parameters can be one of the following constants (parameter type and length if applicable in parentheses):

    • OHMD_EYE_IPD (float)
    • OHMD_PROJECTION_ZFAR (float)
    • OHMD_PROJECTION_ZNEAR (float)
    • OHMD_EXTERNAL_SENSOR_FUSION (ndarray, length 10)

Examples

Set the eye IPD for the device used to compute view matrices:

ohmd.setDeviceParamf(
    hmd_device,
    ohmd.OHMD_EYE_IPD,
    0.062)  # in meters
psychxr.drivers.openhmd.getDeviceParami(device, int param)

Get an integer parameter from a device.

This calls to ohmd_device_geti on the specified device, retrieving the value requested by param.

Parameters:
  • device (OHMDDeviceInfo or int) – Descriptor or enumerated index of a device. Best practice is to pass a descriptor instead of an int.
  • param (int) –

    Symbolic constant representing the parameter to retrieve. Parameters can be one of the following constants (return type and length if applicable in parentheses):

    • OHMD_SCREEN_HORIZONTAL_RESOLUTION (int)
    • OHMD_SCREEN_VERTICAL_RESOLUTION (int)
    • OHMD_DEVICE_CLASS (int)
    • OHMD_DEVICE_FLAGS (int)
    • OHMD_CONTROL_COUNT (int)
    • OHMD_CONTROLS_HINTS (ndarray, length is the value of OHMD_CONTROL_COUNT returned by the API)
    • OHMD_CONTROLS_TYPES (ndarray, length is the value of OHMD_CONTROL_COUNT returned by the API)
Returns:

Single integer value or array. The return value data type and size is automatically determined by the type of parameter.

Return type:

float or ndarray

Examples

Get the horizontal and vertical screen resolution for a device:

hres = ohmd.getDeviceParamf(
    hmd_device,
    ohmd.OHMD_SCREEN_HORIZONTAL_RESOLUTION)
vres = ohmd.getDeviceParamf(
    hmd_device,
    ohmd.OHMD_SCREEN_VERTICAL_RESOLUTION)
psychxr.drivers.openhmd.getString(int stype)

Get a string from OpenHMD.

Parameters:stype (int) – Type of string data to fetch, either one of OHMD_GLSL_DISTORTION_FRAG_SRC or OHMD_GLSL_DISTORTION_FRAG_SRC.
Returns:Result of the ohmd_gets C-API call (int) and the description text (str).
Return type:tuple
psychxr.drivers.openhmd.getListString(int index, int type_)

Get a device description string from an enumeration list index.

Can only be called after probe().

Parameters:
  • index (int) – An index between 0 and the value returned by calling probe().
  • type (int) – Type of string description data to fetch, either one of OHMD_VENDOR, OHMD_PRODUCT and OHMD_PATH.
Returns:

The string description text (str).

Return type:

tuple

psychxr.drivers.openhmd.getListInt(int index, int type_)

Get an integer value from an enumeration list index.

Can only be called after probe().

Parameters:
  • index (int) – An index between 0 and the value returned by calling probe().
  • type (int) – Type of string description data to fetch.
Returns:

Result of the ohmd_list_geti C-API call (int) and the value (int).

Return type:

tuple