Orbit: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: bounds (line 35), frame (line 40), swing (line 43), advance (line 53).
1/// Framing a model automatically, and swinging the view to a new angle
2/// instead of jumping to it.
3///
4/// Quoted by `orbit_controller.md` and shown whole in the Source tab.
5library;
7import 'dart:math' as math;
9import 'package:flutter3d/flutter3d.dart';
10import 'package:flutter3d_showcase/src/demo/demo.dart';
11import 'package:vector_math/vector_math.dart';
13final class OrbitControllerDemo extends ShowcaseDemo {
14 late final Aabb3 _bounds;
15 late final OrbitController _orbit;
16 late final double _startYaw;
19 Scene build(DemoContext context) {
20 _orbit = context.orbit;
21 final Material stone = Material(
22 name: 'stone',
23 baseColor: Vector4(0.6, 0.66, 0.7, 1.0),
24 roughness: 0.7,
25 );
26 final MeshNode box = MeshNode(
27 DeviceMesh.upload(
28 context.device,
29 CuboidShape(size: Vector3(1.0, 2.0, 1.0)).build(),
30 ),
31 stone,
32 name: 'box',
33 );
35 _bounds = Aabb3.centerAndHalfExtents(
36 Vector3.zero(),
37 Vector3(0.5, 1.0, 0.5),
38 );
40 context.orbit.frameBounds(_bounds);
42 _startYaw = context.orbit.yaw;
43 context.orbit.animateTo(yaw: context.orbit.yaw + math.pi, seconds: 1.2);
45 return Scene()
46 ..add(box)
47 ..add(
48 LightNode(name: 'sun', intensity: 3.0)
49 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
50 );
51 }
54 void update(DemoContext context, double dt) {
55 context.orbit.advance(dt);
56 }
59 List<DemoControl> controls(DemoContext context) => <DemoControl>[
60 ToggleControl(
61 'Swing to the other side',
62 value: () => context.orbit.isTurning,
63 onChanged: (bool _) =>
64 context.orbit.animateTo(yaw: context.orbit.yaw + math.pi),
65 ),
66 ];
69 void verify(Scene scene, FrameResult frame) {
70 if (frame.drawCalls < 1) {
71 throw StateError('the box was not drawn');
72 }
73 // frameBounds places the camera at radius / sin(fov / 2) * margin, using
74 // its own default field of view and margin. Recomputing that formula for
75 // this box's own radius has to land on the distance the orbit actually
76 // holds.
77 final Vector3 extent = (_bounds.max - _bounds.min)..scale(0.5);
78 final double radius = math.max(extent.length, 1e-4);
79 final double expected = radius / math.sin(math.pi / 4 * 0.5) * 1.25;
80 if ((_orbit.distance - expected).abs() > 1e-6) {
81 throw StateError(
82 'frameBounds put the camera at ${_orbit.distance}, expected $expected',
83 );
84 }
85 // The camera also spent one frame turning towards the other side, so its
86 // yaw must have moved away from wherever frameBounds first pointed it,
87 // and the turn must still be in flight after a sixtieth of a second of a
88 // 1.2 second swing.
89 if (_orbit.yaw == _startYaw || !_orbit.isTurning) {
90 throw StateError('advance did not step the turn animateTo started');
91 }
92 }
93}