runAt method

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

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 CpuVertexShaderByIndex engine = skinned
      ? _skinned
      : instanced
      ? _instanced
      : lightmapped
      ? _lightmapped
      : _plain;
  final clip = engine.runAt(vertexIndex, instanceIndex, a, bindings, out);
  final model = bindings.mat4('FrameInfo', 'model');
  final mvp = bindings.mat4('FrameInfo', 'mvp');
  final world = Vector3(
    out[CpuMeshLayout.varyingWorld],
    out[CpuMeshLayout.varyingWorld + 1],
    out[CpuMeshLayout.varyingWorld + 2],
  );
  final normal = Vector3(
    out[CpuMeshLayout.varyingNormal],
    out[CpuMeshLayout.varyingNormal + 1],
    out[CpuMeshLayout.varyingNormal + 2],
  );
  final toMesh = Matrix4.copy(model)..invert();
  final position = toMesh.transformed3(world.clone());
  final normalRotation = bindings
      .mat4('FrameInfo', 'normal_matrix')
      .getRotation();
  final objectNormal = (normalRotation..invert()).transformed(normal.clone());
  final origin = model.getTranslation();

  final uniforms = <String, List<double>>{
    for (final parameter in program.parameters)
      if (parameter.uniform)
        parameter.name: ?bindings.read(parameter.block!, parameter.name),
  };
  final written = evaluateMaterialVertex(
    program,
    MaterialSurfaceValues(
      inputs: <String, List<double>>{
        'position': <double>[position.x, position.y, position.z],
        'objectNormal': <double>[
          objectNormal.x,
          objectNormal.y,
          objectNormal.z,
        ],
        'world': <double>[world.x, world.y, world.z],
        'normal': <double>[normal.x, normal.y, normal.z],
        'uv': <double>[
          a[CpuMeshLayout.texcoord],
          a[CpuMeshLayout.texcoord + 1],
        ],
        'color': lightmapped
            ? const <double>[1.0, 1.0, 1.0, 1.0]
            : <double>[
                a[CpuMeshLayout.color],
                a[CpuMeshLayout.color + 1],
                a[CpuMeshLayout.color + 2],
                a[CpuMeshLayout.color + 3],
              ],
        'instance': instanced
            ? <double>[
                for (var i = 0; i < 4; i++)
                  out[CpuMeshLayout.varyingInstance + i],
              ]
            : const <double>[0.0, 0.0, 0.0, 0.0],
        'origin': <double>[origin.x, origin.y, origin.z],
      },
      uniforms: uniforms,
      sample: (_, _, _) =>
          throw StateError('a vertex block samples no texture'),
    ),
  );

  if (written['normal'] case final n?) {
    out
      ..[CpuMeshLayout.varyingNormal] = n[0]
      ..[CpuMeshLayout.varyingNormal + 1] = n[1]
      ..[CpuMeshLayout.varyingNormal + 2] = n[2];
  }
  if (written['position'] case final p?) {
    final local = Vector4(p[0], p[1], p[2], 1.0);
    final Vector4 moved = model * local;
    out
      ..[CpuMeshLayout.varyingWorld] = moved.x
      ..[CpuMeshLayout.varyingWorld + 1] = moved.y
      ..[CpuMeshLayout.varyingWorld + 2] = moved.z;
    return mvp * local;
  }
  if (written['world'] case final w?) {
    out
      ..[CpuMeshLayout.varyingWorld] = w[0]
      ..[CpuMeshLayout.varyingWorld + 1] = w[1]
      ..[CpuMeshLayout.varyingWorld + 2] = w[2];
    final viewProjection = bindings.mat4(
      'MaterialVertexInfo',
      'view_projection',
    );
    return viewProjection * Vector4(w[0], w[1], w[2], 1.0);
  }
  return clip;
}