MeshGeometry.fromArrays constructor

MeshGeometry.fromArrays({
  1. required Float32List positions,
  2. Float32List? normals,
  3. Float32List? texCoords,
  4. Float32List? texCoords1,
  5. Float32List? colors,
  6. Float32List? tangents,
  7. List<int>? indices,
  8. PrimitiveType primitiveType = gpu.PrimitiveType.triangle,
  9. Aabb3? bounds,
  10. GeometryStorage storage = GeometryStorage.fixed,
  11. GeometryBufferArena? bufferArena,
  12. bool retainCpuData = true,
})

Builds a mesh from structure-of-arrays vertex attributes.

positions is required and holds three floats per vertex. The optional attributes hold, per vertex, three floats for normals, two for texCoords and texCoords1, and four for colors and tangents; each must match the vertex count implied by positions when supplied. Absent attributes fall back to defaults: texture coordinate (0, 0) and color opaque white.

When normals is omitted and primitiveType is a triangle list, area-weighted vertex normals are generated from the faces; for line and point primitives, absent normals keep their default. indices, when supplied, is an index list; when omitted a triangle mesh must have a vertex count that is a multiple of three.

primitiveType selects how the vertex/index data is assembled into primitives when drawn, and defaults to a triangle list.

storage selects whether the mesh can later be updated in place. Pass bufferArena to share immutable GPU buffer blocks with other fixed meshes. Set retainCpuData to false when the mesh does not need raycasting or extractMeshData, avoiding a retained CPU copy after the upload. Updatable geometry requires retained CPU data and cannot use an arena. An updatable mesh fixes its indexed-or-not state at construction: supplying indices makes rebuild require indices thereafter, and omitting them makes it reject them. To start empty, pass a zero-length positions array with GeometryStorage.updatable.

bounds, when supplied, becomes the local-space culling AABB and skips the construction-time position scan; a caller that assembled the vertices off the UI thread (a worker isolate) can compute it there. It must cover every position or the mesh over-culls.

Implementation

MeshGeometry.fromArrays({
  required Float32List positions,
  Float32List? normals,
  Float32List? texCoords,
  Float32List? texCoords1,
  Float32List? colors,
  Float32List? tangents,
  List<int>? indices,
  gpu.PrimitiveType primitiveType = gpu.PrimitiveType.triangle,
  Aabb3? bounds,
  this.storage = GeometryStorage.fixed,
  GeometryBufferArena? bufferArena,
  this.retainCpuData = true,
}) {
  if (storage == GeometryStorage.updatable && bufferArena != null) {
    throw ArgumentError('Updatable geometry cannot use a buffer arena');
  }
  if (storage == GeometryStorage.updatable && !retainCpuData) {
    throw ArgumentError('Updatable geometry must retain CPU data');
  }
  if (positions.length % 3 != 0) {
    throw ArgumentError(
      'positions has ${positions.length} floats; expected a multiple of '
      'three (one vec3 per vertex)',
    );
  }
  final vertexCount = positions.length ~/ 3;
  this.primitiveType = primitiveType;

  if (bounds != null) {
    final center = (bounds.min + bounds.max) * 0.5;
    setLocalBounds(
      bounds,
      Sphere.centerRadius(center, (bounds.max - center).length),
    );
  }

  // Normals are generated from triangle faces; line and point
  // geometry has none, so absent normals are left at their default.
  final resolvedNormals =
      normals ??
      (vertexCount > 0 && primitiveType == gpu.PrimitiveType.triangle
          ? InterleavedLayoutAdapter.generateNormals(
              positions: positions,
              vertexCount: vertexCount,
              indices: indices,
            )
          : null);

  if (storage == GeometryStorage.fixed) {
    _uploadFixed(
      positions,
      vertexCount,
      resolvedNormals,
      texCoords,
      texCoords1,
      colors,
      tangents,
      indices,
      bufferArena,
    );
  } else {
    _indexed = indices != null;
    _setCpuStreams(
      positions,
      vertexCount,
      resolvedNormals,
      texCoords,
      texCoords1,
      colors,
      tangents,
    );
    _positionRing = _RingBufferStream(
      InterleavedLayoutAdapter.positionStreamBytes,
    );
    _normalRing = _RingBufferStream(
      InterleavedLayoutAdapter.normalStreamBytes,
    );
    _texCoordRing = _RingBufferStream(
      InterleavedLayoutAdapter.texCoordStreamBytes,
    );
    _texCoord1Ring = _RingBufferStream(
      InterleavedLayoutAdapter.texCoord1StreamBytes,
    );
    _colorRing = _RingBufferStream(InterleavedLayoutAdapter.colorStreamBytes);
    _tangentRing = _RingBufferStream(
      InterleavedLayoutAdapter.tangentStreamBytes,
    );
    _liveVertexCount = vertexCount;
    // Indices first, so the attribute upload can retain them for raycasting.
    if (_indexed) _uploadIndices(indices!);
    _uploadAllStreams();
    _recomputeBounds();
  }
}