flutter3d
Showcase Changelog 38 packages API reference

Picking by pixel: the source

since 0.4.3 Views, picking and input

Regions the guide quotes: pick (line 43).

1/// Asking the renderer which mesh is drawn at a point, by drawing the scene
2/// once more as ids and reading one pixel back.
3///
4/// Quoted by `pixel_picking.md` and shown whole in the Source tab.
5library;
6
7import 'package:flutter3d/flutter3d.dart';
8import 'package:flutter3d_showcase/src/demo/demo.dart';
9import 'package:vector_math/vector_math.dart';
11final class PixelPickingDemo extends ShowcaseDemo {
12 late final CameraNode _camera;
13 final Raycaster _raycaster = Raycaster();
15 /// What the last question answered, once the frame that drew the ids has
16 /// come back. Null until then, and null again for a click on nothing.
17 MeshNode? lastAnswer;
19 @override
20 Scene build(DemoContext context) {
21 _camera = context.camera;
22 final Material stone = Material(
23 name: 'stone',
24 baseColor: Vector4(0.45, 0.6, 0.7, 1.0),
25 roughness: 0.6,
26 );
27 final MeshNode ball = MeshNode(
28 DeviceMesh.upload(
29 context.device,
30 SphereShape(segments: 32, rings: 16).build(),
31 ),
32 stone,
33 name: 'ball',
34 );
35 return Scene()
36 ..add(ball)
37 ..add(
38 LightNode(name: 'sun', intensity: 3.0)
39 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
40 );
41 }
43 void _pickCentre(DemoContext context) {
44 context.renderer
45 .pickPixel(0.5, 0.5)
46 .then((MeshNode? node) => lastAnswer = node);
47 }
49 @override
50 void update(DemoContext context, double dt) => _pickCentre(context);
52 @override
53 void verify(Scene scene, FrameResult frame) {
54 if (frame.drawCalls < 1) {
55 throw StateError('the ball was not drawn');
56 }
57 // pickPixel answers a frame or two after it is asked, on a completer this
58 // page does not block on. The honest, synchronous half of the claim is
59 // that (0.5, 0.5), the middle of the frame, really is over the mesh the
60 // id pass would find there. A raycaster through the same point checks
61 // exactly that, with no frame involved.
62 final HitResult? hit = _raycaster
63 .setFromNdc(_camera, 0.0, 0.0, aspect: 16 / 9)
64 .intersectScene(scene);
65 if (hit?.node?.name != 'ball') {
66 throw StateError('the pixel asked about is not over the ball');
67 }
68 }