step method
Advances simulation without drawing or scheduling another frame.
External GPU hosts pass integrateInput false: their renderer owns
locomotion, tracking and the active ray, so input is never applied twice.
Implementation
void step(double dt, {bool integrateInput = true}) {
if (!dt.isFinite || dt <= 0) return;
dt = dt.clamp(0.0, 0.1);
_frameCount++;
// FPS calculation (smoothed)
_frameTime = dt * 1000;
_fps = _fps * 0.9 + (1.0 / dt) * 0.1;
// Asynchronous Time Warp (ATW) Check:
// If the frame time is above 18ms (frame drop), calculate rotation delta and set ATW matrices
final currentRotation = cameraRig.rotation;
if (_frameTime > 18.0 && _lastCameraRotation != null) {
final delta = currentRotation * _lastCameraRotation!.inverted();
final atwMatrix = Matrix4.compose(
Vector3.zero(),
delta,
Vector3.all(1.0),
);
renderPass.leftAtwMatrix = atwMatrix;
renderPass.rightAtwMatrix = atwMatrix;
renderPass.useATWFallback = true;
} else {
renderPass.leftAtwMatrix = null;
renderPass.rightAtwMatrix = null;
renderPass.useATWFallback = false;
}
_lastCameraRotation = currentRotation.clone();
if (integrateInput) updateInput(dt);
if (!integrateInput) spatialInput.update(dt);
// Update scene
scene.update(dt);
// Update gaze pointer and interactables if active
if (gazePointer != null) {
final pointer = gazePointer!;
final hit = _nearestPointableHit();
final progressBeforeUpdate = pointer.dwellProgress;
pointer.update(dt, hit?.node.name, dwellEnabled: isDwellEnabled);
// Dwell selection activates the same Pointable used by a physical tap.
if (hit != null &&
progressBeforeUpdate < 1 &&
pointer.dwellProgress >= 1) {
hit.node.pointable?.press(hit);
Future.delayed(const Duration(milliseconds: 100), () {
hit.node.pointable?.release();
});
}
// Traversal to update Pointable hover states
scene.root.traverse((node) {
if (node.pointable != null) {
final isGazingThisNode = hit != null && hit.node == node;
node.pointable!.updateHover(isGazingThisNode);
}
});
}
// Update desktop locomotion (WASD) if active
if (integrateInput) desktopInput?.update(dt);
// Custom update callback
onUpdate?.call(dt);
// Notify the CustomPainter to redraw!
notifyListeners();
}