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,
) {
  final mvp = bindings.mat4('FrameInfo', 'mvp');
  final model = bindings.mat4('FrameInfo', 'model');
  final normalMatrix = bindings.mat4('FrameInfo', 'normal_matrix');

  // Morphed first and in the mesh's own space, which is the order the GLSL
  // uses and the order glTF specifies.
  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 world = model * local;
  out[kVWorld] = world.x;
  out[kVWorld + 1] = world.y;
  out[kVWorld + 2] = world.z;

  // `mat3(normal_matrix) * normal`: the upper-left three by three, so the
  // translation does not move a direction.
  final Vector3 n =
      normalMatrix.getRotation() *
      (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;
  }

  // The tangent transforms with the model matrix, not the normal matrix: it
  // lies *in* the surface, so it stretches with the geometry rather than
  // staying perpendicular to it.
  final Vector3 t =
      model.getRotation() *
      (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;
  // The bitangent sign turned round under a mirroring model, as the GLSL
  // does: cross(n, t) of two transformed vectors carries the determinant.
  out[kVTangent + 3] = model.getRotation().determinant() < 0.0
      ? -a[kTangent + 3]
      : a[kTangent + 3];

  return mvp * local;
}