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');

  // The rows as the buffer holds them; `Matrix4` wants columns, so each
  // argument below is one row's worth of a column.
  final instance = Matrix4(
    a[_row0],
    a[_row1],
    a[_row2],
    0.0, // column 0
    a[_row0 + 1],
    a[_row1 + 1],
    a[_row2 + 1],
    0.0, // column 1
    a[_row0 + 2],
    a[_row1 + 2],
    a[_row2 + 2],
    0.0, // column 2
    a[_row0 + 3],
    a[_row1 + 3],
    a[_row2 + 3],
    1.0, // column 3
  );
  // Morphed in the mesh's own space, before the instance transform: every
  // instance of a batch shares the mesh, which is what the GLSL says at the
  // same point. What they need not share is the shape — the instance index
  // goes in, and a batch that gives each copy its own weights is read a row
  // at a time out of a second texture.
  final morphed =
      vertexIndex >= 0 &&
      _morphScratch.load(
        vertexIndex,
        a,
        bindings,
        positionAt: kPosition,
        normalAt: kNormal,
        tangentAt: kTangent,
        instanceIndex: instanceIndex,
      );
  final Vector4 local =
      instance *
      (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;

  final rotation = instance.getRotation();
  final rotated = rotation.transformed(
    morphed
        ? _morphScratch.normal
        : Vector3(a[kNormal], a[kNormal + 1], a[kNormal + 2]),
  )..normalize();
  final n = normalMatrix.getRotation().transformed(rotated);
  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] * a[_color + i];
  }
  out[kVLightmap] = 0.0;
  out[kVLightmap + 1] = 0.0;
  for (var i = 0; i < 4; i++) {
    out[kVInstance + i] = a[_data + i];
  }

  final Vector3 t =
      model.getRotation() *
      (rotation *
          (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;
  // Mirrored by the node or by the instance, and by both is not mirrored.
  final mirrored =
      (model.getRotation().determinant() < 0.0) !=
      (rotation.determinant() < 0.0);
  out[kVTangent + 3] = mirrored ? -a[kTangent + 3] : a[kTangent + 3];

  return mvp * local;
}