pyopenvr

repository·main·Indexed 43 days ago

https://github.com/cmbruns/pyopenvr

Unofficial Python bindings for Valve's OpenVR virtual reality SDK, enabling interaction with VR hardware and SteamVR. Provides access to core interfaces such as VRSystem, VRCompositor, VROverlay, and VRInput, along with constants and data structures for device poses, event polling, and tracked device properties. Note: This library is being superseded by OpenXR; users are encouraged to switch to pyopenxr.

Tokens
18.3K
Snippets
47
Records
74
Agent score
53%

What's inside pyopenvr

  1. Install pyopenvr

    main

    To use pyopenvr, ensure you have the following prerequisites installed:

    1. Python 3.5+
    2. HTC Vive SteamVR

    Then, install the bindings using pip or by downloading the installer from the official releases page.

    pip install openvr
  2. Handle OpenVR error codes with OpenVRError

    main

    When OpenVR functions return a failure code, pyopenvr raises specific Python exceptions derived from OpenVRError. Instead of catching a generic Exception, you can catch OpenVRError or more specific error subclasses to handle different failure modes precisely.

    To convert a raw error value (integer) into its corresponding Python exception class, use the check_error_value class method on an ErrorCode subclass.

    from openvr.error_code import TrackedPropertyError
    
    # Example of checking an error value returned from an OpenVR call
    try:
        # Assume error_val is an integer returned by a C++ binding
        TrackedPropertyError.check_error_value(error_val, "Failed to read property")
    except TrackedPropertyError as e:
        print(f"OpenVR Tracked Property Error: {e}")
    except OpenVRError as e:
        print(f"General OpenVR Error: {e}")
  3. Understand VREvent_t and event data structures

    main

    OpenVR uses a union-based event system. When an event is polled via IVRSystem.pollNextEvent, the resulting VREvent_t structure contains an eventType and a data field. The data field is a VREvent_Data_t union, which holds the specific details for that event type.

    Common event data structures include:

    • VREvent_Controller_t: For controller button events.
    • VREvent_Mouse_t: For simulated mouse events in overlay space.
    • VREvent_TouchPadMove_t: For touchpad movement data.
    • VREvent_Overlay_t: For events related to overlays.
    • VREvent_HapticVibration_t: For haptic feedback events.
    • VREvent_Screenshot_t: For screenshot related events.

    To use them, check the eventType and then access the corresponding field in the data union.

    import openvr
    
    vr_system = openvr.VRSystem()
    event = openvr.VREvent_t()
    
    # Polling an event
    if vr_system.pollNextEvent(event, 0x00000001): # Example mask
        if event.eventType == openvr.EVREventType.ControllerButton:
            button_id = event.data.controller.button
        elif event.eventType == openvr.EVREventType.Mouse:
            mouse_x = event.data.mouse.x
            mouse_y = event.data.mouse.y
  4. Initialize and shutdown OpenVR

    main

    To use the OpenVR API, you must first initialize the runtime. Use init(applicationType, pStartupInfo=None) to find the active installation and initialize it. This returns a VRSystem instance. To release resources and unload the library, call shutdown().

    Note on pStartupInfo: If provided, this must be an absolute or relative path to the "root" of the VR API install (the directory containing the drivers directory and a platform directory, not just the DLL location).

    import openvr
    
    # Initialize OpenVR
    system = openvr.init(openvr.EVRApplicationType.VRApplication_Application)
    
    # ... use system ...
    
    # Shutdown OpenVR
    openvr.shutdown()
  5. Basic usage of pyopenvr

    main

    To use pyopenvr, you must initialize the VR system using openvr.init() with an appropriate application type (e.g., openvr.VRApplication_Scene). After performing your VR operations, always call openvr.shutdown() to release resources.

    In the main loop, you can use openvr.VRCompositor().waitGetPoses() to retrieve device poses, such as the HMD pose, which can be accessed via the openvr.k_unTrackedDeviceIndex_Hmd index.

    import sys
    import time
    import openvr
    
    openvr.init(openvr.VRApplication_Scene)
    poses = []  # will be populated with proper type after first call
    for i in range(100):
        poses, _ = openvr.VRCompositor().waitGetPoses(poses, None)
        hmd_pose = poses[openvr.k_unTrackedDeviceIndex_Hmd]
        print(hmd_pose.mDeviceToAbsoluteTracking)
        sys.stdout.flush()
        time.sleep(0.2)
    openvr.shutdown()
  6. Manage and interact with VR Overlays

    main

    The Overlay interface allows you to create and manage 2D elements in the VR space. You can set textures, handle mouse/laser intersections, and manage cursors.

    Key Tasks:

    • Set Texture: Use setOverlayTexture to provide a texture for the overlay. Note that this can only be called by the overlay's creator or renderer process.
    • Intersection Testing: Use computeOverlayIntersection to find where a ray intersects the overlay in pixel coordinates.
    • Cursor Management: Use setOverlayCursor to change the default blob to a custom cursor, and setOverlayCursorPositionOverride to manually position the cursor.
    • Haptics: Trigger haptic feedback on the laser mouse controller using triggerLaserMouseHapticVibration.
  7. Work with Dashboard Overlays

    main

    Dashboard overlays are special overlays that appear when the user opens the SteamVR dashboard.

    Key Tasks:

    • Create Dashboard Overlay: Use createDashboardOverlay(overlayKey, overlayFriendlyName) to create a new dashboard overlay. It returns a mainHandle and a thumbnailHandle.
    • Visibility: Check if the dashboard is visible using isDashboardVisible() or if a specific overlay is the active one using isActiveDashboardOverlay(overlayHandle).
    • Process Focus: Use setDashboardOverlaySceneProcess(overlayHandle, processId) to ensure the overlay only appears when a specific process has scene focus.
  8. Use the IVROverlayView interface

    main

    The IVROverlayView interface allows you to acquire read privileges over an overlay's contents to sample them.

    Workflow:

    1. Acquire: Call acquireOverlayView(overlayHandle) to get a VROverlayView_t and a VRNativeDevice_t.
    2. Sample: Use the acquired view to sample contents via native APIs.
    3. Release: You must call releaseOverlayView() to release the acquired resources. Failure to do so will leak GPU resources.
  9. Load and manage VR Render Models

    main

    The IVRRenderModels interface is used to load and interact with 3D models representing tracked devices (like controllers).

    Key Tasks:

    • Load Models: Use loadRenderModel_Async(renderModelName) to load a model. It returns a RenderModel_t pointer. The method returns VRRenderModelError_Loading while the model is still loading.
    • Manage Memory: Always call freeRenderModel(renderModel) when finished to prevent memory leaks.
    • Component Interaction: Models are composed of 'components' (e.g., buttons, triggers). Use getComponentCount(renderModelName) and getComponentName(renderModelName, componentIndex) to iterate through them.
    • Component State: Use getComponentStateForDevicePath(renderModelName, componentName, devicePath, state) to query the current state (position, motion, etc.) of a specific component.
  10. Manage VR Overlays with IVROverlay

    main

    The IVROverlay class provides the API for creating, managing, and transforming 2D overlays in VR space. Overlays can be standalone quads, subviews composited onto parent overlays, or projection overlays.

    Core Overlay Lifecycle

    • Create: Use createOverlay(overlayKey, overlayName) to create a new named overlay. It starts hidden by default.
    • Find: Use findOverlay(overlayKey) to retrieve a handle for an existing overlay using its unique key.
    • Destroy: Use destroyOverlay(overlayHandle) to remove an overlay. Overlays are automatically destroyed when the application calls VR_Shutdown.
    • Visibility: Use showOverlay(overlayHandle) and hideOverlay(overlayHandle) to control visibility. Use isOverlayVisible(overlayHandle) to check the current state.

    Subview Overlays

    Subviews are separate images composited onto a parent overlay based on a 2D transform. They can only be created for parent overlays owned by the same process.

    • Create: createSubviewOverlay(parentOverlayHandle, subviewOverlayKey, subviewOverlayName)
    • Position: setSubviewPosition(overlayHandle, x, y) positions the subview in the pixel coordinate space of the parent overlay (top-left is 0,0).

    Overlay Properties and Appearance

    • Naming: Retrieve or set the overlay's identity using getOverlayKey(overlayHandle), getOverlayName(overlayHandle), and setOverlayName(overlayHandle, name).
    • Color and Alpha: Use setOverlayColor(overlayHandle, red, green, blue) (values 0.0 to 1.0) and setOverlayAlpha(overlayHandle, alpha) to adjust appearance.
    • Dimensions: Set the physical size using setOverlayWidthInMeters(overlayHandle, widthInMeters). The default is 1 meter.
    • Curvature: Use setOverlayCurvature(overlayHandle, curvature) to draw the overlay as a curved surface (0..1, where 1 is a closed cylinder). Use setOverlayPreCurvePitch(overlayHandle, radians) to adjust the pitch before curvature is applied.
    overlay = ivroverlay.createOverlay("my_key", "My Overlay")
    ivroverlay.setOverlayWidthInMeters(overlay, 2.0)
    ivroverlay.showOverlay(overlay)
  11. Poll for VR events with pollNextEvent

    main

    Retrieves the next event from the OpenVR event queue.

    • pollNextEvent(event): Fills the provided event struct with the next event. Returns True if an event was available, False otherwise.
    • pollNextEventWithPose(origin, event): Fills the event and also provides the pose of the associated tracked device in a provided pose struct. Note that this pose will be older than the call time and should not be used for rendering.
    • pollNextEventWithPoseAndOverlays(origin, event): An efficient way to poll both system events and overlay-specific events (like button presses on an overlay). If an event is targeted at a specific overlay, pulOverlayHandle will be set.
    # Example: Polling for a standard event
    event = VREvent_t()
    if vr_system.pollNextEvent(event):
        print(f"Event type: {vr_system.getEventTypeNameFromEnum(event.eventType)}")