optimizeVertexCache function

MeshData optimizeVertexCache(
  1. MeshData mesh, {
  2. int cacheSize = kDefaultVertexCacheSize,
})

mesh with its triangles and vertices reordered for GPU cache reuse: optimizeTriangleOrder on the index buffer, then optimizeVertexFetch to renumber vertices by first use in that new order — the same two-pass scheme real engines and meshoptimizer split into, because the two caches they target (post- and pre-transform) are optimized by different orders.

The same triangles, drawn in the same orientation, described by the same vertex data — nothing here changes what the mesh looks like, only which byte offset a vertex or an index lands at. MeshData.morphTargets are carried through the same vertex permutation, so a blend shape still targets the vertex it always did.

A mesh with no triangles is returned unchanged, since there is nothing to reorder and Forsyth's scoring divides by a vertex's remaining triangle count.

Implementation

MeshData optimizeVertexCache(
  MeshData mesh, {
  int cacheSize = kDefaultVertexCacheSize,
}) {
  if (mesh.triangleCount == 0) return mesh;

  final cacheOrdered = optimizeTriangleOrder(
    mesh.indices,
    mesh.vertexCount,
    cacheSize: cacheSize,
  );
  final fetch = optimizeVertexFetch(cacheOrdered, mesh.vertexCount);
  final oldToNew = fetch.oldToNew;

  final stride = mesh.layout.floatsPerVertex;
  final vertices = Float32List(mesh.vertices.length);
  for (var oldV = 0; oldV < mesh.vertexCount; oldV++) {
    final newV = oldToNew[oldV];
    final from = oldV * stride;
    final to = newV * stride;
    for (var c = 0; c < stride; c++) {
      vertices[to + c] = mesh.vertices[from + c];
    }
  }

  return MeshData(
    layout: mesh.layout,
    vertices: vertices,
    indices: fetch.indices,
    morphTargets: [
      for (final target in mesh.morphTargets)
        _remapMorphTarget(target, oldToNew),
    ],
  );
}