UEVR

Welcome to UEVR! This powerful tool will transform your favorite Unreal Engine games into 6DOF VR experiences with minimal effort.

UEVR can be downloaded from here.

Supported Engine Versions

  • Unreal Engine 4.8 - 5.3

Supported Graphics APIs

  • Direct3D 11
  • Direct3D 12

Features

  • Full 6DOF support out of the box (HMD movement)
  • Full stereoscopic 3D out of the box
  • Frontend GUI for easy process injection
  • Supports OpenVR and OpenXR runtimes
  • Native UE4/UE5 stereo rendering system for a truly immersive VR experience
  • 3 rendering modes: Native Stereo, Synchronized Sequential, and Alternating/AFR
  • Optional 3DOF motion controls in many games, essentially emulating a semi-native VR experience
  • Optional roomscale movement in many games, moving the player character itself in 3D space along with the headset
  • User-authored UI-based system for adding motion controls and first person to games that don't support them
  • Automatic handling of most in-game UI so it is projected into 3D space
  • In-game menu with shortcuts for adjusting settings
  • Access to various CVars for fixing broken shaders/effects
  • Optional depth buffer integration for improved latency on some headsets
  • Per-game configurations
  • C++ Plugin system and Blueprint support for modders to add additional features like motion controls

Reporting a bug

Click "Open Global Dir" in the frontend. Browse to the corresponding game folder that is having an issue and zip up the folder and upload it for us. Clicking "Export Config" is an alternative here to do this automatically.

You can then describe the issue and upload the file for us on the Issues.

You can read the full instructions when making an issue, choose the bug report template.

Getting Started

Before launching, ensure you have installed .NET 6.0. It should tell you where to install it upon first open, but if not, you can download it from here

Anti-viruses may delete files required to run from UEVR at this time. You may need to whitelist the UEVR directory and un-quarantine any files the anti-virus may have deleted.

  1. Launch UEVRInjector.exe
  2. Launch the target game
  3. Locate the game in the process dropdown list
  4. Select your desired runtime (OpenVR/OpenXR)
  5. Configure pre-injection settings
  6. Inject

To-dos before injection

Double check that the game you are injecting into has no Anti-Cheat mechanisms. If you are not sure, assume that all multiplayer games have Anti-Cheat, and do not attempt to inject into them. The exception here is if it can be disabled in some way to play offline.

  1. Disable HDR (it will still work without it, but the game will be darker than usual if it is)
  2. Start as administrator if the game is not visible in the list
  3. Pass -nohmd to the game's command line and/or delete VR plugins from the game directory if the game contains any existing VR plugins
  4. Disable any overlays that may conflict and cause crashes (Rivatuner, ASUS software, Razer software, Overwolf, etc...)
  5. Disable graphical options in-game that may cause crashes or severe issues like DLSS Frame Generation
  6. Consider disabling Hardware Accelerated GPU Scheduling in your Windows Graphics settings

Quick troubleshooting

There are some games that work pretty much perfectly out of the box, and others will need tinkering.

  • If there are major graphical bugs or crashing, change the rendering method to Synced Sequential and check if the issue goes away
  • If there is still crashing, there are various Compatibility options that can be enabled (Advanced View shows them, or can be seen in the frontend)
    • Do not enable Extreme Compatibility Mode, only as a last resort
  • If there are still graphical bugs present, enable Advanced View and tweak the CVars
    • INI tweaks can work here as well
  • If the game is locked to 60 FPS (even in flat), disable ASW/motion smoothing so the game doesn't slow down due to the runtime halving the framerate
  • If the game is running poorly, lower the in-game settings, lower the resolution in the UEVR interface or SteamVR
  • If there is rapid flickering or extreme lag, this likely indicates that DLSS Frame Generation is enabled, turn it off or disable Hardware Accelerated GPU Scheduling in your Windows Graphics settings.
  • OpenXR Toolkit may also need to be disabled if there is even more crashing or lag

If all fails, someone may have came up with a config for the game or can help you on the Flat2VR Discord.

If there is rotational judder/lag

