A pose with no scene: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: pose (line 32), clip (line 55), sample (line 108).
1/// A two-joint pendulum sampled straight off a clip through `Pose`, with no
2/// `AnimationPlayer` and no scene node anywhere near the arithmetic. The
3/// clip turns the root; the tip only moves because it hangs off it. Markers
4/// are placed afterwards, only to give this page something to draw.
5///
6/// Quoted by `pose_sampling.md` and shown whole in the Source tab.
7library;
9import 'dart:typed_data';
11import 'package:flutter3d/flutter3d.dart';
12import 'package:flutter3d_showcase/src/demo/demo.dart';
13import 'package:vector_math/vector_math.dart';
15final class PoseSamplingDemo extends ShowcaseDemo {
16 double _time = 0.3;
18 late final Pose _pose;
19 late final AnimationClip _swing;
20 late final MeshNode _rootMarker;
21 late final MeshNode _tipMarker;
24 void configureView(DemoContext context) {
25 context.orbit
26 ..distance = 3.5
27 ..pitch = 0.15;
28 }
31 Scene build(DemoContext context) {
32 _pose = Pose(
33 parents: <int>[-1, 0],
34 restTranslations: Float32List.fromList(<double>[
35 0.0,
36 0.0,
37 0.0,
38 0.0,
39 -1.2,
40 0.0,
41 ]),
42 restRotations: Float32List.fromList(<double>[
43 0.0,
44 0.0,
45 0.0,
46 1.0,
47 0.0,
48 0.0,
49 0.0,
50 1.0,
51 ]),
52 restScales: Float32List.fromList(<double>[1.0, 1.0, 1.0, 1.0, 1.0, 1.0]),
53 );
55 final Float32List times = Float32List.fromList(<double>[0.0, 0.75, 1.5]);
56 final List<double> angles = <double>[-0.6, 0.6, -0.6];
57 final Float32List values = Float32List(angles.length * 4);
58 for (var i = 0; i < angles.length; i++) {
59 final Quaternion q = Quaternion.axisAngle(
60 Vector3(1.0, 0.0, 0.0),
61 angles[i],
62 );
63 values[i * 4] = q.x;
64 values[i * 4 + 1] = q.y;
65 values[i * 4 + 2] = q.z;
66 values[i * 4 + 3] = q.w;
67 }
68 _swing = AnimationClip(
69 name: 'swing',
70 tracks: <AnimationTrack>[
71 AnimationTrack(
72 nodeIndex: 0,
73 path: AnimationPath.rotation,
74 interpolation: AnimationInterpolation.linear,
75 times: times,
76 values: values,
77 componentCount: 4,
78 ),
79 ],
80 );
82 final DeviceMesh bead = DeviceMesh.upload(
83 context.device,
84 SphereShape(radius: 0.15, segments: 16, rings: 8).build(),
85 );
86 _rootMarker = MeshNode(
87 bead,
88 Material(name: 'root', baseColor: Vector4(0.6, 0.6, 0.65, 1.0)),
89 name: 'root marker',
90 );
91 _tipMarker = MeshNode(
92 bead,
93 Material(name: 'tip', baseColor: Vector4(0.9, 0.5, 0.2, 1.0)),
94 name: 'tip marker',
95 );
97 return Scene()
98 ..add(_rootMarker)
99 ..add(_tipMarker)
100 ..add(
101 LightNode(name: 'sun', intensity: 3.0)
102 ..setLocalForward(Vector3(-0.3, -0.7, -0.5)),
103 );
104 }
107 void update(DemoContext context, double dt) {
108 _time = (_time + dt) % _swing.duration;
109 _pose.sampleClip(_swing, _time);
110 final List<Matrix4> world = _pose.worldMatrices();
111 _rootMarker.setPositionFrom(world[0].getTranslation());
112 _tipMarker.setPositionFrom(world[1].getTranslation());
113 }
116 void verify(Scene scene, FrameResult frame) {
117 final Matrix4 restTip = _pose.restOf(1);
118 final Vector3 sampledTip = _pose.worldMatrices()[1].getTranslation();
119 final double moved = (sampledTip - restTip.getTranslation()).length;
120 if (moved < 1e-3) {
121 throw StateError(
122 'sampling the clip did not move the tip off its rest pose',
123 );
124 }
125 }
126}