UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

498 lines (497 loc) 20.5 kB
import { __decorate } from "../tslib.es6.js"; import { serialize, serializeAsColor4, serializeAsCameraReference } from "../Misc/decorators.js"; import { Tools } from "../Misc/tools.js"; import { Observable } from "../Misc/observable.js"; import { EngineStore } from "../Engines/engineStore.js"; import { Texture } from "../Materials/Textures/texture.js"; import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture.js"; import { _WarnImport } from "../Misc/devTools.js"; import { GetExponentOfTwo } from "../Misc/tools.functions.js"; import { ThinEffectLayer } from "./thinEffectLayer.js"; import { UniqueIdGenerator } from "../Misc/uniqueIdGenerator.js"; /** * The effect layer Helps adding post process effect blended with the main pass. * * This can be for instance use to generate glow or highlight effects on the scene. * * The effect layer class can not be used directly and is intented to inherited from to be * customized per effects. */ export class EffectLayer { get _shouldRender() { return this._thinEffectLayer._shouldRender; } set _shouldRender(value) { this._thinEffectLayer._shouldRender = value; } get _emissiveTextureAndColor() { return this._thinEffectLayer._emissiveTextureAndColor; } set _emissiveTextureAndColor(value) { this._thinEffectLayer._emissiveTextureAndColor = value; } get _effectIntensity() { return this._thinEffectLayer._effectIntensity; } set _effectIntensity(value) { this._thinEffectLayer._effectIntensity = value; } /** * Force all the effect layers to compile to glsl even on WebGPU engines. * False by default. This is mostly meant for backward compatibility. */ static get ForceGLSL() { return ThinEffectLayer.ForceGLSL; } static set ForceGLSL(value) { ThinEffectLayer.ForceGLSL = value; } /** * The name of the layer */ get name() { return this._thinEffectLayer.name; } set name(value) { this._thinEffectLayer.name = value; } /** * The clear color of the texture used to generate the glow map. */ get neutralColor() { return this._thinEffectLayer.neutralColor; } set neutralColor(value) { this._thinEffectLayer.neutralColor = value; } /** * Specifies whether the highlight layer is enabled or not. */ get isEnabled() { return this._thinEffectLayer.isEnabled; } set isEnabled(value) { this._thinEffectLayer.isEnabled = value; } /** * Gets the camera attached to the layer. */ get camera() { return this._thinEffectLayer.camera; } /** * Gets the rendering group id the layer should render in. */ get renderingGroupId() { return this._thinEffectLayer.renderingGroupId; } set renderingGroupId(renderingGroupId) { this._thinEffectLayer.renderingGroupId = renderingGroupId; } /** * Specifies if the bounding boxes should be rendered normally or if they should undergo the effect of the layer */ get disableBoundingBoxesFromEffectLayer() { return this._thinEffectLayer.disableBoundingBoxesFromEffectLayer; } set disableBoundingBoxesFromEffectLayer(value) { this._thinEffectLayer.disableBoundingBoxesFromEffectLayer = value; } /** * Gets the main texture where the effect is rendered */ get mainTexture() { return this._mainTexture; } get _shaderLanguage() { return this._thinEffectLayer.shaderLanguage; } /** * Gets the shader language used in this material. */ get shaderLanguage() { return this._thinEffectLayer.shaderLanguage; } /** * Sets a specific material to be used to render a mesh/a list of meshes in the layer * @param mesh mesh or array of meshes * @param material material to use by the layer when rendering the mesh(es). If undefined is passed, the specific material created by the layer will be used. */ setMaterialForRendering(mesh, material) { this._thinEffectLayer.setMaterialForRendering(mesh, material); } /** * Gets the intensity of the effect for a specific mesh. * @param mesh The mesh to get the effect intensity for * @returns The intensity of the effect for the mesh */ getEffectIntensity(mesh) { return this._thinEffectLayer.getEffectIntensity(mesh); } /** * Sets the intensity of the effect for a specific mesh. * @param mesh The mesh to set the effect intensity for * @param intensity The intensity of the effect for the mesh */ setEffectIntensity(mesh, intensity) { this._thinEffectLayer.setEffectIntensity(mesh, intensity); } /** * Instantiates a new effect Layer and references it in the scene. * @param name The name of the layer * @param scene The scene to use the layer in * @param forceGLSL Use the GLSL code generation for the shader (even on WebGPU). Default is false * @param thinEffectLayer The thin instance of the effect layer (optional) */ constructor( /** The Friendly of the effect in the scene */ name, scene, forceGLSL = false, thinEffectLayer) { this._mainTextureCreatedSize = { width: 0, height: 0 }; this._maxSize = 0; this._mainTextureDesiredSize = { width: 0, height: 0 }; this._postProcesses = []; this._textures = []; /** * The unique id of the layer */ this.uniqueId = UniqueIdGenerator.UniqueId; /** * An event triggered when the effect layer has been disposed. */ this.onDisposeObservable = new Observable(); /** * An event triggered when the effect layer is about rendering the main texture with the glowy parts. */ this.onBeforeRenderMainTextureObservable = new Observable(); /** * An event triggered when the generated texture is being merged in the scene. */ this.onBeforeComposeObservable = new Observable(); /** * An event triggered when the mesh is rendered into the effect render target. */ this.onBeforeRenderMeshToEffect = new Observable(); /** * An event triggered after the mesh has been rendered into the effect render target. */ this.onAfterRenderMeshToEffect = new Observable(); /** * An event triggered when the generated texture has been merged in the scene. */ this.onAfterComposeObservable = new Observable(); /** * An event triggered when the effect layer changes its size. */ this.onSizeChangedObservable = new Observable(); this._internalThinEffectLayer = !thinEffectLayer; if (!thinEffectLayer) { thinEffectLayer = new ThinEffectLayer(name, scene, forceGLSL, false, this._importShadersAsync.bind(this)); thinEffectLayer.getEffectName = this.getEffectName.bind(this); thinEffectLayer.isReady = this.isReady.bind(this); thinEffectLayer._createMergeEffect = this._createMergeEffect.bind(this); thinEffectLayer._createTextureAndPostProcesses = this._createTextureAndPostProcesses.bind(this); thinEffectLayer._internalCompose = this._internalRender.bind(this); thinEffectLayer._setEmissiveTextureAndColor = this._setEmissiveTextureAndColor.bind(this); thinEffectLayer._numInternalDraws = this._numInternalDraws.bind(this); thinEffectLayer._addCustomEffectDefines = this._addCustomEffectDefines.bind(this); thinEffectLayer.hasMesh = this.hasMesh.bind(this); thinEffectLayer.shouldRender = this.shouldRender.bind(this); thinEffectLayer._shouldRenderMesh = this._shouldRenderMesh.bind(this); thinEffectLayer._canRenderMesh = this._canRenderMesh.bind(this); thinEffectLayer._useMeshMaterial = this._useMeshMaterial.bind(this); } this._thinEffectLayer = thinEffectLayer; this.name = name; this._scene = scene || EngineStore.LastCreatedScene; EffectLayer._SceneComponentInitialization(this._scene); this._engine = this._scene.getEngine(); this._maxSize = this._engine.getCaps().maxTextureSize; this._scene.addEffectLayer(this); this._thinEffectLayer.onDisposeObservable.add(() => { this.onDisposeObservable.notifyObservers(this); }); this._thinEffectLayer.onBeforeRenderLayerObservable.add(() => { this.onBeforeRenderMainTextureObservable.notifyObservers(this); }); this._thinEffectLayer.onBeforeComposeObservable.add(() => { this.onBeforeComposeObservable.notifyObservers(this); }); this._thinEffectLayer.onBeforeRenderMeshToEffect.add((mesh) => { this.onBeforeRenderMeshToEffect.notifyObservers(mesh); }); this._thinEffectLayer.onAfterRenderMeshToEffect.add((mesh) => { this.onAfterRenderMeshToEffect.notifyObservers(mesh); }); this._thinEffectLayer.onAfterComposeObservable.add(() => { this.onAfterComposeObservable.notifyObservers(this); }); } get _shadersLoaded() { return this._thinEffectLayer._shadersLoaded; } set _shadersLoaded(value) { this._thinEffectLayer._shadersLoaded = value; } /** * Number of times _internalRender will be called. Some effect layers need to render the mesh several times, so they should override this method with the number of times the mesh should be rendered * @returns Number of times a mesh must be rendered in the layer */ _numInternalDraws() { return this._internalThinEffectLayer ? 1 : this._thinEffectLayer._numInternalDraws(); } /** * Initializes the effect layer with the required options. * @param options Sets of none mandatory options to use with the layer (see IEffectLayerOptions for more information) */ _init(options) { // Adapt options this._effectLayerOptions = { mainTextureRatio: 0.5, alphaBlendingMode: 2, camera: null, renderingGroupId: -1, mainTextureType: 0, generateStencilBuffer: false, ...options, }; this._setMainTextureSize(); this._thinEffectLayer._init(options); this._createMainTexture(); this._createTextureAndPostProcesses(); } /** * Sets the main texture desired size which is the closest power of two * of the engine canvas size. */ _setMainTextureSize() { if (this._effectLayerOptions.mainTextureFixedSize) { this._mainTextureDesiredSize.width = this._effectLayerOptions.mainTextureFixedSize; this._mainTextureDesiredSize.height = this._effectLayerOptions.mainTextureFixedSize; } else { this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._effectLayerOptions.mainTextureRatio; this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._effectLayerOptions.mainTextureRatio; this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width; this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height; } this._mainTextureDesiredSize.width = Math.floor(this._mainTextureDesiredSize.width); this._mainTextureDesiredSize.height = Math.floor(this._mainTextureDesiredSize.height); } /** * Creates the main texture for the effect layer. */ _createMainTexture() { this._mainTexture = new RenderTargetTexture("EffectLayerMainRTT", { width: this._mainTextureDesiredSize.width, height: this._mainTextureDesiredSize.height, }, this._scene, { type: this._effectLayerOptions.mainTextureType, samplingMode: Texture.TRILINEAR_SAMPLINGMODE, generateStencilBuffer: this._effectLayerOptions.generateStencilBuffer, existingObjectRenderer: this._thinEffectLayer.objectRenderer, }); this._mainTexture.activeCamera = this._effectLayerOptions.camera; this._mainTexture.wrapU = Texture.CLAMP_ADDRESSMODE; this._mainTexture.wrapV = Texture.CLAMP_ADDRESSMODE; this._mainTexture.anisotropicFilteringLevel = 1; this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE); this._mainTexture.renderParticles = false; this._mainTexture.renderList = null; this._mainTexture.ignoreCameraViewport = true; this._mainTexture.onClearObservable.add((engine) => { engine.clear(this.neutralColor, true, true, true); }); } /** * Adds specific effects defines. * @param defines The defines to add specifics to. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars _addCustomEffectDefines(defines) { // Nothing to add by default. } /** * Checks for the readiness of the element composing the layer. * @param subMesh the mesh to check for * @param useInstances specify whether or not to use instances to render the mesh * @param emissiveTexture the associated emissive texture used to generate the glow * @returns true if ready otherwise, false */ _isReady(subMesh, useInstances, emissiveTexture) { return this._internalThinEffectLayer ? this._thinEffectLayer._internalIsSubMeshReady(subMesh, useInstances, emissiveTexture) : this._thinEffectLayer._isSubMeshReady(subMesh, useInstances, emissiveTexture); } async _importShadersAsync() { } _arePostProcessAndMergeReady() { return this._internalThinEffectLayer ? this._thinEffectLayer._internalIsLayerReady() : this._thinEffectLayer.isLayerReady(); } /** * Checks if the layer is ready to be used. * @returns true if the layer is ready to be used */ isLayerReady() { return this._arePostProcessAndMergeReady() && this._mainTexture.isReady(); } /** * Renders the glowing part of the scene by blending the blurred glowing meshes on top of the rendered scene. */ render() { if (!this._thinEffectLayer.compose()) { return; } // Handle size changes. this._setMainTextureSize(); if ((this._mainTextureCreatedSize.width !== this._mainTextureDesiredSize.width || this._mainTextureCreatedSize.height !== this._mainTextureDesiredSize.height) && this._mainTextureDesiredSize.width !== 0 && this._mainTextureDesiredSize.height !== 0) { // Recreate RTT and post processes on size change. this.onSizeChangedObservable.notifyObservers(this); this._disposeTextureAndPostProcesses(); this._createMainTexture(); this._createTextureAndPostProcesses(); this._mainTextureCreatedSize.width = this._mainTextureDesiredSize.width; this._mainTextureCreatedSize.height = this._mainTextureDesiredSize.height; } } /** * Determine if a given mesh will be used in the current effect. * @param mesh mesh to test * @returns true if the mesh will be used */ hasMesh(mesh) { return this._internalThinEffectLayer ? this._thinEffectLayer._internalHasMesh(mesh) : this._thinEffectLayer.hasMesh(mesh); } /** * Returns true if the layer contains information to display, otherwise false. * @returns true if the glow layer should be rendered */ shouldRender() { return this._internalThinEffectLayer ? this._thinEffectLayer._internalShouldRender() : this._thinEffectLayer.shouldRender(); } /** * Returns true if the mesh should render, otherwise false. * @param mesh The mesh to render * @returns true if it should render otherwise false */ // eslint-disable-next-line @typescript-eslint/no-unused-vars _shouldRenderMesh(mesh) { return this._internalThinEffectLayer ? true : this._thinEffectLayer._shouldRenderMesh(mesh); } /** * Returns true if the mesh can be rendered, otherwise false. * @param mesh The mesh to render * @param material The material used on the mesh * @returns true if it can be rendered otherwise false */ _canRenderMesh(mesh, material) { return this._internalThinEffectLayer ? this._thinEffectLayer._internalCanRenderMesh(mesh, material) : this._thinEffectLayer._canRenderMesh(mesh, material); } /** * Returns true if the mesh should render, otherwise false. * @returns true if it should render otherwise false */ _shouldRenderEmissiveTextureForMesh() { return true; } /** * Defines whether the current material of the mesh should be use to render the effect. * @param mesh defines the current mesh to render * @returns true if the mesh material should be use */ // eslint-disable-next-line @typescript-eslint/no-unused-vars _useMeshMaterial(mesh) { return this._internalThinEffectLayer ? false : this._thinEffectLayer._useMeshMaterial(mesh); } /** * Rebuild the required buffers. * @internal Internal use only. */ _rebuild() { this._thinEffectLayer._rebuild(); } /** * Dispose only the render target textures and post process. */ _disposeTextureAndPostProcesses() { this._mainTexture.dispose(); for (let i = 0; i < this._postProcesses.length; i++) { if (this._postProcesses[i]) { this._postProcesses[i].dispose(); } } this._postProcesses = []; for (let i = 0; i < this._textures.length; i++) { if (this._textures[i]) { this._textures[i].dispose(); } } this._textures = []; } /** * Dispose the highlight layer and free resources. */ dispose() { this._thinEffectLayer.dispose(); // Clean textures and post processes this._disposeTextureAndPostProcesses(); // Remove from scene this._scene.removeEffectLayer(this); // Callback this.onDisposeObservable.clear(); this.onBeforeRenderMainTextureObservable.clear(); this.onBeforeComposeObservable.clear(); this.onBeforeRenderMeshToEffect.clear(); this.onAfterRenderMeshToEffect.clear(); this.onAfterComposeObservable.clear(); this.onSizeChangedObservable.clear(); } /** * Gets the class name of the effect layer * @returns the string with the class name of the effect layer */ getClassName() { return "EffectLayer"; } /** * Creates an effect layer from parsed effect layer data * @param parsedEffectLayer defines effect layer data * @param scene defines the current scene * @param rootUrl defines the root URL containing the effect layer information * @returns a parsed effect Layer */ static Parse(parsedEffectLayer, scene, rootUrl) { const effectLayerType = Tools.Instantiate(parsedEffectLayer.customType); // eslint-disable-next-line @typescript-eslint/no-unsafe-return return effectLayerType.Parse(parsedEffectLayer, scene, rootUrl); } } /** * @internal */ EffectLayer._SceneComponentInitialization = (_) => { throw _WarnImport("EffectLayerSceneComponent"); }; __decorate([ serialize() ], EffectLayer.prototype, "name", null); __decorate([ serializeAsColor4() ], EffectLayer.prototype, "neutralColor", null); __decorate([ serialize() ], EffectLayer.prototype, "isEnabled", null); __decorate([ serializeAsCameraReference() ], EffectLayer.prototype, "camera", null); __decorate([ serialize() ], EffectLayer.prototype, "renderingGroupId", null); __decorate([ serialize() ], EffectLayer.prototype, "disableBoundingBoxesFromEffectLayer", null); //# sourceMappingURL=effectLayer.js.map