There's a few reasons for this and fixes for it:

  • If using Virtual Desktop, you must use OpenXR to prevent lag when rotating your head, OpenVR simply does not work correctly right now with Virtual Desktop
    • The Virtual Desktop fix must be enabled if it isn't getting enabled (under Runtime)
  • You can also disable r.OneFrameThreadLag under Console/CVars (advanced view must be enabled)
  • You can also modify the Frame Delay Compensation under Debug (advanced view must be enabled)

In-Game Menu

Press the Insert key or L3+R3 on a controller to access the in-game menu, which opens by default at startup. With the menu open, hold RT for various shortcuts:

  • RT + Left Stick: Move the camera left/right/forward/back
  • RT + Right Stick: Move the camera up/down
  • RT + B: Reset camera offset
  • RT + Y: Recenter view
  • RT + X: Reset standing origin

Help! What games are Unreal Engine?

Use tools like Rai Pal.

Rai Pal is a tool by Raicuparta that can go through your entire library of games and attempt to tell you what engine, and what version of that engine they are using.

It also has some level of support for UEVR for automatic launching, as well as displaying user scores given to the VR compatibility of a given game.

Alex also has made a spreadsheet of supported games and how well they function, and various other metrics.

Quick overview of rendering methods

Native Stereo

When it works, it looks the best, performs the best (usually). Can cause crashes or graphical bugs if the game does not play well with it.

Temporal effects like TAA are fully intact. DLSS/FSR2 usually work completely fine with no ghosting in this mode.

Fully synchronized eye rendering. Works with the majority of games. Uses the actual stereo rendering pipeline in the Unreal Engine to achieve a stereoscopic image.

Synchronized Sequential

This is the first alternative option that should be used if Native Stereo is not working as expected or you are encountering graphical bugs. It should be noted that this mode gives worse performance than Native Stereo.

A form of AFR. Can fix many rendering bugs that are introduced with Native Stereo. Renders two frames sequentially in a synchronized fashion on the same engine tick.

Fully synchronized eye rendering. Game world does not advance time between frames.

Looks normal but temporal effects like TAA will have ghosting/doubling effect. Motion blur will need to be turned off.

Skip Draw skips the viewport draw on the next engine tick. Usually works the best but sometimes particle effects may not play at the correct speed.

Skip Tick skips the next engine tick entirely. Usually buggy but does fix particle effects and sometimes brings higher performance.

AFR

Alternated Frame Rendering. Renders each eye on separate frames in an alternating fashion, with the game world advancing time in between frames. Causes eye desyncs and usually nausea along with it.

Not synchronized. Generally should not be used unless the other two are unusable in some way.

Overview of UEVR usage by Alex

video

Getting started spreadsheet by Alex

Additional information

Read the Detailed overview for a comprehensive guide on how to fine-tune your VR experience.

Head over to the Flat2VR Discord.

Special thanks

To all the testers. These include:

  • Brian Tate - Founder of the Flat2VR community. He is the catalyst that kicked off this project. He was digging around and found out that most of the stereo pipeline is left in the engine.

  • Alex G/Virtual Insider - A tester from the start. Has helped find many bugs and issues. Has also provided many helpful suggestions and ideas.

  • Alex/Waifu Enjoyer - One of the most prolific testers. Has tested hundreds of games and has helped find many bugs and issues. Has also provided many helpful suggestions and ideas. Has provided helpful statistics on game compatibility.

  • Alchemist - DRG VR developer.

  • Ashok - Has helped find many bugs and issues. Has also provided many helpful suggestions and ideas. Has poked around various specific games making mods to fix issues with them and add things like Oculus controller prompts. Experimented a lot with UObjectHook to fix games and find issues/pain points with this feature.

  • Igoreso

  • Jay Fullerton - Has tested many games, and stands out as one of the testers with a lower end system. This has helped get an idea of how the tool performs on lower end hardware.

  • Kitt - Mechwarrior 5 VR developer.

  • Lance McCary - Has helped find many bugs and issues. Has also provided many helpful suggestions and ideas. Helped coordinate with Matthieu Bucchianeri to get eye tracked foveated rendering working in OpenXR Toolkit.

  • Kalth Streil - Has helped find many bugs and issues.

  • Matthieu Bucchianeri - Creator of OpenXR Toolkit. Has helped figure out what needed to be changed to get eye tracked foveated rendering working. Has also provided many helpful technical insights into the OpenXR API and VR development in general.

  • Narknon - UE4SS developer. Provided a lot of helpful information about the Unreal Engine in general.

  • PanterA - Joined late into development, but tested a good amount of games and provided good feedback. Located games that were causing crashes after code changes. Helped find issues with AMD GPUs.

  • PinkMilkProductions - DRG VR developer.

  • pvncher

  • webhead

