OpenVR SDK

repository·master·Indexed 27 days ago

https://github.com/valvesoftware/openvr

An abstraction layer and SDK that allows VR applications and drivers to interact with various VR hardware without vendor-specific implementations. This repository contains the SDK, API, and sample code, while the runtime is provided by SteamVR. It includes tools for generating language bindings for C, C#, and C via Python scripts, as well as reference driver samples for HMDs, controllers, trackers, and skeletal input simulation.

Tokens
44.5K
Snippets
81
Records
165
Agent score
92%

What's inside OpenVR

  1. Overview of OpenVR Driver and Utility Samples

    master

    The samples/drivers/ directory contains several sample projects for development:

    Driver Samples (drivers/):

    • barebones: A minimal driver implementation.
    • handskeletonsimulation: A driver for simulating hand skeletons.
    • simplecontroller: A driver for a simple controller.

    Utility Samples (utils/): These can be copied into your own projects to assist with development:

    • driverlog: Utility for driver logging.
    • vrmath: Mathematical utilities for VR.
  2. Overview of OpenVR Driver Samples

    master

    The OpenVR driver samples provide reference implementations for different types of hardware drivers:

    • barebones: A minimal driver implementation containing only the essential interfaces.

      • Uses: HmdDriverFactory, IServerTrackedDeviceProvider
    • handskeletonsimulation: Demonstrates skeletal input where hand positions are derived from the HMD position.

      • Uses: HmdDriverFactory, IServerTrackedDeviceProvider, IVRDriverInput (skeletal input), IVRSettings, IVRServerDriverHost, IVRDriverLog, IVRProperties
    • simplecontroller: Demonstrates a basic controller with standard input types.

      • Uses: HmdDriverFactory, IServerTrackedDeviceProvider, IVRDriverInput (boolean, scalar, haptic), IVRSettings, IVRServerDriverHost, IVRDriverLog, IVRProperties
  3. Overview of the OpenVR SDK

    master
    OpenVR is an API and runtime designed to provide hardware-agnostic access to VR hardware from multiple vendors. This repository contains the SDK, which includes the API and sample code. Note that the actual runtime is provided via SteamVR, which can be found under 'Tools' in the Steam client.
  4. Understand the Simple HMD Driver Sample

    master

    The simplehmd driver is an example implementation demonstrating how to add a Head-Mounted Display (HMD) device to SteamVR. It includes an IVRDisplayComponent to provide display information for the HMD.

    When running the sample, ensure the window is in focus to see the output. The sample simulates pose data by having the HMD move slowly up and down.

  5. Use the Hand Skeleton Simulation Driver

    master
    The Hand Skeleton Simulation Driver provides left and right hand controllers that interact with the Skeletal Input System. It allows hands to curl and splay. The driver implements both hands within a single ITrackedDeviceServerDriver class, using a constructor argument to define the controller role. This role is passed to SteamVR as a device property and is used internally to offset the pose x position. Tracking data is derived from the current HMD position.
  6. Understand the Simple Controller Driver example

    master

    The simplecontroller driver serves as an example of how to implement right and left hand controller devices for SteamVR. It demonstrates how to add devices with simple inputs using a single ITrackedDeviceServerDriver class.

    Key implementation details:

    • The class constructor accepts a controller role to distinguish between hands.
    • The controller role is provided to SteamVR as a device property.
    • The driver uses the current HMD position as the source for tracking data, demonstrating how to manipulate poses (e.g., offsetting the pose x position).
  7. Handle Haptic Vibration events

    master

    Haptic events are received via IVRServerDriverHost::PollNextEvent with the event type vr::EVREventType::VREvent_Input_HapticVibration.

    To correctly route the event, the driver must check the componentHandle property against the handle created via IVRDriverInput::CreateHapticComponent.

    Haptic Event Properties

    • fDurationSeconds: The duration of the event in seconds.
    • fFrequency: The frequency in Hz. Lower frequencies result in a "rumble" feel.
    • fAmplitude: The intensity of the vibration (0 to 1).

    Implementation Guidelines

    Drivers should apply the following constraints:

    • Amplitude: Clamp to the range [0, 1]. If $\le 0$, do not trigger haptics.
    • Duration: Clamp to [0, 10] seconds. If fDurationSeconds is 0, the driver should trigger a single pulse.
    • Frequency: Clamp to a minimum of 1000000.f / 65535.f and a maximum of 1000000.f / 300.f.

    Pulse Calculation Logic

    To convert a continuous haptic event into discrete pulses:

    1. Pulse Period: 1.f / fFrequency (in seconds).
    2. Pulse Count: fDurationSeconds * fFrequency. If fDurationSeconds is 0, the count is 1.
    3. Pulse Duration: Interpolate fAmplitude between a set minimum pulse duration and a maximum (which should be no more than half the total pulse duration or a set maximum, whichever is less).
    switch (vrEvent.eventType) {
      case vr::VREvent_Input_HapticVibration: {
        if (vrEvent.data.hapticVibration.componentHandle == m_compMyHaptic) {
          // This is where you would send a signal to your hardware to trigger actual haptic feedback
          
          const float pulse_period = 1.f / vrEvent.data.hapticVibration.fFrequency
          const float frequency = std::clamp(1000000.f / 65535.f, 1000000.f / 300.f, pulse_period);
          const float amplitude = std::clamp(0.f, 1.f, vrEvent.data.hapticVibration.fAmplitude);
          const float duration = std::clamp(0.f, 10.f, vrEvent.data.hapticVibration.fDurationSeconds);
          
          if(duration == 0.f) {
            // Trigger a single pulse of the haptic component
          } else {
            const float pulse_count = fDurationSeconds * fFrequency;
            const float pulse_duration = Lerp(my_minimum_duration, my_maximum_duration, amplitude);
            const float pulse_interval = pulse_period - pulse_duration;
          }
        }
      }
      break;
    }
  8. Add devices to the OpenVR runtime

    master

    To make a device visible to the OpenVR runtime, you must register it via the IVRServerDriverHost::TrackedDeviceAdded method within your IServerTrackedDeviceProvider implementation (typically your DeviceProvider class).

    Steps to add a device:

    1. Store device pointers: Maintain ownership of your device implementations (e.g., using std::unique_ptr<ControllerDevice>) within your DeviceProvider class.
    2. Call TrackedDeviceAdded: Invoke this method to notify the runtime that a new device is available. This can be done during DeviceProvider::Init for startup devices, or at any time (e.g., when a connection is detected).
    3. Provide a unique serial number: Every device added to the runtime must have a unique string as its serial number.
    4. Specify the device class: Pass the appropriate vr::TrackedDeviceClass (e.g., vr::TrackedDeviceClass_Controller).
    5. Pass the device pointer: Provide the pointer to the instantiated device object.
    6. Update poses: Ensure you call the device's update method (e.g., RunFrame()) within the DeviceProvider::RunFrame() loop to keep poses updated.
    // 1. Store pointers in DeviceProvider
    class DeviceProvider : public vr::IServerTrackedDeviceProvider {
        // ...
    private:
        std::unique_ptr<ControllerDevice> my_left_device_;
        std::unique_ptr<ControllerDevice> my_right_device_;
    };
    
    // 2. Register devices in Init()
    void DeviceProvider::Init() {
        my_left_device_ = std::make_unique<ControllerDevice>(vr::TrackedControllerRole_LeftHand);
        vr::VRServerDriverHost()->TrackedDeviceAdded("<my_left_serial_number>",
                                                    vr::TrackedDeviceClass_Controller,
                                                    my_left_device_.get());
        
        my_right_device_ = std::make_unique<ControllerDevice>(vr::TrackedControllerRole_RightHand);
        vr::VRServerDriverHost()->TrackedDeviceAdded("<my_right_serial_number>",
                                                    vr::TrackedDeviceClass_Controller,
                                                    my_right_device_.get());
    }
    
    // 3. Update device frames in RunFrame()
    void DeviceProvider::RunFrame() {
        if(my_left_device_ != nullptr) {
            my_left_device_->RunFrame();
        }
        
        if(my_right_device_ != nullptr) {
            my_right_device_->RunFrame();
        }
    }