preload method

Future<void> preload({
  1. bool includeEnvironments = true,
})

Resolves the resources that want asynchronous work (every texture, and fmat materials), caching them so the synchronous realize path finds them ready.

External image assets and encoded payloads have no synchronous path at all; an embedded rgba8 payload does, but preloading it builds its mip chain on a background isolate instead of the calling thread.

Await this before realizing a document that may reference such resources (the async loaders do). A resource that fails to load degrades to a placeholder (textures) or an unlit material (fmat) with a warning, rather than failing the whole scene.

Set includeEnvironments false to skip realizing EnvironmentResources (which build GPU prefilter cubes, the expensive part). The editor uses this to re-realize just a changed material without re-baking environments.

Implementation

Future<void> preload({bool includeEnvironments = true}) async {
  // Textures first: an fmat material's parameter overrides may reference a
  // texture resource, which must be decoded before the override resolves it.
  final textures = <Future<void>>[];
  for (final resource in document.resources.values) {
    // Every texture preloads, not only the ones the sync path cannot do at
    // all: an rgba8 payload realizes synchronously but builds its mip chain
    // on the calling thread, and preloading moves that off it. Entries
    // carried over by [adoptUnchanged] are already live.
    if (resource is TextureResource && !_textures.containsKey(resource.id)) {
      textures.add(_preloadTexture(resource));
    }
  }
  await Future.wait(textures);

  final materials = <Future<void>>[];
  for (final resource in document.resources.values) {
    if (resource is MaterialResource &&
        !_materials.containsKey(resource.id)) {
      if (resource.type == 'fmat') {
        materials.add(_preloadFmat(resource));
      } else if (resource.type == 'physical') {
        materials.add(_preloadPhysical(resource));
      }
    }
  }
  await Future.wait(materials);

  // Environments are GPU-bound and async (they build prefilter cubes and may
  // load image assets), so realize them here and cache the result for the
  // synchronous component realize path.
  if (!includeEnvironments) return;
  for (final resource in document.resources.values) {
    if (resource is EnvironmentResource &&
        !_environments.containsKey(resource.id)) {
      // Origin-tagged below so a component holding the realized settings
      // can recover the source resource at serialize time.
      _environments[resource.id] = tagResourceOrigin(
        await realizeEnvironmentSettings(
          environment: resource.environment,
          environmentIntensity: resource.environmentIntensity,
          exposure: resource.exposure,
          toneMapping: resource.toneMapping,
          agxWhite: resource.agxWhite,
          agxContrast: resource.agxContrast,
          environmentRotationY: resource.environmentRotationY,
          radianceCubeSize: resource.radianceCubeSize,
          skybox: resource.skybox,
          skyEnvironment: resource.skyEnvironment,
          effects: resource.effects,
          bundle: bundle,
          environmentLoader: environmentLoader,
          payloadLookup: document.payload,
        ),
        document,
        resource.id,
      );
    }
  }
}