Disclaimer

UEVR is not affiliated with, endorsed by, or connected to Epic Games, Unreal Engine, or any of their affiliates. All trademarks are the property of their respective owners.

UEVR: In-Depth Guide

Dive deeper into the UEVR and learn how to fine-tune your VR experience with this comprehensive guide. We'll explore various settings, configurations, and troubleshooting tips to help you get the most out of your favorite Unreal Engine games in VR.

Frontend GUI

The frontend GUI provides an intuitive interface for injecting VR functionality into your chosen game. Here, you can:

  1. Select a process to inject into
  2. Choose your desired runtime (OpenVR/OpenXR)
  3. Toggle VR plugins (if necessary)
  4. Configure pre-injection settings

Frontend GUI Screenshot Placeholder

OpenVR or OpenXR?

OpenVR usually has the highest compatibility, but OpenXR usually gives higher performance when it works, especially if the headset has a native OpenXR runtime.

OpenVR requires SteamVR to be installed. OpenXR requires a valid OpenXR runtime for the headset, but can also run through SteamVR if SteamVR is set as the active runtime.

When using Virtual Desktop, you must use OpenXR to avoid rotation lag when moving your head.

Pre-Injection Settings

Before injecting, you can customize the following settings:

  • VR_RenderingMethod: Choose from Native Stereo, Synchronized Sequential, or Alternating/AFR
  • VR_SyncedSequentialMethod: Configure the behavior of the Synced Sequential rendering method
  • VR_UncapFramerate: Enable or disable framerate uncapping

After injection, the rest of the options will populate automatically. You can modify these settings in the in-game menu or through the config.txt file.

Pre-Injection Settings Screenshot

In-Game Menu

The in-game menu offers additional configuration options and shortcuts for adjusting settings on the fly. Access the menu by pressing the Insert key or L3+R3 on a controller.

The in-game menu can be accessed either inside the VR headset, or you can use the desktop view to adjust settings without having to put on a headset.

As of recent updates, the menu can be controlled as well by pointing your motion controller at it to emulate a mouse.

In-Game Menu Screenshot 1

In-Game Menu Screenshot 2

In-Game Shortcuts

While holding RT:

  • RT + Left Stick: Move the camera left/right/forward/back
  • RT + Right Stick: Move the camera up/down
  • RT + B: Reset camera offset
  • RT + Y: Recenter view
  • RT + X: Reset standing origin

CVars and Fixes

CVars Screenshot

Use the in-game menu to access and modify various CVars for fixing broken shaders and effects. The tool offers a range of options for addressing common rendering issues.

Depth Buffer Integration

While depth buffer integration is disabled by default, enabling it can greatly improve latency on Oculus headsets when using OpenXR with the native Oculus OpenXR runtime. To enable depth buffer integration, adjust the VR_EnableDepth setting.

Depth Buffer Integration Screenshot

Configurations

All configurations are stored on a per-game basis in the %APPDATA%/UnrealVRMod directory. You can modify settings directly in the UI or through the config.txt file. This directory can be accessed in the frontend GUI by clicking the "Open Global Dir" button.

Configurations Screenshot

Plugins

Plugins can be installed in the plugins folder in the game's configuration directory. Simply drop the plugin dll into it.

Troubleshooting & Optimization

Optimal performance and compatibility

  • Tweak graphical settings in-game to reduce load
  • Experiment with different rendering methods if you encounter rendering bugs or crashes
  • Use the in-game menu and CVars to address shader and effect issues
  • Enable depth buffer integration for improved latency on Oculus headsets (OpenXR only)
  • Consider upgrading your system for the best experience with high-end AAA titles

