CPU raycasting: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: raycaster (line 15), cast (line 44).
1/// Finding which mesh a ray hits, entirely on the CPU: no frame, no readback,
2/// an answer the moment it is asked.
3///
4/// Quoted by `raycast.md` and shown whole in the Source tab.
5library;
7import 'package:flutter3d/flutter3d.dart';
8import 'package:flutter3d_showcase/src/demo/demo.dart';
9import 'package:vector_math/vector_math.dart';
11final class RaycastDemo extends ShowcaseDemo {
12 late final CameraNode _camera;
13 late final Scene _scene;
15 final Raycaster _raycaster = Raycaster();
17 HitResult? lastHit;
20 Scene build(DemoContext context) {
21 _camera = context.camera;
22 final Material stone = Material(
23 name: 'stone',
24 baseColor: Vector4(0.7, 0.5, 0.4, 1.0),
25 roughness: 0.7,
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 _scene = Scene()
36 ..add(ball)
37 ..add(
38 LightNode(name: 'sun', intensity: 3.0)
39 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
40 );
41 return _scene;
42 }
44 void _castAtCentre() {
45 _raycaster.setFromNdc(_camera, 0.0, 0.0, aspect: 16 / 9);
46 lastHit = _raycaster.intersectScene(_scene);
47 }
50 void update(DemoContext context, double dt) => _castAtCentre();
53 void verify(Scene scene, FrameResult frame) {
54 if (frame.drawCalls < 1) {
55 throw StateError('the ball was not drawn');
56 }
57 // The ball sits on the camera's own axis, so a ray straight down the
58 // middle of the screen has to find it.
59 final HitResult? hit = lastHit;
60 if (hit == null || hit.node?.name != 'ball') {
61 throw StateError(
62 'a ray through the centre of the screen missed the ball',
63 );
64 }
65 }
66}