The skeleton drawn: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: rig (line 34), settings (line 94).
1/// The same bending banner `skinning` builds, with its skeleton drawn over
2/// it: `DebugDrawOptions.skeletons` puts one octahedron a bone and one cross
3/// a leaf joint on every skinned mesh in the scene.
4///
5/// Quoted by `skeleton_debug.md` and shown whole in the Source tab.
6library;
8import 'dart:typed_data';
10import 'package:flutter3d/flutter3d.dart';
11import 'package:flutter3d_showcase/src/demo/demo.dart';
12import 'package:vector_math/vector_math.dart';
14final class SkeletonDebugDemo extends ShowcaseDemo {
15 bool showSkeleton = true;
16 double bend = 0.6;
18 static const double _depth = 2.4;
20 late final SceneNode _base;
21 late final SceneNode _tip;
22 late final Skeleton _skeleton;
25 void configureView(DemoContext context) {
26 context.orbit
27 ..distance = 4.0
28 ..pitch = 0.15
29 ..yaw = 0.5;
30 }
33 Scene build(DemoContext context) {
34 _base = SceneNode(name: 'base')..setPosition(0.0, 0.0, -_depth / 2);
35 _tip = SceneNode(name: 'tip')..setPosition(0.0, 0.0, _depth);
36 _base.add(_tip);
38 final Matrix4 inverseBindBase = Matrix4.copy(_base.worldMatrix)..invert();
39 final Matrix4 inverseBindTip = Matrix4.copy(_tip.worldMatrix)..invert();
40 _skeleton = Skeleton(
41 joints: <SceneNode>[_base, _tip],
42 inverseBindMatrices: <Matrix4>[inverseBindBase, inverseBindTip],
43 name: 'arm',
44 );
46 final MeshData mesh = const PlaneShape(
47 width: 0.7,
48 depth: _depth,
49 depthSegments: 8,
50 ).build().convertedTo(VertexLayout.skinned);
51 _paintWeights(mesh);
53 final MeshNode banner = MeshNode(
54 DeviceMesh.upload(context.device, mesh),
55 Material(
56 name: 'banner',
57 baseColor: Vector4(0.5, 0.55, 0.6, 1.0),
58 doubleSided: true,
59 ),
60 name: 'banner',
61 )..skeleton = _skeleton;
63 return Scene()
64 ..add(banner)
65 ..add(
66 LightNode(name: 'sun', intensity: 3.0)
67 ..setLocalForward(Vector3(-0.3, -0.6, -0.7)),
68 );
69 }
71 void _paintWeights(MeshData mesh) {
72 final VertexLayout layout = mesh.layout;
73 final int stride = layout.floatsPerVertex;
74 final int positionAt = layout.floatOffsetOf(VertexLayout.position.name);
75 final int jointsAt = layout.floatOffsetOf(VertexLayout.joints.name);
76 final int weightsAt = layout.floatOffsetOf(VertexLayout.weights.name);
77 final Float32List vertices = mesh.vertices;
78 for (var v = 0; v < mesh.vertexCount; v++) {
79 final int at = v * stride;
80 final double z = vertices[at + positionAt + 2];
81 final double t = ((z + _depth / 2) / _depth).clamp(0.0, 1.0);
82 vertices[at + jointsAt] = 0.0;
83 vertices[at + jointsAt + 1] = 1.0;
84 vertices[at + weightsAt] = 1.0 - t;
85 vertices[at + weightsAt + 1] = t;
86 }
87 }
90 void update(DemoContext context, double dt) {
91 _tip.setRotation(Quaternion.axisAngle(Vector3(1.0, 0.0, 0.0), bend * 1.2));
92 }
95 RenderSettings settings(DemoContext context) =>
96 RenderSettings(debug: DebugDrawOptions(skeletons: showSkeleton));
99 List<DemoControl> controls(DemoContext context) => <DemoControl>[
100 ToggleControl(
101 'Show skeleton',
102 value: () => showSkeleton,
103 onChanged: (bool v) => showSkeleton = v,
104 ),
105 SliderControl(
106 'Bend',
107 min: 0,
108 max: 1,
109 value: () => bend,
110 onChanged: (double v) => bend = v,
111 ),
112 ];
115 void verify(Scene scene, FrameResult frame) {
116 if (frame.debugLines < 1) {
117 throw StateError('the skeleton overlay drew no lines');
118 }
119 }
120}