Further tweaks can be done by modifying the game's INI files, using UUU, UE4SS, or other external tools. Various tweaks that have been made for the normal version of the games can be applied to the VR version as well.

For those with motion sickness

Enable "Decoupled Pitch" under the VR options. This will stop the camera from rolling or moving vertically.

UEVR: Default VR Controller Bindings

UEVR implements (or attempts to implement) 1:1 mappings from a default Xbox controller to a VR controller.

These can be rebound in a few ways:

  1. If using OpenXR, you can modify the bindings under Runtime
  2. If using SteamVR/OpenVR, they can be remapped like a normal VR game
  3. If using Steam with any runtime, Steam Input can be used to remap the buttons as well

Default Bindings

  • Left thumbstick: Move

  • Right thumbstick: Rotate

  • Left Grip: LB

  • Right Grip: RB

  • Left Trigger: LT

  • Right Trigger: RT

  • Left Thumbstick Click: L3

  • Right Thumbstick Click: R3

  • Left System Button: Start/Pause

  • Left A Button: B/Circle

  • Left B Button: Y/Triangle

  • Right A Button: A/X

  • Right B Button: X/Square

Special Default Bindings

  • Left System Button (Long Press): Select/View

Oculus Special Bindings

  • Right Thumbrest + Left thumbstick: DPad
    • This can be disabled if you want to use the thumbrest for something else or the thumbrest has issues

UEVR: UObjectHook

What is UObjectHook?

UObjectHook is an extra part of UEVR that can be used to do many things, such as:

  • Attach the camera to any object or component in the game
  • Add 6DoF motion controls by attaching any components to the motion controllers
    • This is usually contingent on the 3DoF motion controls working in the first place
  • Modify and view any property of any active UObject in the game
    • This can be saved by right clicking the property
  • Toggle the visibility of any component in the game
  • Save the state of all of the above for persistence across sessions

Important information

UObjectHook is not enabled by default. When giving out profiles, ensure you have "Enabled at Startup" enabled under UObjectHook's config tab.

If a profile received does not work, it is likely because of the above. UObjectHook is not enabled by default, so you need to manually open the UObjectHook menu to enable it if this is the case.

Examples

In the following examples, we'll be using the "First Person Template" from the Unreal Engine.

These are just the simplest examples possible. There are games that will have differently named components. Sometimes you'll need to go through multiple component lists, children, or properties. You'll usually want to be on the lookout for SkeletalMeshComponent components for attachments.

Attaching the camera to an object

In this example, we'll attach the camera to the local player/pawn.

  1. Open the in-game menu and enable advanced options if not already enabled
  2. Navigate to UObjectHook on the left side
  3. Go to "Common Objects"
  4. Go to "Acknowledged Pawn"
  5. Click "Attach Camera to" (click "Attach Camera to (Relative)" if the game is already first-person)
  6. Click "Save State"

Doing this allows you to turn third person games into first person games. If done in a first person game, this removes unwanted camera movement that should not be there in VR.

Attaching components to motion controllers

In this example, we'll attach a component(s) to the motion controllers.

  1. Open the in-game menu and enable advanced options if not already enabled
  2. Navigate to UObjectHook on the left side
  3. Go to "Common Objects"
  4. Go to "Acknowledged Pawn"
  5. Go to "Components"
  6. Click "SkeletalMeshComponent Mesh2P" and uncheck "Visible"
  7. Click "SkeletalMeshComponent FP_Gun"
  8. Click "Attach right" or "Attach left" depending on which controller you want to attach to
  9. Click "Adjust"
  10. The menu will close, move your controller so that it lines up with the gun in-game
  11. Open the menu again, and the weapon should be attached to the controller with the correct offset
  12. Now click "Permanent Change", this will allow the projectiles to fire from the correct location
  13. Click "Save State"
  14. Navigate to "Input" on the left side
  15. Change the "Aim Method" to "Left Controller" or "Right Controller" depending on which controller you attached the weapon to
  16. You should now have full 6DoF motion controls for the weapon

Glossary

Permanent Change

This means that UEVR will no longer reset the object back to its original position and rotation after rendering is over. This can make projectiles come out of the correct spot with weapons, and make things like colliders work with melee weapons (if they have them).

