OpenXR
The OpenXR plugin integrates Tellusim SDK with the OpenXR API, enabling seamless interoperability between Tellusim and OpenXR applications. It supports Windows, Linux, and Android platforms, and works with D3D12, Vulkan, OpenGL, and OpenGLES APIs.
- The XR namespace provides functions for managing and interacting with OpenXR instances and systems.
- The XRSystem class encapsulates an OpenXR system, offering functionality to manage systems, sessions, view configurations, reference spaces, and swap chains.
- The XRInput class manages input for virtual reality (VR) and augmented reality (AR) devices, integrating with the OpenXR API within Tellusim SDK.
- The XRWindow class extends the base Window class, adding functionality for VR/AR systems such as managing OpenXR sessions, handling surfaces, and retrieving headset and input data.
info
This plugin is integrated into Tellusim Executor.
#include <platform/openxr/include/TellusimXR.h>
#include <platform/openxr/include/TellusimXRInput.h>
#include <platform/openxr/include/TellusimXRWindow.h>
See Readme.txt for Android build instructions.
Example
The following example code demonstrates how to set up an OpenXR window, handle input from VR/AR controllers, and render content using Tellusim SDK integrated with the OpenXR API:
// create OpenXR window
auto xr_window = makeAutoPtr(new XRWindow(app.getPlatform(), app.getDevice()));
Window &window = xr_window.ref();
DECLARE_WINDOW_CALLBACKS
String title = String::format("%s Tellusim::XRWindow", window.getPlatformName());
if(!window.create(title) || !window.setHidden(false)) return false;
// left controller
XRInput left_input;
if(!left_input.create(xr_window.get(), XRInput::HandLeft)) return false;
uint32_t l_aim_id = left_input.addMatrix4x3f(XRInput::InputAim, XRInput::ProfileOculus);
uint32_t l_offset_id = left_input.addVector2f(XRInput::InputThumb, XRInput::ProfileOculus);
uint32_t l_trigger_id = left_input.addScalarf32(XRInput::InputTrigger, XRInput::ProfileOculus);
uint32_t l_squeeze_id = left_input.addScalarf32(XRInput::InputSqueeze, XRInput::ProfileOculus);
uint32_t l_button_id = left_input.addBool(XRInput::InputX, XRInput::ProfileOculus);
// right controller
XRInput right_input;
if(!right_input.create(xr_window.get(), XRInput::HandRight)) return false;
uint32_t r_aim_id = right_input.addMatrix4x3f(XRInput::InputAim, XRInput::ProfileOculus);
uint32_t r_offset_id = right_input.addVector2f(XRInput::InputThumb, XRInput::ProfileOculus);
uint32_t r_trigger_id = right_input.addScalarf32(XRInput::InputTrigger, XRInput::ProfileOculus);
uint32_t r_squeeze_id = right_input.addScalarf32(XRInput::InputSqueeze, XRInput::ProfileOculus);
uint32_t r_button_id = right_input.addBool(XRInput::InputA, XRInput::ProfileOculus);
// OpenXR instance info
const auto &properties = XR::getProperties();
TS_LOGF(Verbose, "Layers: %s\n", XR::getLayers().get());
TS_LOGF(Verbose, "Extensions: %s\n", XR::getExtensions().get());
TS_LOGF(Verbose, "Runtime Name: %s\n", properties.runtimeName);
TS_LOGF(Verbose, "Runtime Version: %u.%u.%u\n", XR_VERSION_MAJOR(properties.runtimeVersion), XR_VERSION_MINOR(properties.runtimeVersion), XR_VERSION_PATCH(properties.runtimeVersion));
TS_LOGF(Verbose, "Window: %s %ux%u\n", window.getPlatformName(), window.getWidth(), window.getHeight());
TS_LOGF(Verbose, "Color Format: %s\n", getFormatName(window.getColorFormat()));
TS_LOGF(Verbose, "Depth Format: %s\n", getFormatName(window.getDepthFormat()));
TS_LOGF(Verbose, "Swap Chains: %u\n", xr_window->getNumSwapChains());
// create targets
Array<Target> targets;
for(uint32_t i = 0; i < xr_window->getNumSurfaces(); i++) {
Surface surface = xr_window->getSurface(i);
Target target = device.createTarget(surface);
target.setClearColor(Color(0.2f, 0.2f, 0.2f, 1.0f).gammaToLinear());
target.setClearDepth(0.0f);
targets.append(target);
}
// window targets
for(uint32_t i = 0; i < targets.size() && !window.isMinimized(); i++) {
// common parameters
CommonParameters common_parameters;
common_parameters.projection = xr_window->getProjection(i, 0.01f);
common_parameters.modelview = Matrix4x4f(xr_window->getModelview(i) * window_transform * Matrix4x3f::translate(-position));
if(targets[i].isFlipped()) common_parameters.projection = Matrix4x4f::scale(1.0f, -1.0f, 1.0f) * common_parameters.projection;
common_parameters.camera = Vector4f(window_itransform * xr_window->getPosition() + position, 0.0f);
// window target
targets[i].begin();
{
// create command list
Command command = device.createCommand(targets[i]);
// set pipeline
command.setPipeline(pipeline);
// set buffers
model.setBuffers(command);
// left input
Matrix4x3f left_transform = window_itransform * left_input.getMatrix4x3f(l_aim_id);
if(left_transform != Matrix4x3f::zero) {
Vector2f offset = left_input.getVector2f(l_offset_id) * 0.05f;
float32_t scale = left_input.getScalarf32(l_trigger_id) + 1.0f;
scale /= left_input.getScalarf32(l_squeeze_id, 1.0f) + 1.0f;
common_parameters.transform = Matrix4x4f::translate(position) * Matrix4x4f(left_transform) * Matrix4x4f::translate(offset.x, 0.0f, -offset.y) * Matrix4x4f::scale(scale * 0.05f);
command.setUniform(0, common_parameters);
model.draw(command);
}
// right input
Matrix4x3f right_transform = window_itransform * right_input.getMatrix4x3f(r_aim_id);
if(right_transform != Matrix4x3f::zero && !right_input.getBool(r_button_id)) {
Vector2f offset = right_input.getVector2f(r_offset_id) * 0.05f;
float32_t scale = right_input.getScalarf32(r_trigger_id) + 1.0f;
scale /= right_input.getScalarf32(r_squeeze_id) + 1.0f;
common_parameters.transform = Matrix4x4f::translate(position) * Matrix4x4f(right_transform) * Matrix4x4f::translate(offset.x, 0.0f, -offset.y) * Matrix4x4f::scale(scale * 0.05f);
command.setUniform(0, common_parameters);
model.draw(command);
}
// draw canvas
canvas.setViewport(Viewport(-1.0f, -1.0f, 2.0f, 2.0f, 0.0f, -1.0f));
Matrix4x4f transform = Matrix4x4f::scale(1.0f / 200.0f, 1.0f / 200.0f, 1.0f) * Matrix4x4f::translate(-1920.0f / 2.0f, -1080.0f / 2.0f, 0.0f);
if(right_input.getBool(r_button_id)) transform = Matrix4x4f::translate(position) * Matrix4x4f(right_transform) * Matrix4x4f::rotateX(-90.0f) * Matrix4x4f::scale(0.5f) * transform;
else transform = Matrix4x4f::translate(0.0f, 1.0f, -0.1f) * Matrix4x4f::rotateX(90.0f) * transform;
canvas.setTransform(common_parameters.projection * common_parameters.modelview * transform);
canvas.draw(command);
}
targets[i].end();
}