step method

void step(
  1. double dt, {
  2. bool integrateInput = true,
})

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();
}