It is not enabled by default because the user can inadvertently set this option on something that dictates their player position, launching them out of the map.

UEVR: Plugin Development

This section covers the basics of developing plugins for UEVR. If you're looking for information on how to use UEVR, check out the Usage section.

If you are unfamiliar with C/C++, you can try the Blueprint API.

Overview

Plugins in UEVR are developed primarily in C++. However, the API header is written in C. This means it's possible to bind it to other languages, or even just write them in C.

The API header is located in include/uevr/API.h.

The C++ API header is in include/uevr/API.hpp.

The base C++ plugin header is in include/uevr/Plugin.hpp.

Plugin Installation

In the frontend, click the "Open Global Dir" button. Locate the corresponding game directory, and place the DLL in the plugins folder.

During plugin development, you many want to create a symbolic link from the plugins dir to your project's DLL output directory. This way, you can build the plugin and have it automatically load into UEVR.

Plugin Lifecycle

Plugins are loaded and unloaded at runtime. The plugin lifecycle is as follows:

  1. UEVR starts its initialization process
  2. UEVR initially loads all plugins, and calls their DLLMain functions
    • If the plugin is a C++ plugin, the DLLMain function will call the on_dllmain function of the plugin
  3. UEVR begins initializing the rest of its own components
  4. After initialization, UEVR calls the on_initialize function of each plugin
  5. During its execution loop, UEVR will call the various on_* functions of each plugin
  6. The user can choose to unload all plugins, and reload them at will at runtime

The easy way

Plugin.hpp provides a class that can be inherited from to create a plugin. This class provides a number of virtual functions that can be overridden to implement the plugin's functionality.

The way it is structured is also how you would use the API in C++ using the API.h and API.hpp headers.

Plugin.hpp also implements a DLLMain for you, so you don't have to worry about that. All you need to worry about is overriding the virtual functions.

The project must be compiled as a DLL.

A simple example

#include <memory>

#include "uevr/Plugin.hpp"

using namespace uevr;

#define PLUGIN_LOG_ONCE(...) {\
    static bool _logged_ = false; \
    if (!_logged_) { \
        _logged_ = true; \
        API::get()->log_info(__VA_ARGS__); \
    }}

class ExamplePlugin : public uevr::Plugin {
public:
    ExamplePlugin() = default;

    void on_dllmain() override {}

    void on_initialize() override {
        // Logs to the appdata UnrealVRMod log.txt file
        API::get()->log_error("%s %s", "Hello", "error");
        API::get()->log_warn("%s %s", "Hello", "warning");
        API::get()->log_info("%s %s", "Hello", "info");
    }

    void on_pre_engine_tick(UEVR_UGameEngineHandle engine, float delta) override {
        PLUGIN_LOG_ONCE("Pre Engine Tick: %f", delta);
    }

    void on_post_engine_tick(UEVR_UGameEngineHandle engine, float delta) override {
        PLUGIN_LOG_ONCE("Post Engine Tick: %f", delta);
    }

    void on_pre_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) override {
        PLUGIN_LOG_ONCE("Pre Slate Draw Window");
    }

    void on_post_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) override {
        PLUGIN_LOG_ONCE("Post Slate Draw Window");
    }
};

// Actually creates the plugin. Very important that this global is created.
// The fact that it's using std::unique_ptr is not important, as long as the constructor is called in some way.
std::unique_ptr<ExamplePlugin> g_plugin{new ExamplePlugin()};

UEVR: Blueprint API

UEVR implements some functions within the Unreal Engine's existing VR Blueprint API. These functions would usually do nothing because the VR plugins are not present. UEVR implements these functions so modders can easily access HMD and controller data without having to write a C++ plugin.

This should be more familiar to modders, and easier to use than the C++ API. Normal game developers should feel right at home as well.

It should be noted that generating the uproject for interacting with these games is out of UEVR's scope. There are other tools to assist in this, and others may have already generated one somewhere.

UE4SS can assist in generating projects for blueprints.

Official modding tools by the game developers can work here too, if they provide a working uproject.

Unreal Engine Modding Discord

UE4SS Discord

Automatically handled components

Motion Controller components

When UObjectHook is activated

