Repository navigation
VR Support
SparkEngine provides a VR/AR integration framework designed for OpenXR. It handles head tracking, stereoscopic rendering, motion controller input, and haptic feedback. Currently a framework stub requiring the OpenXR SDK and a VR runtime (SteamVR, Oculus, WMR).
Source: SparkEngine/Source/Engine/VR/VRSystem.h
Namespace: Spark::VR
CMake toggle: ENABLE_VR=ON
+--------------------------------------------------------------+
| VRSystem |
| (per-instance, not singleton) |
| |
| m_initialized: bool |
| m_headPosition: XMFLOAT3 m_headOrientation: XMFLOAT4 |
| m_leftEye: VREye m_rightEye: VREye |
| m_leftController: VRController m_rightController: VRController|
| m_trackingSpace: VRTrackingSpace (default: RoomScale) |
| m_recommendedWidth: int (1440) m_recommendedHeight: int (1600)|
| |
| Initialize() / Shutdown() |
| UpdateTracking() |
| TriggerHaptic() |
| RecenterTracking() |
+------------------+-------------------+-----------------------+
| |
+----------+------+ +--------+--------+
| VREye | | VRController |
| (per-eye data) | | (controller state)|
| | | |
| viewMatrix | | connected |
| projectionMatrix | | position |
| position | | orientation |
| viewportWidth | | velocity |
| viewportHeight | | angularVelocity |
+------------------+ | triggerValue |
| gripValue |
| thumbstick |
| buttonMask |
+-------------------+
| Type | Responsibility |
|---|---|
VRSystem |
Hardware initialization, tracking updates, per-eye rendering data, controller state, haptic feedback, and console integration |
VRController |
Snapshot of a single motion controller's state: position, orientation, velocity, trigger/grip axes, thumbstick, and button mask |
VREye |
Per-eye rendering data: view matrix, projection matrix, eye world position, and recommended viewport dimensions |
VRTrackingSpace |
Enum selecting between Seated and RoomScale tracking modes |
#include "Engine/VR/VRSystem.h"
using namespace Spark::VR;
VRSystem vr;
if (vr.Initialize())
{
vr.SetTrackingSpace(VRTrackingSpace::RoomScale);
// Main loop
while (running)
{
// Update tracking data once per frame
vr.UpdateTracking();
// Read head pose
auto headPos = vr.GetHeadPosition(); // XMFLOAT3
auto headOri = vr.GetHeadOrientation(); // XMFLOAT4 (quaternion)
// Render left eye
const auto& leftEye = vr.GetLeftEye();
RenderScene(leftEye.viewMatrix, leftEye.projectionMatrix);
// Render right eye
const auto& rightEye = vr.GetRightEye();
RenderScene(rightEye.viewMatrix, rightEye.projectionMatrix);
// Handle controller input
ProcessControllerInput(vr);
}
vr.Shutdown();
}The VRController struct holds the complete state of a single motion controller, updated each frame by VRSystem::UpdateTracking().
struct VRController
{
bool connected = false; // Is the controller active?
DirectX::XMFLOAT3 position{0, 0, 0}; // World-space position
DirectX::XMFLOAT4 orientation{0, 0, 0, 1}; // Orientation quaternion
DirectX::XMFLOAT3 velocity{0, 0, 0}; // Linear velocity (m/s)
DirectX::XMFLOAT3 angularVelocity{0, 0, 0}; // Angular velocity (rad/s)
float triggerValue = 0.0f; // Trigger axis (0.0 to 1.0)
float gripValue = 0.0f; // Grip axis (0.0 to 1.0)
DirectX::XMFLOAT2 thumbstick{0, 0}; // Thumbstick x,y (-1.0 to 1.0)
uint32_t buttonMask = 0; // Bitmask of pressed buttons
};| Member | Type | Range | Description |
|---|---|---|---|
connected |
bool |
true/false | Whether this controller is currently tracked and active. Always check before reading other fields. |
position |
XMFLOAT3 |
world-space | Position in the tracking space, in meters. Origin depends on VRTrackingSpace. |
orientation |
XMFLOAT4 |
unit quaternion | Rotation as a quaternion (x, y, z, w). w=1 is identity. |
velocity |
XMFLOAT3 |
m/s | Linear velocity. Useful for throw estimation and gesture detection. |
angularVelocity |
XMFLOAT3 |
rad/s | Angular velocity around each axis. Useful for spin-throw gestures. |
triggerValue |
float |
0.0 - 1.0 | Analog trigger pull depth. 0.0 = released, 1.0 = fully pressed. |
gripValue |
float |
0.0 - 1.0 | Analog grip squeeze depth. Some controllers have binary grips (0 or 1). |
thumbstick |
XMFLOAT2 |
-1.0 to 1.0 | Thumbstick deflection on x (left/right) and y (forward/back) axes. |
buttonMask |
uint32_t |
bitmask | Bitfield of currently pressed buttons. Use bitwise AND to check specific buttons. |
const auto& left = vr.GetLeftController();
const auto& right = vr.GetRightController();
if (right.connected)
{
// Position and orientation
auto pos = right.position;
auto ori = right.orientation;
// Analog inputs
float trigger = right.triggerValue; // 0.0 to 1.0
float grip = right.gripValue; // 0.0 to 1.0
auto stick = right.thumbstick; // x,y: -1.0 to 1.0
// Button state
uint32_t buttons = right.buttonMask;
// Velocity (useful for throw physics)
auto vel = right.velocity;
auto angVel = right.angularVelocity;
}The VREye struct contains all data needed to render a single eye's view.
struct VREye
{
DirectX::XMFLOAT4X4 viewMatrix; // Eye view matrix
DirectX::XMFLOAT4X4 projectionMatrix; // Eye projection matrix
DirectX::XMFLOAT3 position{0, 0, 0}; // Eye world position
int viewportWidth = 0; // Render target width per eye
int viewportHeight = 0; // Render target height per eye
};| Member | Type | Description |
|---|---|---|
viewMatrix |
XMFLOAT4X4 |
The view transformation matrix for this eye. Accounts for head tracking position/orientation plus the eye offset (half IPD). |
projectionMatrix |
XMFLOAT4X4 |
The projection matrix for this eye. Uses the asymmetric frustum reported by the VR runtime for correct lens distortion. |
position |
XMFLOAT3 |
The world-space position of this eye. Differs from head position by the inter-pupillary distance (IPD) offset. |
viewportWidth |
int |
Recommended render target width for this eye, as reported by the VR runtime. |
viewportHeight |
int |
Recommended render target height for this eye. |
// Get per-eye render target dimensions
int width, height;
vr.GetRecommendedRenderSize(width, height);
// Create render targets (once, at initialization)
auto leftRT = CreateRenderTarget(width, height);
auto rightRT = CreateRenderTarget(width, height);
// Per frame
vr.UpdateTracking();
const auto& leftEye = vr.GetLeftEye();
const auto& rightEye = vr.GetRightEye();
// Render left eye
SetRenderTarget(leftRT);
SetViewport(0, 0, leftEye.viewportWidth, leftEye.viewportHeight);
RenderScene(leftEye.viewMatrix, leftEye.projectionMatrix);
// Render right eye
SetRenderTarget(rightRT);
SetViewport(0, 0, rightEye.viewportWidth, rightEye.viewportHeight);
RenderScene(rightEye.viewMatrix, rightEye.projectionMatrix);
// Submit both textures to the VR compositor
SubmitFrames(leftRT, rightRT);enum class VRTrackingSpace
{
Seated, // Seated or standing in a small area
RoomScale // Full room-scale tracking with boundary
};| Value | Use Case | Origin | Description |
|---|---|---|---|
Seated |
Cockpit games, seated experiences | Head position at launch / recenter | The origin is at the user's head when they start or recenter. Small positional tracking area. Suitable for gamepad-based games or cockpit simulators. |
RoomScale |
FPS, room-scale experiences | Floor center of play area | The origin is at floor level in the center of the user's configured play area. Full positional tracking within the boundary. Default value. |
vr.SetTrackingSpace(VRTrackingSpace::RoomScale);
// Get the current tracking space
VRTrackingSpace space = vr.GetTrackingSpace();
// Recenter seated position (only meaningful for Seated mode)
vr.RecenterTracking();| Method | Signature | Description |
|---|---|---|
| Constructor | VRSystem() |
Construct a VR system instance. Does not initialize hardware. |
| Destructor | ~VRSystem() |
Destroy the instance. Calls Shutdown() if still initialized. |
Initialize |
bool Initialize() |
Connect to the VR runtime and initialize hardware. Returns true if VR hardware was found and initialized. |
Shutdown |
void Shutdown() |
Release all VR resources and disconnect from the runtime. |
IsAvailable |
bool IsAvailable() const |
Check if VR is initialized and available. Returns m_initialized. |
| Method | Signature | Description |
|---|---|---|
UpdateTracking |
void UpdateTracking() |
Update all tracking data: head position/orientation, eye matrices, and controller states. Call once per frame before rendering. |
GetHeadPosition |
DirectX::XMFLOAT3 GetHeadPosition() const |
Get the head position in world space. |
GetHeadOrientation |
DirectX::XMFLOAT4 GetHeadOrientation() const |
Get the head orientation as a quaternion (x, y, z, w). |
| Method | Signature | Description |
|---|---|---|
GetLeftEye |
const VREye& GetLeftEye() const |
Get the left eye rendering data (view matrix, projection matrix, position, viewport). |
GetRightEye |
const VREye& GetRightEye() const |
Get the right eye rendering data. |
GetRecommendedRenderSize |
void GetRecommendedRenderSize(int& width, int& height) const |
Get the recommended per-eye render target resolution. Default values: 1440x1600. |
| Method | Signature | Description |
|---|---|---|
GetLeftController |
const VRController& GetLeftController() const |
Get the left controller state. |
GetRightController |
const VRController& GetRightController() const |
Get the right controller state. |
TriggerHaptic |
void TriggerHaptic(bool isLeft, float amplitude, float duration) |
Trigger haptic feedback on a controller. isLeft=true for left controller, false for right. amplitude is 0.0 to 1.0. duration is in seconds. |
| Method | Signature | Description |
|---|---|---|
SetTrackingSpace |
void SetTrackingSpace(VRTrackingSpace space) |
Set the tracking space type (Seated or RoomScale). |
GetTrackingSpace |
VRTrackingSpace GetTrackingSpace() const |
Get the current tracking space. |
RecenterTracking |
void RecenterTracking() |
Reset the seated position origin to the current head position. Primarily useful in Seated mode. |
| Method | Signature | Description |
|---|---|---|
Console_GetStatus |
std::string Console_GetStatus() const |
Return a status string with initialization state, tracking space, head position, controller connectivity, and recommended render size. |
| Member | Type | Default | Description |
|---|---|---|---|
m_initialized |
bool |
false |
Whether the VR system has been successfully initialized |
m_headPosition |
DirectX::XMFLOAT3 |
{0, 0, 0} |
Current head position in world space |
m_headOrientation |
DirectX::XMFLOAT4 |
{0, 0, 0, 1} |
Current head orientation quaternion (identity = looking forward) |
m_leftEye |
VREye |
(default) | Left eye rendering data |
m_rightEye |
VREye |
(default) | Right eye rendering data |
m_leftController |
VRController |
(default, disconnected) | Left motion controller state |
m_rightController |
VRController |
(default, disconnected) | Right motion controller state |
m_trackingSpace |
VRTrackingSpace |
RoomScale |
Current tracking space configuration |
m_recommendedWidth |
int |
1440 |
Recommended per-eye render width |
m_recommendedHeight |
int |
1600 |
Recommended per-eye render height |
The VR system depends on platform-specific types from Core/Platform.h:
#ifdef SPARK_PLATFORM_WINDOWS
#include <DirectXMath.h>
#endifAll position, orientation, and matrix types use DirectXMath (XMFLOAT3, XMFLOAT4, XMFLOAT4X4, XMFLOAT2). On Linux/macOS, these types are provided by the cross-platform stubs in Core/Platform.h.
Haptic feedback provides physical vibration in the motion controllers. Common use cases include weapon firing, collision feedback, UI interaction confirmation, and environmental effects.
// Right controller, 50% intensity, 0.1 second burst
vr.TriggerHaptic(false, 0.5f, 0.1f);
// Left controller, full intensity, 0.3 second buzz
vr.TriggerHaptic(true, 1.0f, 0.3f);
// Light tap on right controller
vr.TriggerHaptic(false, 0.2f, 0.05f);| Parameter | Type | Range | Description |
|---|---|---|---|
isLeft |
bool |
true/false |
true = left controller, false = right controller |
amplitude |
float |
0.0 - 1.0 | Vibration intensity. 0.0 = off, 1.0 = maximum. |
duration |
float |
seconds | Duration of the haptic pulse. Very short values (0.01-0.05s) create taps; longer values (0.1-0.5s) create buzzes. |
| Pattern | Amplitude | Duration | Use Case |
|---|---|---|---|
| Light tap | 0.1 - 0.2 | 0.02 - 0.05s | UI button hover, soft contact |
| Button press | 0.3 - 0.5 | 0.05 - 0.1s | UI selection, object pickup |
| Impact | 0.6 - 0.8 | 0.1 - 0.2s | Weapon hit, collision, landing |
| Heavy impact | 0.9 - 1.0 | 0.2 - 0.4s | Explosion, heavy weapon fire |
| Continuous buzz | 0.2 - 0.4 | Per frame | Holding a vibrating object, engine rumble |
The VR runtime reports the optimal per-eye resolution based on the headset's native display resolution and any supersampling configured by the user.
int width, height;
vr.GetRecommendedRenderSize(width, height);
// Default: 1440 x 1600 (typical for Quest 2 / Index-class headsets)
// Create per-eye render targets at the recommended size
auto leftRT = GraphicsEngine::CreateRenderTarget(width, height, DXGI_FORMAT_R8G8B8A8_UNORM);
auto rightRT = GraphicsEngine::CreateRenderTarget(width, height, DXGI_FORMAT_R8G8B8A8_UNORM);For performance tuning, you can render at a fraction of the recommended resolution:
float renderScale = 0.8f; // 80% of recommended resolution
int scaledWidth = static_cast<int>(width * renderScale);
int scaledHeight = static_cast<int>(height * renderScale);| Headset | Per-Eye Resolution | Refresh Rate | Recommended Width x Height |
|---|---|---|---|
| Meta Quest 2 | 1832 x 1920 | 72/90/120 Hz | ~1440 x 1600 |
| Meta Quest 3 | 2064 x 2208 | 90/120 Hz | ~1680 x 1760 |
| Valve Index | 1440 x 1600 | 80/90/120/144 Hz | 1440 x 1600 |
| HTC Vive Pro 2 | 2448 x 2448 | 90/120 Hz | ~2000 x 2000 |
| HP Reverb G2 | 2160 x 2160 | 90 Hz | ~2160 x 2160 |
Always check connected before reading controller data. Controllers may disconnect during gameplay (battery loss, out of tracking range).
const auto& right = vr.GetRightController();
if (!right.connected)
{
// Show "controller disconnected" notification
ShowNotification("Right controller lost. Please move it into tracking range.");
return;
}Analog axes require thresholding to distinguish intentional presses from accidental touches:
const float TRIGGER_THRESHOLD = 0.7f;
const float GRIP_THRESHOLD = 0.5f;
bool isFiring = right.triggerValue > TRIGGER_THRESHOLD;
bool isGripping = right.gripValue > GRIP_THRESHOLD;
// Thumbstick dead zone
const float DEADZONE = 0.15f;
float stickX = std::abs(right.thumbstick.x) > DEADZONE ? right.thumbstick.x : 0.0f;
float stickY = std::abs(right.thumbstick.y) > DEADZONE ? right.thumbstick.y : 0.0f;When the player releases a grabbed object, use the controller's velocity to calculate throw force:
void OnObjectReleased(PhysicsBody& body)
{
const auto& controller = vr.GetRightController();
// Apply linear velocity
DirectX::XMFLOAT3 throwVel = controller.velocity;
// Scale up slightly for a more satisfying throw feel
constexpr float THROW_SCALE = 1.5f;
throwVel.x *= THROW_SCALE;
throwVel.y *= THROW_SCALE;
throwVel.z *= THROW_SCALE;
body.SetLinearVelocity(throwVel);
body.SetAngularVelocity(controller.angularVelocity);
}Suitable for cockpit games, racing simulators, or experiences where the player remains seated. The origin is at the player's head position when Initialize() is called or RecenterTracking() is invoked.
vr.SetTrackingSpace(VRTrackingSpace::Seated);
// Player presses "recenter" button
vr.RecenterTracking();
// Head position resets to (0, 0, 0) relative to the seated originSuitable for FPS games, room-scale experiences, and any game where the player physically walks. The origin is at floor level in the center of the play area boundary.
vr.SetTrackingSpace(VRTrackingSpace::RoomScale);
// Head position reports absolute position relative to the room center
auto headPos = vr.GetHeadPosition();
// headPos.y represents actual height above the floor| Mode | Origin | Y-Axis Zero | Best For |
|---|---|---|---|
Seated |
User's head at recenter time | Head height | Cockpit sims, seated games, gamepad VR |
RoomScale |
Floor center of play area | Floor level | FPS, action games, room-scale experiences |
The VR system integrates with the existing Rendering and Graphics pipeline. The key modification is rendering the scene twice (once per eye) with different view and projection matrices.
Standard Render Loop: VR Render Loop:
1. UpdateTracking() 1. vr.UpdateTracking()
2. RenderScene(camera) 2. RenderScene(leftEye)
3. PostProcess 3. PostProcess (left)
4. Present 4. RenderScene(rightEye)
5. PostProcess (right)
6. SubmitToCompositor
Some post-processing effects should be disabled or modified in VR:
| Effect | VR Recommendation | Reason |
|---|---|---|
| Motion blur | Disable | The VR compositor handles temporal prediction. Motion blur causes severe nausea. |
| Depth of field | Disable | Conflicts with natural eye accommodation. The user's eyes perform their own focus. |
| Chromatic aberration | Disable | HMD lenses already introduce chromatic aberration that the runtime corrects. |
| Film grain | Disable | Appears unnatural at close viewing distance in VR. |
| Bloom | Reduce intensity | Can be distracting at VR close viewing distance. Use subtle amounts. |
| Tonemapping | Use ACES | Standard tonemapping works well in VR. |
| SSAO | Optional | Expensive at VR resolution. Consider baked ambient occlusion instead. |
| Anti-aliasing | MSAA recommended | TAA can cause ghosting with head motion. MSAA 4x is preferred. |
VR requires consistently hitting the headset's refresh rate. Dropped frames cause judder and motion sickness.
| Refresh Rate | Frame Budget | GPU Recommendation |
|---|---|---|
| 72 Hz | 13.9 ms | GTX 1070 / RX 5600 XT |
| 90 Hz | 11.1 ms | RTX 2070 / RX 6700 XT |
| 120 Hz | 8.3 ms | RTX 3080 / RX 6800 XT |
| 144 Hz | 6.9 ms | RTX 4080 / RX 7900 XT |
- Use the recommended render size. Do not oversample beyond what the runtime recommends.
- Reduce shadow quality. Use 2 shadow cascades instead of 4. Lower shadow map resolution to 1024.
- Aggressive LOD. Increase LOD bias to push objects to lower detail levels earlier.
- Limit draw calls. Use instancing and batching aggressively. Target under 1500 draw calls.
- Disable expensive post-processing. See the table above.
- Use MSAA instead of TAA. 4x MSAA avoids the temporal ghosting artifacts that TAA produces with VR head motion.
The current VR system is a framework stub. The public API and data structures are fully defined, but the actual OpenXR integration requires:
-
OpenXR SDK: The OpenXR loader and headers must be available at build time when
ENABLE_VR=ON. - VR Runtime: A compatible runtime must be installed (SteamVR, Meta OpenXR, Windows Mixed Reality OpenXR).
-
Graphics Binding: The
Initialize()method needs to create the OpenXR graphics binding for D3D11 (usingXR_KHR_D3D11_enable). -
Frame Loop Integration: The
UpdateTracking()method needs to callxrWaitFrame,xrBeginFrame, and the rendering code needs to callxrEndFrame.
The stub provides default values for all tracking data (zero positions, identity orientations, default render size) so that VR-dependent code can be developed and tested without VR hardware.
| Command | Description |
|---|---|
vr_status |
Show VR system status including: initialized state, tracking space, head position, head orientation, left/right controller connectivity, and recommended render size. Calls Console_GetStatus() internally. |
> vr_status
VR System Status:
Initialized: true
Tracking Space: RoomScale
Head Position: (0.12, 1.65, -0.34)
Head Orientation: (0.00, 0.02, 0.00, 1.00)
Left Controller: Connected
Position: (-0.25, 1.10, -0.40)
Trigger: 0.00 Grip: 0.00
Right Controller: Connected
Position: (0.30, 1.05, -0.35)
Trigger: 0.85 Grip: 0.12
Recommended Render Size: 1440 x 1600
#include "Engine/VR/VRSystem.h"
#include "Graphics/GraphicsEngine.h"
using namespace Spark::VR;
class VRGameApp
{
public:
bool Initialize()
{
m_vr = std::make_unique<VRSystem>();
if (!m_vr->Initialize())
{
LOG_WARN("VR hardware not found, falling back to flat-screen mode");
m_vrEnabled = false;
return true; // Continue without VR
}
m_vrEnabled = true;
m_vr->SetTrackingSpace(VRTrackingSpace::RoomScale);
// Create per-eye render targets
int width, height;
m_vr->GetRecommendedRenderSize(width, height);
m_leftEyeRT = CreateRenderTarget(width, height);
m_rightEyeRT = CreateRenderTarget(width, height);
return true;
}
void Update(float deltaTime)
{
if (!m_vrEnabled) return;
m_vr->UpdateTracking();
// Use head position for camera
auto headPos = m_vr->GetHeadPosition();
auto headOri = m_vr->GetHeadOrientation();
UpdateCamera(headPos, headOri);
// Process controller input
ProcessInput();
}
void Render()
{
if (!m_vrEnabled)
{
RenderFlatScreen();
return;
}
// Render left eye
const auto& leftEye = m_vr->GetLeftEye();
SetRenderTarget(m_leftEyeRT);
RenderScene(leftEye.viewMatrix, leftEye.projectionMatrix);
// Render right eye
const auto& rightEye = m_vr->GetRightEye();
SetRenderTarget(m_rightEyeRT);
RenderScene(rightEye.viewMatrix, rightEye.projectionMatrix);
}
void ProcessInput()
{
const auto& right = m_vr->GetRightController();
if (!right.connected) return;
// Fire weapon on trigger press
if (right.triggerValue > 0.8f)
{
FireWeapon(right.position, right.orientation);
m_vr->TriggerHaptic(false, 0.6f, 0.1f); // Recoil feedback
}
// Grab objects on grip
if (right.gripValue > 0.5f)
{
TryGrabObject(right.position);
}
// Movement on left thumbstick
const auto& left = m_vr->GetLeftController();
if (left.connected)
{
float moveX = left.thumbstick.x;
float moveY = left.thumbstick.y;
MovePlayer(moveX, moveY);
}
}
void Shutdown()
{
if (m_vr)
{
m_vr->Shutdown();
}
}
private:
std::unique_ptr<VRSystem> m_vr;
bool m_vrEnabled = false;
RenderTarget m_leftEyeRT;
RenderTarget m_rightEyeRT;
};- Verify that a VR runtime is installed and running (SteamVR, Oculus app, WMR Portal).
- Verify that the headset is connected and powered on.
- Ensure
ENABLE_VR=ONwas set during CMake configuration. - Check that the OpenXR SDK libraries are available at build time.
- Check the engine log for specific initialization error messages.
If GetHeadPosition() returns {0, 0, 0} and orientation is identity, ensure:
-
UpdateTracking()is being called each frame. -
Initialize()returnedtrue. - The headset is being worn or placed within tracking range.
- The tracking space is calibrated in the VR runtime settings.
- Ensure controllers are powered on and paired with the headset.
- Ensure controllers are within tracking range (visible to sensors/cameras).
- Check battery levels.
- Some runtimes require a "wake up" gesture (pressing a button).
- Check GPU frame time. VR requires rendering the scene twice at high resolution.
- Reduce shadow quality, disable SSAO, and lower LOD bias.
- Ensure MSAA is used instead of TAA (TAA ghosting is worse in VR).
- Disable motion blur, depth of field, and chromatic aberration.
- Use
vr_statusin the console to verify recommended render size is not excessively high.
- Verify the controller is connected.
- Ensure
amplitudeis greater than 0.0 anddurationis greater than 0.0. - Some runtimes have a minimum haptic duration; try at least 0.01s.
- Check that the VR runtime supports haptics for the connected controller model.
| Scenario | Behavior |
|---|---|
Initialize() called when no VR hardware present |
Returns false. IsAvailable() returns false. All tracking data returns defaults. |
UpdateTracking() called before Initialize()
|
No-op. Tracking data remains at default values. |
TriggerHaptic() called when VR not initialized |
No-op. No crash. |
Shutdown() called when not initialized |
Safe no-op. |
Shutdown() called multiple times |
Safe; sets m_initialized = false on first call. |
| Controller disconnects mid-frame |
connected field becomes false on next UpdateTracking(). Other fields retain last known values. |
| Headset removed during gameplay | Runtime signals session state change. Tracking data may freeze at last known values. |
RecenterTracking() in RoomScale mode |
Resets the origin for seated-style recenter. Most useful in Seated mode. |
To enable VR support in the build:
cmake -B build -DENABLE_VR=ON
# Or with a preset
cmake --preset windows-release -DENABLE_VR=ONWhen ENABLE_VR=OFF (default), the VR source files are excluded from the build and all VR-related code paths are compiled out via preprocessor guards.
- Rendering and Graphics -- Stereoscopic rendering pipeline, render target management
- Input System -- Standard controller input alongside VR input
- Physics -- VR hand interaction with physics objects, rigid body constraints
- Entity-Component-System -- Attaching VR tracking to entities
- UI System -- World-space UI rendering for VR menus
- Audio -- Spatial audio is critical for VR immersion (3D sound positioning)
- SparkEditor -- Editor VR preview mode
- Build System -- CMake toggles for VR support
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers