123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- import { Material } from "./material.js";
- import { MaterialPluginEvent } from "./materialPluginEvent.js";
- import { EngineStore } from "../Engines/engineStore.js";
- import { ShaderProcessor } from "../Engines/Processors/shaderProcessor.js";
- import { ShaderLanguage } from "./shaderLanguage.js";
- import { ShaderStore } from "../Engines/shaderStore.js";
- const rxOption = new RegExp("^([gimus]+)!");
- /**
- * Class that manages the plugins of a material
- * @since 5.0
- */
- export class MaterialPluginManager {
- /**
- * Creates a new instance of the plugin manager
- * @param material material that this manager will manage the plugins for
- */
- constructor(material) {
- /** @internal */
- this._plugins = [];
- this._activePlugins = [];
- this._activePluginsForExtraEvents = [];
- this._material = material;
- this._scene = material.getScene();
- this._engine = this._scene.getEngine();
- }
- /**
- * @internal
- */
- _addPlugin(plugin) {
- for (let i = 0; i < this._plugins.length; ++i) {
- if (this._plugins[i].name === plugin.name) {
- return false;
- }
- }
- if (this._material._uniformBufferLayoutBuilt) {
- // eslint-disable-next-line no-throw-literal
- throw `The plugin "${plugin.name}" can't be added to the material "${this._material.name}" because this material has already been used for rendering! Please add plugins to materials before any rendering with this material occurs.`;
- }
- const pluginClassName = plugin.getClassName();
- if (!MaterialPluginManager._MaterialPluginClassToMainDefine[pluginClassName]) {
- MaterialPluginManager._MaterialPluginClassToMainDefine[pluginClassName] = "MATERIALPLUGIN_" + ++MaterialPluginManager._MaterialPluginCounter;
- }
- this._material._callbackPluginEventGeneric = (id, info) => this._handlePluginEvent(id, info);
- this._plugins.push(plugin);
- this._plugins.sort((a, b) => a.priority - b.priority);
- this._codeInjectionPoints = {};
- const defineNamesFromPlugins = {};
- defineNamesFromPlugins[MaterialPluginManager._MaterialPluginClassToMainDefine[pluginClassName]] = {
- type: "boolean",
- default: true,
- };
- for (const plugin of this._plugins) {
- plugin.collectDefines(defineNamesFromPlugins);
- this._collectPointNames("vertex", plugin.getCustomCode("vertex"));
- this._collectPointNames("fragment", plugin.getCustomCode("fragment"));
- }
- this._defineNamesFromPlugins = defineNamesFromPlugins;
- return true;
- }
- /**
- * @internal
- */
- _activatePlugin(plugin) {
- if (this._activePlugins.indexOf(plugin) === -1) {
- this._activePlugins.push(plugin);
- this._activePlugins.sort((a, b) => a.priority - b.priority);
- this._material._callbackPluginEventIsReadyForSubMesh = this._handlePluginEventIsReadyForSubMesh.bind(this);
- this._material._callbackPluginEventPrepareDefinesBeforeAttributes = this._handlePluginEventPrepareDefinesBeforeAttributes.bind(this);
- this._material._callbackPluginEventPrepareDefines = this._handlePluginEventPrepareDefines.bind(this);
- this._material._callbackPluginEventBindForSubMesh = this._handlePluginEventBindForSubMesh.bind(this);
- if (plugin.registerForExtraEvents) {
- this._activePluginsForExtraEvents.push(plugin);
- this._activePluginsForExtraEvents.sort((a, b) => a.priority - b.priority);
- this._material._callbackPluginEventHasRenderTargetTextures = this._handlePluginEventHasRenderTargetTextures.bind(this);
- this._material._callbackPluginEventFillRenderTargetTextures = this._handlePluginEventFillRenderTargetTextures.bind(this);
- this._material._callbackPluginEventHardBindForSubMesh = this._handlePluginEventHardBindForSubMesh.bind(this);
- }
- }
- }
- /**
- * Gets a plugin from the list of plugins managed by this manager
- * @param name name of the plugin
- * @returns the plugin if found, else null
- */
- getPlugin(name) {
- for (let i = 0; i < this._plugins.length; ++i) {
- if (this._plugins[i].name === name) {
- return this._plugins[i];
- }
- }
- return null;
- }
- _handlePluginEventIsReadyForSubMesh(eventData) {
- let isReady = true;
- for (const plugin of this._activePlugins) {
- isReady = isReady && plugin.isReadyForSubMesh(eventData.defines, this._scene, this._engine, eventData.subMesh);
- }
- eventData.isReadyForSubMesh = isReady;
- }
- _handlePluginEventPrepareDefinesBeforeAttributes(eventData) {
- for (const plugin of this._activePlugins) {
- plugin.prepareDefinesBeforeAttributes(eventData.defines, this._scene, eventData.mesh);
- }
- }
- _handlePluginEventPrepareDefines(eventData) {
- for (const plugin of this._activePlugins) {
- plugin.prepareDefines(eventData.defines, this._scene, eventData.mesh);
- }
- }
- _handlePluginEventHardBindForSubMesh(eventData) {
- for (const plugin of this._activePluginsForExtraEvents) {
- plugin.hardBindForSubMesh(this._material._uniformBuffer, this._scene, this._engine, eventData.subMesh);
- }
- }
- _handlePluginEventBindForSubMesh(eventData) {
- for (const plugin of this._activePlugins) {
- plugin.bindForSubMesh(this._material._uniformBuffer, this._scene, this._engine, eventData.subMesh);
- }
- }
- _handlePluginEventHasRenderTargetTextures(eventData) {
- let hasRenderTargetTextures = false;
- for (const plugin of this._activePluginsForExtraEvents) {
- hasRenderTargetTextures = plugin.hasRenderTargetTextures();
- if (hasRenderTargetTextures) {
- break;
- }
- }
- eventData.hasRenderTargetTextures = hasRenderTargetTextures;
- }
- _handlePluginEventFillRenderTargetTextures(eventData) {
- for (const plugin of this._activePluginsForExtraEvents) {
- plugin.fillRenderTargetTextures(eventData.renderTargets);
- }
- }
- _handlePluginEvent(id, info) {
- switch (id) {
- case MaterialPluginEvent.GetActiveTextures: {
- const eventData = info;
- for (const plugin of this._activePlugins) {
- plugin.getActiveTextures(eventData.activeTextures);
- }
- break;
- }
- case MaterialPluginEvent.GetAnimatables: {
- const eventData = info;
- for (const plugin of this._activePlugins) {
- plugin.getAnimatables(eventData.animatables);
- }
- break;
- }
- case MaterialPluginEvent.HasTexture: {
- const eventData = info;
- let hasTexture = false;
- for (const plugin of this._activePlugins) {
- hasTexture = plugin.hasTexture(eventData.texture);
- if (hasTexture) {
- break;
- }
- }
- eventData.hasTexture = hasTexture;
- break;
- }
- case MaterialPluginEvent.Disposed: {
- const eventData = info;
- for (const plugin of this._plugins) {
- plugin.dispose(eventData.forceDisposeTextures);
- }
- break;
- }
- case MaterialPluginEvent.GetDefineNames: {
- const eventData = info;
- eventData.defineNames = this._defineNamesFromPlugins;
- break;
- }
- case MaterialPluginEvent.PrepareEffect: {
- const eventData = info;
- for (const plugin of this._activePlugins) {
- eventData.fallbackRank = plugin.addFallbacks(eventData.defines, eventData.fallbacks, eventData.fallbackRank);
- plugin.getAttributes(eventData.attributes, this._scene, eventData.mesh);
- }
- if (this._uniformList.length > 0) {
- eventData.uniforms.push(...this._uniformList);
- }
- if (this._samplerList.length > 0) {
- eventData.samplers.push(...this._samplerList);
- }
- if (this._uboList.length > 0) {
- eventData.uniformBuffersNames.push(...this._uboList);
- }
- eventData.customCode = this._injectCustomCode(eventData, eventData.customCode);
- break;
- }
- case MaterialPluginEvent.PrepareUniformBuffer: {
- const eventData = info;
- this._uboDeclaration = "";
- this._vertexDeclaration = "";
- this._fragmentDeclaration = "";
- this._uniformList = [];
- this._samplerList = [];
- this._uboList = [];
- for (const plugin of this._plugins) {
- const uniforms = plugin.getUniforms();
- if (uniforms) {
- if (uniforms.ubo) {
- for (const uniform of uniforms.ubo) {
- if (uniform.size && uniform.type) {
- const arraySize = uniform.arraySize ?? 0;
- eventData.ubo.addUniform(uniform.name, uniform.size, arraySize);
- this._uboDeclaration += `${uniform.type} ${uniform.name}${arraySize > 0 ? `[${arraySize}]` : ""};\n`;
- }
- this._uniformList.push(uniform.name);
- }
- }
- if (uniforms.vertex) {
- this._vertexDeclaration += uniforms.vertex + "\n";
- }
- if (uniforms.fragment) {
- this._fragmentDeclaration += uniforms.fragment + "\n";
- }
- }
- plugin.getSamplers(this._samplerList);
- plugin.getUniformBuffersNames(this._uboList);
- }
- break;
- }
- }
- }
- _collectPointNames(shaderType, customCode) {
- if (!customCode) {
- return;
- }
- for (const pointName in customCode) {
- if (!this._codeInjectionPoints[shaderType]) {
- this._codeInjectionPoints[shaderType] = {};
- }
- this._codeInjectionPoints[shaderType][pointName] = true;
- }
- }
- _injectCustomCode(eventData, existingCallback) {
- return (shaderType, code) => {
- if (existingCallback) {
- code = existingCallback(shaderType, code);
- }
- if (this._uboDeclaration) {
- code = code.replace("#define ADDITIONAL_UBO_DECLARATION", this._uboDeclaration);
- }
- if (this._vertexDeclaration) {
- code = code.replace("#define ADDITIONAL_VERTEX_DECLARATION", this._vertexDeclaration);
- }
- if (this._fragmentDeclaration) {
- code = code.replace("#define ADDITIONAL_FRAGMENT_DECLARATION", this._fragmentDeclaration);
- }
- const points = this._codeInjectionPoints?.[shaderType];
- if (!points) {
- return code;
- }
- let processorOptions = null;
- for (let pointName in points) {
- let injectedCode = "";
- for (const plugin of this._activePlugins) {
- let customCode = plugin.getCustomCode(shaderType)?.[pointName];
- if (!customCode) {
- continue;
- }
- if (plugin.resolveIncludes) {
- if (processorOptions === null) {
- const shaderLanguage = ShaderLanguage.GLSL;
- processorOptions = {
- defines: [],
- indexParameters: eventData.indexParameters,
- isFragment: false,
- shouldUseHighPrecisionShader: this._engine._shouldUseHighPrecisionShader,
- processor: undefined,
- supportsUniformBuffers: this._engine.supportsUniformBuffers,
- shadersRepository: ShaderStore.GetShadersRepository(shaderLanguage),
- includesShadersStore: ShaderStore.GetIncludesShadersStore(shaderLanguage),
- version: undefined,
- platformName: this._engine.shaderPlatformName,
- processingContext: undefined,
- isNDCHalfZRange: this._engine.isNDCHalfZRange,
- useReverseDepthBuffer: this._engine.useReverseDepthBuffer,
- processCodeAfterIncludes: undefined, // not used by _ProcessIncludes
- };
- }
- processorOptions.isFragment = shaderType === "fragment";
- ShaderProcessor._ProcessIncludes(customCode, processorOptions, (code) => (customCode = code));
- }
- injectedCode += customCode + "\n";
- }
- if (injectedCode.length > 0) {
- if (pointName.charAt(0) === "!") {
- // pointName is a regular expression
- pointName = pointName.substring(1);
- let regexFlags = "g";
- if (pointName.charAt(0) === "!") {
- // no flags
- regexFlags = "";
- pointName = pointName.substring(1);
- }
- else {
- // get the flag(s)
- const matchOption = rxOption.exec(pointName);
- if (matchOption && matchOption.length >= 2) {
- regexFlags = matchOption[1];
- pointName = pointName.substring(regexFlags.length + 1);
- }
- }
- if (regexFlags.indexOf("g") < 0) {
- // we force the "g" flag so that the regexp object is stateful!
- regexFlags += "g";
- }
- const sourceCode = code;
- const rx = new RegExp(pointName, regexFlags);
- let match = rx.exec(sourceCode);
- while (match !== null) {
- let newCode = injectedCode;
- for (let i = 0; i < match.length; ++i) {
- newCode = newCode.replace("$" + i, match[i]);
- }
- code = code.replace(match[0], newCode);
- match = rx.exec(sourceCode);
- }
- }
- else {
- const fullPointName = "#define " + pointName;
- code = code.replace(fullPointName, "\n" + injectedCode + "\n" + fullPointName);
- }
- }
- }
- return code;
- };
- }
- }
- /** Map a plugin class name to a #define name (used in the vertex/fragment shaders as a marker of the plugin usage) */
- MaterialPluginManager._MaterialPluginClassToMainDefine = {};
- MaterialPluginManager._MaterialPluginCounter = 0;
- (() => {
- EngineStore.OnEnginesDisposedObservable.add(() => {
- UnregisterAllMaterialPlugins();
- });
- })();
- const plugins = [];
- let inited = false;
- let observer = null;
- /**
- * Registers a new material plugin through a factory, or updates it. This makes the plugin available to all materials instantiated after its registration.
- * @param pluginName The plugin name
- * @param factory The factory function which allows to create the plugin
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export function RegisterMaterialPlugin(pluginName, factory) {
- if (!inited) {
- observer = Material.OnEventObservable.add((material) => {
- for (const [, factory] of plugins) {
- factory(material);
- }
- }, MaterialPluginEvent.Created);
- inited = true;
- }
- const existing = plugins.filter(([name, _factory]) => name === pluginName);
- if (existing.length > 0) {
- existing[0][1] = factory;
- }
- else {
- plugins.push([pluginName, factory]);
- }
- }
- /**
- * Removes a material plugin from the list of global plugins.
- * @param pluginName The plugin name
- * @returns true if the plugin has been removed, else false
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export function UnregisterMaterialPlugin(pluginName) {
- for (let i = 0; i < plugins.length; ++i) {
- if (plugins[i][0] === pluginName) {
- plugins.splice(i, 1);
- if (plugins.length === 0) {
- UnregisterAllMaterialPlugins();
- }
- return true;
- }
- }
- return false;
- }
- /**
- * Clear the list of global material plugins
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export function UnregisterAllMaterialPlugins() {
- plugins.length = 0;
- inited = false;
- Material.OnEventObservable.remove(observer);
- observer = null;
- }
- //# sourceMappingURL=materialPluginManager.js.map
|