All UMotionControllerComponent ("Motion Controller") components will have their location and rotation set correctly to the world transform of the user's motion controllers.

This means you can make Motion Controller components in Blueprint and UEVR will handle it for you, as long as you enable UObjectHook. You can then do any logic you wish with the Motion Controller transforms, like parenting a weapon mesh to one of them.

You must correctly set the Motion Source name to either Left or Right. Case sensitive. Or modify the Hand property on older UE versions.

Implemented functions

Tested and confirmed working on 4.27 and 5.0.3

Load Blueprint Code in the UEVR menu must be turned on or one of the head/controller aiming options must be enabled for these to work.

Some functions explicitly require Load Blueprint Code to be enabled, not just the aiming options.

For more information on these functions, visit the Unreal Engine documentation

Head Mounted Display: Is Head Mounted Display Enabled

Always works on < 4.18. >= 4.18, Load Blueprint Code must be explicitly enabled for this to work.

Head Mounted Display: Get Device Pose

This one only works for the HMD for now. Bit of a hacky implementation, but it works.

Head Mounted Display: Get Orientation And Position

Gets data about the HMD transform. Load Blueprint Code must be explicitly enabled for this to work on >= 4.18.

Head Mounted Display: Get Motion Controller Data

Only works for left and right controller.

Valid, Aim Position, Aim Rotation, Grip Position, Grip Rotation are implemented.

Head Mounted Display: Get XRSystem Flags

Metadata used to inform blueprint about certain things.

enum ECustomSystemFlags : int32_t {
    SYSTEMFLAG_NONE = 0,
    SYSTEMFLAG_HMD_ACTIVE = 1 << 0,
    SYSTEMFLAG_DECOUPLED_PITCH = 1 << 1,
    SYSTEMFLAG_OPENXR = 1 << 2,
    SYSTEMFLAG_OPENVR = 1 << 3,
    SYSTEMFLAG_MOTION_CONTROLLERS_ACTIVE = 1 << 4,
    SYSTEMFLAG_LEFT_THUMBREST_ACTIVE = 1 << 5,
    SYSTEMFLAG_RIGHT_THUMBREST_ACTIVE = 1 << 6,
    SYSTEMFLAG_GAME_AIMING_MODE = 1 << 7,
    SYSTEMFLAG_HEAD_AIMING_MODE = 1 << 8,
    SYSTEMFLAG_LEFT_CONTROLLER_AIMING_MODE = 1 << 9,
    SYSTEMFLAG_RIGHT_CONTROLLER_AIMING_MODE = 1 << 10,
    SYSTEMFLAG_TWO_HANDED_LEFT_AIMING_MODE = 1 << 11,
    SYSTEMFLAG_TWO_HANDED_RIGHT_AIMING_MODE = 1 << 12,
};

Head Mounted Display: Get HMDData

Untested.

Head Mounted Display: Reset Orientation

Head Mounted Display: Reset Position

Head Mounted Display: Reset Orientation and Position

UEVR: Lua API

UEVR provides a Lua API that can be used to create plugins. This API is a wrapper around the C++ API, and is intended to be used by plugins written in Lua.

It is intended to be used within UE4SS, but can technically be used in any Lua environment.

This API is in its infancy, and is subject to change.

Example

local LuaVR = require("LuaVR")

local function vr_print(text)
    print("[LuaVR Script] " .. text .. "\n")
end

local params = LuaVR.params
local callbacks = params.sdk.callbacks

local total_t = 0.0

-- Example usage of callbacks
callbacks.on_pre_engine_tick(function(engine, delta)
    total_t = total_t + delta
end)

-- Modifies the camera position
callbacks.on_post_calculate_stereo_view_offset(function(device, view_index, world_to_meters, position, rotation, is_double)
    position.z = position.z + 100.0
    position.y = position.y - 100.0
end)



-- UEVR_PluginInitializeParam

-- UEVR_PluginVersion
vr_print("Major: " .. tostring(params.version.major))
vr_print("Minor: " .. tostring(params.version.minor))
vr_print("Patch: " .. tostring(params.version.patch))

