addMaterial method
Compiles source for this engine's backend and adds it to the
renderer's materials once loaded. from names it in every message.
The registration takes it out of the renderer again.
In the plugin's namespace. Through the view of a plugin from
outside the engine, the bare name the source writes (material Ink)
is compiled as <namespace>__Ink (materialStageIn), the source
renamed to match, so its lighting model is <namespace>.Ink whatever
the file wrote; a name in another plugin's namespace is a
PluginException.
Implementation
Registration addMaterial(String source, {required String from}) {
final String name;
// [source], in the plugin's namespace.
final String named;
final ShaderBundle bundle;
try {
final parsed = parseMaterial(source);
final manifest = _scope?.manifest;
final stage = manifest == null || manifest.isEngine
? parsed.name
: materialStageIn(manifest.namespace, parsed.name);
if (stage == null) {
throw PluginException(
'${_owner()} brought the material "${parsed.name}", which is named '
'in another plugin\'s namespace: a plugin writes the bare name, '
'which it compiles as "${manifest!.namespace}__<Name>"',
);
}
named = stage == parsed.name
? source
: renameMaterialSource(source, stage);
final program = stage == parsed.name ? parsed : parseMaterial(named);
name = program.name;
if (program.kind != MaterialStageKind.surface) {
throw PluginException(
'${_owner()} brought "$name", a ${program.kind} stage, as a '
'material: a full-screen stage is a render step\'s, under its '
'"shaders" as "material"',
);
}
} on MaterialSyntaxException catch (error, stack) {
// A source that does not parse is the author's mistake on every
// backend, so it fails the install rather than switching anything off.
Error.throwWithStackTrace(
PluginException(
'${_owner()} brought a material that does not parse: '
'$from:${error.line}:${error.column}: ${error.message}',
),
stack,
);
}
if (_store.backend == RuntimeBackends.impeller) {
return _prebuiltMaterial(named, name);
}
try {
bundle = runtimeMaterialBundle(
named,
backend: _store.backend,
from: from,
engineSources: _store.engineSources,
webGpuHost: _store.webGpuHost,
webGpuFullscreenUvLocation: _store.webGpuFullscreenUvLocation,
compilers: _store.compilers,
);
} on RuntimeShadersException catch (refused) {
_store.statuses[name] = refused.message;
return _tracked(Registration(() => _store.statuses.remove(name)));
}
_store.statuses[name] = null;
Registration? added;
var cancelled = false;
_store.load(bundle, name, (library) {
if (cancelled) return;
added = _store.addMaterials(library);
_store.lighting[name] = describeMaterial(
parseMaterial(named),
).lightingModel(label: name, shaderName: name);
});
return _tracked(
Registration(() {
cancelled = true;
added?.cancel();
_store.lighting.remove(name);
_store.statuses.remove(name);
}),
);
}