Head tracking
Open the live demo · Read the source · View on GitHub
HeadTracker promises one thing: a pose that says which way the head is
turned. SensorHeadTracker, the real implementation, reads that from a
phone's rotation sensor. This page cannot run on one, so it implements the
same interface by hand, driven by a drag of the pointer, and the rig on the
other end cannot tell the two apart.
Step 1: A tracker that is not a sensor #
/// Turns pointer drags into a head pose, the same shape a sensor would.
final class _PointerHeadTracker implements HeadTracker {
final ValueNotifier<HeadPose> _pose = ValueNotifier<HeadPose>(
HeadPose.still(),
);
double _yaw = 0.0;
@override
ValueListenable<HeadPose> get pose => _pose;
void dragBy(double dx) {
_yaw -= dx * 0.01;
_pose.value = HeadPose(
rotation: Quaternion.axisAngle(Vector3(0.0, 1.0, 0.0), _yaw),
position: Vector3.zero(),
);
}
@override
Future<void> start() async {}
@override
Future<void> stop() async {}
}
Step 2: Feed the pose to the rig #
StereoRig.applyHead reads a pose's rotation and position and points the
head node with them, whichever kind of tracker produced it.
void _applyHead() => _rig.applyHead(_tracker.pose.value);
Drag across the page and the view turns, the same way turning a phone would.
Step 3: Check that turning the head turns the gaze #
final before = _rig.gaze();
_tracker.dragBy(200.0);
_applyHead();
final after = _rig.gaze();
StereoRig.gaze reads where the head is looking in world space. Dragging
the pointer changes the tracker's pose, and applying that pose visibly
turns it.