-- UEVR_PluginFunctions
vr_print("Is drawing ui: " .. tostring(params.functions.is_drawing_ui()))
params.functions.log_info("Hello from LuaVR!")
params.functions.log_warn("Hello from LuaVR!")
params.functions.log_error("Hello from LuaVR!")

-- UEVR_VRData
vr_print("Runtime ready state: " .. tostring(params.vr.is_runtime_ready()))
vr_print("Is OpenVR: " .. tostring(params.vr.is_openvr()))
vr_print("Is OpenXR: " .. tostring(params.vr.is_openxr()))
vr_print("Is HMD Active: " .. tostring(params.vr.is_hmd_active()))

local standing_origin = UEVR_Vector3f.new()
params.vr.get_standing_origin(standing_origin)
vr_print("Standing Origin: " .. tostring(standing_origin.x) .. ", " .. tostring(standing_origin.y) .. ", " .. tostring(standing_origin.z))

local rotation_offset = UEVR_Vector3f.new()
params.vr.get_rotation_offset(rotation_offset)
vr_print("Rotation Offset: " .. tostring(rotation_offset.x) .. ", " .. tostring(rotation_offset.y) .. ", " .. tostring(rotation_offset.z))

local hmd_index = params.vr.get_hmd_index()
vr_print("HMD Index: " .. tostring(hmd_index))

local left_controller_index = params.vr.get_left_controller_index()
vr_print("Left Controller Index: " .. tostring(left_controller_index))

local right_controller_index = params.vr.get_right_controller_index()
vr_print("Right Controller Index: " .. tostring(right_controller_index))

local hmd_position = UEVR_Vector3f.new()
local hmd_rotation = UEVR_Quaternionf.new()
params.vr.get_pose(hmd_index, hmd_position, hmd_rotation)

vr_print("HMD Position: " .. tostring(hmd_position.x) .. ", " .. tostring(hmd_position.y) .. ", " .. tostring(hmd_position.z))
vr_print("HMD Rotation: " .. tostring(hmd_rotation.x) .. ", " .. tostring(hmd_rotation.y) .. ", " .. tostring(hmd_rotation.z) .. ", " .. tostring(hmd_rotation.w))

if left_controller_index ~= -1 then
    local left_controller_position = UEVR_Vector3f.new()
    local left_controller_rotation = UEVR_Quaternionf.new()
    params.vr.get_pose(left_controller_index, left_controller_position, left_controller_rotation)

    vr_print("Left Controller Position: " .. tostring(left_controller_position.x) .. ", " .. tostring(left_controller_position.y) .. ", " .. tostring(left_controller_position.z))
    vr_print("Left Controller Rotation: " .. tostring(left_controller_rotation.x) .. ", " .. tostring(left_controller_rotation.y) .. ", " .. tostring(left_controller_rotation.z) .. ", " .. tostring(left_controller_rotation.w))        
end

if right_controller_index ~= -1 then
    local right_controller_position = UEVR_Vector3f.new()
    local right_controller_rotation = UEVR_Quaternionf.new()
    params.vr.get_pose(right_controller_index, right_controller_position, right_controller_rotation)

    vr_print("Right Controller Position: " .. tostring(right_controller_position.x) .. ", " .. tostring(right_controller_position.y) .. ", " .. tostring(right_controller_position.z))
    vr_print("Right Controller Rotation: " .. tostring(right_controller_rotation.x) .. ", " .. tostring(right_controller_rotation.y) .. ", " .. tostring(right_controller_rotation.z) .. ", " .. tostring(right_controller_rotation.w))        
end

local left_eye_offset = UEVR_Vector3f.new()
local right_eye_offset = UEVR_Vector3f.new()

params.vr.get_eye_offset(0, left_eye_offset)
params.vr.get_eye_offset(1, right_eye_offset)

vr_print("Left Eye Offset: " .. tostring(left_eye_offset.x) .. ", " .. tostring(left_eye_offset.y) .. ", " .. tostring(left_eye_offset.z))
vr_print("Right Eye Offset: " .. tostring(right_eye_offset.x) .. ", " .. tostring(right_eye_offset.y) .. ", " .. tostring(right_eye_offset.z))

local is_using_controllers = params.vr.is_using_controllers()
vr_print("Is Using Controllers: " .. tostring(is_using_controllers))