flutter3d
Showcase Changelog 38 packages API reference

Morph targets

since 0.5.2 Animation

A morph target is a set of deltas: how far each vertex moves, added to its base position by some amount from nought to one. This page builds a flat sheet and one target that lifts its middle into a bump.

Step 1: Deltas for one target #

The target is the same shape as the mesh, but its numbers are offsets rather than positions. Here only Y moves, most in the middle and less toward the edges, which is what a gaussian falloff gives for free.

const int segments = 16;
final MeshData flat = const PlaneShape(
  width: 2.0,
  depth: 2.0,
  widthSegments: segments,
  depthSegments: segments,
).build();
final Float32List bump = Float32List(flat.vertexCount * 3);
for (var v = 0; v < flat.vertexCount; v++) {
  final Vector3 p = flat.positionAt(v);
  final double falloff = _gaussian(p.x, p.z);
  bump[v * 3 + 1] = falloff * 0.9;
}
final MeshData withBump = flat.withMorphTargets(<MorphTarget>[
  MorphTarget(vertexCount: flat.vertexCount, positions: bump, name: 'bump'),
]);

Step 2: Pack the deltas into a texture #

The deltas do not travel as a second set of vertex attributes: MorphTexture packs them into a float texture instead, one column a vertex, so a lighting model does not need a second vertex shader to read them.

final MorphTexture packed = MorphTexture.pack(withBump)!;
final TextureHandle? deltaTexture = context.device.createTextureFromPixels(
  width: packed.width,
  height: packed.height,
  format: TextureFormat.r32g32b32a32Float,
  pixels: packed.bytes,
);

Step 3: Weigh it #

MorphState holds how much of each target is showing, from nought to one, and it lives on the node rather than on the mesh: two copies of one sheet can wear different bumps from the same uploaded deltas.

sheet.morph = MorphState(
  texture: deltaTexture,
  targetCount: packed.targetCount,
  reaches: packed.reaches,
)..setWeights(<double>[weight]);

Step 4: Change the weight while it runs #

_sheet?.morph?.setWeights(<double>[weight]);

Drag Weight from nought to one and watch the bump rise out of a flat sheet. At nought the sheet is exactly the base mesh; the deltas are being added, not substituted, so a weight of nought costs nothing and changes nothing.