runAt method

  1. @override
Vector4 runAt(
  1. int vertexIndex,
  2. int instanceIndex,
  3. Float32List a,
  4. ShaderBindings bindings,
  5. Float32List out,
)
override

The same as CpuVertexShader.run, plus the vertex's and the instance's own indices.

run must still work — a caller that has no indices, or a rasteriser that has not been taught to pass them, falls back to it — so an implementation answers both and shares whatever it can.

Implementation

@override
Vector4 runAt(
  int vertexIndex,
  int instanceIndex,
  Float32List a,
  ShaderBindings bindings,
  Float32List out,
) {
  // The weights are renormalised rather than trusted: an exporter that
  // writes three of four and leaves the fourth at zero is common, and a
  // total below one shrinks the vertex towards the origin.
  final total =
      a[_weights] + a[_weights + 1] + a[_weights + 2] + a[_weights + 3];
  final w = total > 1e-5
      ? <double>[
          a[_weights] / total,
          a[_weights + 1] / total,
          a[_weights + 2] / total,
          a[_weights + 3] / total,
        ]
      : <double>[1.0, 0.0, 0.0, 0.0];

  // Every slot is read, a zero weight included, and the index is clamped
  // into the palette: `JointIndex` in the shader, line for line.
  final skin = Matrix4.zero();
  for (var i = 0; i < 4; i++) {
    final joint = bindings.mat4(
      'SkinInfo',
      'joint_matrices',
      at: jointIndex(a[_joints + i]),
    );
    for (var e = 0; e < 16; e++) {
      skin[e] = skin[e] + joint[e] * w[i];
    }
  }

  final model = bindings.mat4('FrameInfo', 'model');
  final mvp = bindings.mat4('FrameInfo', 'mvp');
  final normalMatrix = bindings.mat4('FrameInfo', 'normal_matrix');

  // Morphed in the rest pose and skinned afterwards, which is the order glTF
  // specifies and the only one that composes: a face morphs where it was
  // modelled and the skeleton then carries it.
  final morphed =
      vertexIndex >= 0 &&
      _morphScratch.load(
        vertexIndex,
        a,
        bindings,
        positionAt: kPosition,
        normalAt: kNormal,
        tangentAt: kTangent,
      );
  final local = morphed
      ? Vector4(
          _morphScratch.position.x,
          _morphScratch.position.y,
          _morphScratch.position.z,
          1.0,
        )
      : Vector4(a[kPosition], a[kPosition + 1], a[kPosition + 2], 1.0);
  final Vector4 skinned = skin * local;
  final Vector4 world = model * skinned;
  out[kVWorld] = world.x;
  out[kVWorld + 1] = world.y;
  out[kVWorld + 2] = world.z;

  final skinRotation = skin.getRotation();
  final Vector3 n =
      normalMatrix.getRotation() *
      (skinRotation *
          (morphed
              ? _morphScratch.normal
              : Vector3(a[kNormal], a[kNormal + 1], a[kNormal + 2])));
  out[kVNormal] = n.x;
  out[kVNormal + 1] = n.y;
  out[kVNormal + 2] = n.z;

  out[kVUv] = a[kTexcoord];
  out[kVUv + 1] = a[kTexcoord + 1];
  for (var i = 0; i < 4; i++) {
    out[kVColour + i] = a[kColour + i];
  }
  out[kVLightmap] = 0.0;
  out[kVLightmap + 1] = 0.0;
  for (var i = 0; i < 4; i++) {
    out[kVInstance + i] = 0.0;
  }

  final Vector3 t =
      model.getRotation() *
      (skinRotation *
          (morphed
              ? Vector3(
                  _morphScratch.tangent.x,
                  _morphScratch.tangent.y,
                  _morphScratch.tangent.z,
                )
              : Vector3(a[kTangent], a[kTangent + 1], a[kTangent + 2])));
  out[kVTangent] = t.x;
  out[kVTangent + 1] = t.y;
  out[kVTangent + 2] = t.z;
  final mirrored =
      (model.getRotation().determinant() < 0.0) !=
      (skinRotation.determinant() < 0.0);
  out[kVTangent + 3] = mirrored ? -a[kTangent + 3] : a[kTangent + 3];

  return mvp * skinned;
}