UNPKG

@luma.gl/engine

Version:

3D Engine Components for luma.gl

111 lines 4.31 kB
// luma.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors import { ShaderInputs } from "../shader-inputs.js"; import { Material as MaterialClass } from "./material.js"; /** Logical bind-group slot reserved for material-owned bindings. */ export const MATERIAL_BIND_GROUP = 3; /** * Creates typed {@link Material} instances for a stable material binding schema. * * @example * ```ts * const pbrFactory = new MaterialFactory< * {pbrMaterial: PBRMaterialUniforms}, * PBRMaterialBindings * >(device, {modules: [pbrMaterial]}); * const pbr = pbrFactory.createMaterial(); * pbr.setProps({pbrMaterial: {baseColorFactor: [1, 0, 0, 1]}}); * const pbrVariant = pbr.clone({bindings: {pbr_baseColorSampler: texture}}); * ``` * * @example * ```ts * const phongFactory = new MaterialFactory< * {phongMaterial: PhongMaterialProps}, * {} * >(device, {modules: [phongMaterial]}); * const phong = phongFactory.createMaterial(); * phong.setProps({phongMaterial: {ambient: 0.4, diffuse: 0.7}}); * const phongVariant = phong.clone({moduleProps: {phongMaterial: {shininess: 64}}}); * ``` * * @example * ```ts * const gouraudFactory = new MaterialFactory< * {gouraudMaterial: GouraudMaterialProps}, * {} * >(device, {modules: [gouraudMaterial]}); * const gouraud = gouraudFactory.createMaterial(); * gouraud.setProps({gouraudMaterial: {ambient: 0.25}}); * const gouraudVariant = gouraud.clone({moduleProps: {gouraudMaterial: {diffuse: 0.8}}}); * ``` */ export class MaterialFactory { /** Device that creates materials for this schema. */ device; /** Shader modules that define the material schema. */ modules; _materialBindingNames; _materialModuleNames; constructor(device, props = {}) { this.device = device; this.modules = props.modules || []; const shaderInputs = new ShaderInputs(Object.fromEntries(this.modules.map(module => [module.name, module]))); this._materialBindingNames = getMaterialBindingNames(shaderInputs); this._materialModuleNames = getMaterialModuleNames(shaderInputs); } /** Creates one typed material instance for this factory's schema. */ createMaterial(props = {}) { return new MaterialClass(this.device, { ...props, factory: this }); } /** Returns the logical material-owned resource binding names. */ getBindingNames() { return Array.from(this._materialBindingNames); } /** Returns `true` when the supplied binding belongs to this material schema. */ ownsBinding(bindingName) { if (this._materialBindingNames.has(bindingName)) { return true; } const aliasedModuleName = getModuleNameFromUniformBinding(bindingName); return aliasedModuleName ? this._materialModuleNames.has(aliasedModuleName) : false; } /** Returns `true` when the supplied shader module is owned by this material schema. */ ownsModule(moduleName) { return this._materialModuleNames.has(moduleName); } /** Packages resolved material bindings into bind group `3`. */ getBindingsByGroup(bindings) { return Object.keys(bindings).length > 0 ? { [MATERIAL_BIND_GROUP]: bindings } : {}; } } /** Returns the module name corresponding to an auto-generated `*Uniforms` binding. */ export function getModuleNameFromUniformBinding(bindingName) { return bindingName.endsWith('Uniforms') ? bindingName.slice(0, -'Uniforms'.length) : null; } function getMaterialBindingNames(shaderInputs) { const bindingNames = new Set(); for (const module of Object.values(shaderInputs.modules)) { for (const binding of module.bindingLayout || []) { if (binding.group === MATERIAL_BIND_GROUP) { bindingNames.add(binding.name); } } } return bindingNames; } function getMaterialModuleNames(shaderInputs) { const moduleNames = new Set(); for (const module of Object.values(shaderInputs.modules)) { if (module.name && module.bindingLayout?.some(binding => binding.group === MATERIAL_BIND_GROUP && binding.name === module.name)) { moduleNames.add(module.name); } } return moduleNames; } //# sourceMappingURL=material-factory.js.map