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.

491 lines (490 loc) 19.9 kB
import { __decorate } from "../tslib.es6.js"; /* eslint-disable @typescript-eslint/no-unused-vars */ import { serialize } from "../Misc/decorators.js"; import { Observable } from "../Misc/observable.js"; import { Scene } from "../scene.js"; import { Vector2 } from "../Maths/math.vector.js"; import { Texture } from "../Materials/Textures/texture.js"; import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture.js"; import { PostProcess } from "../PostProcesses/postProcess.js"; import { PassPostProcess } from "../PostProcesses/passPostProcess.js"; import { BlurPostProcess } from "../PostProcesses/blurPostProcess.js"; import { EffectLayer } from "./effectLayer.js"; import { Logger } from "../Misc/logger.js"; import { RegisterClass } from "../Misc/typeStore.js"; import { Color3 } from "../Maths/math.color.js"; import { SerializationHelper } from "../Misc/decorators.serialization.js"; import { GetExponentOfTwo } from "../Misc/tools.functions.js"; import { ThinHighlightLayer } from "./thinHighlightLayer.js"; import { ThinGlowBlurPostProcess } from "./thinEffectLayer.js"; Scene.prototype.getHighlightLayerByName = function (name) { for (let index = 0; index < this.effectLayers?.length; index++) { if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === HighlightLayer.EffectName) { return this.effectLayers[index]; } } return null; }; /** * Special Glow Blur post process only blurring the alpha channel * It enforces keeping the most luminous color in the color channel. */ class GlowBlurPostProcess extends PostProcess { constructor(name, direction, kernel, options, camera = null, samplingMode = Texture.BILINEAR_SAMPLINGMODE, engine, reusable) { const localOptions = { uniforms: ThinGlowBlurPostProcess.Uniforms, size: typeof options === "number" ? options : undefined, camera, samplingMode, engine, reusable, ...options, }; super(name, ThinGlowBlurPostProcess.FragmentUrl, { effectWrapper: typeof options === "number" || !options.effectWrapper ? new ThinGlowBlurPostProcess(name, engine, direction, kernel, localOptions) : undefined, ...localOptions, }); this.direction = direction; this.kernel = kernel; this.onApplyObservable.add(() => { this._effectWrapper.textureWidth = this.width; this._effectWrapper.textureHeight = this.height; }); } _gatherImports(useWebGPU, list) { if (useWebGPU) { this._webGPUReady = true; list.push(import("../ShadersWGSL/glowBlurPostProcess.fragment.js")); } else { list.push(import("../Shaders/glowBlurPostProcess.fragment.js")); } super._gatherImports(useWebGPU, list); } } /** * The highlight layer Helps adding a glow effect around a mesh. * * Once instantiated in a scene, simply use the addMesh or removeMesh method to add or remove * glowy meshes to your scene. * * !!! THIS REQUIRES AN ACTIVE STENCIL BUFFER ON THE CANVAS !!! */ export class HighlightLayer extends EffectLayer { /** * The neutral color used during the preparation of the glow effect. * This is black by default as the blend operation is a blend operation. */ static get NeutralColor() { return ThinHighlightLayer.NeutralColor; } static set NeutralColor(value) { ThinHighlightLayer.NeutralColor = value; } /** * Specifies whether or not the inner glow is ACTIVE in the layer. */ get innerGlow() { return this._thinEffectLayer.innerGlow; } set innerGlow(value) { this._thinEffectLayer.innerGlow = value; } /** * Specifies whether or not the outer glow is ACTIVE in the layer. */ get outerGlow() { return this._thinEffectLayer.outerGlow; } set outerGlow(value) { this._thinEffectLayer.outerGlow = value; } /** * Specifies the horizontal size of the blur. */ set blurHorizontalSize(value) { this._thinEffectLayer.blurHorizontalSize = value; } /** * Specifies the vertical size of the blur. */ set blurVerticalSize(value) { this._thinEffectLayer.blurVerticalSize = value; } /** * Gets the horizontal size of the blur. */ get blurHorizontalSize() { return this._thinEffectLayer.blurHorizontalSize; } /** * Gets the vertical size of the blur. */ get blurVerticalSize() { return this._thinEffectLayer.blurVerticalSize; } /** * Instantiates a new highlight Layer and references it to the scene.. * @param name The name of the layer * @param scene The scene to use the layer in * @param options Sets of none mandatory options to use with the layer (see IHighlightLayerOptions for more information) */ constructor(name, scene, options) { super(name, scene, options !== undefined ? !!options.forceGLSL : false, new ThinHighlightLayer(name, scene, options)); /** * An event triggered when the highlight layer is being blurred. */ this.onBeforeBlurObservable = new Observable(); /** * An event triggered when the highlight layer has been blurred. */ this.onAfterBlurObservable = new Observable(); // Warn on stencil if (!this._engine.isStencilEnable) { Logger.Warn("Rendering the Highlight Layer requires the stencil to be active on the canvas. var engine = new Engine(canvas, antialias, { stencil: true }"); } // Adapt options this._options = { mainTextureRatio: 0.5, blurTextureSizeRatio: 0.5, mainTextureFixedSize: 0, blurHorizontalSize: 1.0, blurVerticalSize: 1.0, alphaBlendingMode: 2, camera: null, renderingGroupId: -1, mainTextureType: 0, forceGLSL: false, isStroke: false, ...options, }; // Initialize the layer this._init(this._options); // Do not render as long as no meshes have been added this._shouldRender = false; } /** * Get the effect name of the layer. * @returns The effect name */ getEffectName() { return HighlightLayer.EffectName; } _numInternalDraws() { return 2; // we need two rendering, one for the inner glow and the other for the outer glow } /** * Create the merge effect. This is the shader use to blit the information back * to the main canvas at the end of the scene rendering. * @returns The effect created */ _createMergeEffect() { return this._thinEffectLayer._createMergeEffect(); } /** * Creates the render target textures and post processes used in the highlight layer. */ _createTextureAndPostProcesses() { let blurTextureWidth = this._mainTextureDesiredSize.width * this._options.blurTextureSizeRatio; let blurTextureHeight = this._mainTextureDesiredSize.height * this._options.blurTextureSizeRatio; blurTextureWidth = this._engine.needPOTTextures ? GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth; blurTextureHeight = this._engine.needPOTTextures ? GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight; let textureType = 0; if (this._engine.getCaps().textureHalfFloatRender) { textureType = 2; } else { textureType = 0; } this._blurTexture = new RenderTargetTexture("HighlightLayerBlurRTT", { width: blurTextureWidth, height: blurTextureHeight, }, this._scene, false, true, textureType); this._blurTexture.wrapU = Texture.CLAMP_ADDRESSMODE; this._blurTexture.wrapV = Texture.CLAMP_ADDRESSMODE; this._blurTexture.anisotropicFilteringLevel = 16; this._blurTexture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE); this._blurTexture.renderParticles = false; this._blurTexture.ignoreCameraViewport = true; this._textures = [this._blurTexture]; this._thinEffectLayer.bindTexturesForCompose = (effect) => { effect.setTexture("textureSampler", this._blurTexture); }; this._thinEffectLayer._createTextureAndPostProcesses(); if (this._options.alphaBlendingMode === 2) { this._downSamplePostprocess = new PassPostProcess("HighlightLayerPPP", { size: this._options.blurTextureSizeRatio, samplingMode: Texture.BILINEAR_SAMPLINGMODE, engine: this._scene.getEngine(), effectWrapper: this._thinEffectLayer._postProcesses[0], }); this._downSamplePostprocess.externalTextureSamplerBinding = true; this._downSamplePostprocess.onApplyObservable.add((effect) => { effect.setTexture("textureSampler", this._mainTexture); }); this._horizontalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize, { samplingMode: Texture.BILINEAR_SAMPLINGMODE, engine: this._scene.getEngine(), effectWrapper: this._thinEffectLayer._postProcesses[1], }); this._horizontalBlurPostprocess.onApplyObservable.add((effect) => { effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight); }); this._verticalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize, { samplingMode: Texture.BILINEAR_SAMPLINGMODE, engine: this._scene.getEngine(), effectWrapper: this._thinEffectLayer._postProcesses[2], }); this._verticalBlurPostprocess.onApplyObservable.add((effect) => { effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight); }); this._postProcesses = [this._downSamplePostprocess, this._horizontalBlurPostprocess, this._verticalBlurPostprocess]; } else { this._horizontalBlurPostprocess = new BlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize / 2, { size: { width: blurTextureWidth, height: blurTextureHeight, }, samplingMode: Texture.BILINEAR_SAMPLINGMODE, engine: this._scene.getEngine(), textureType, effectWrapper: this._thinEffectLayer._postProcesses[0], }); this._horizontalBlurPostprocess.width = blurTextureWidth; this._horizontalBlurPostprocess.height = blurTextureHeight; this._horizontalBlurPostprocess.externalTextureSamplerBinding = true; this._horizontalBlurPostprocess.onApplyObservable.add((effect) => { effect.setTexture("textureSampler", this._mainTexture); }); this._verticalBlurPostprocess = new BlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize / 2, { size: { width: blurTextureWidth, height: blurTextureHeight, }, samplingMode: Texture.BILINEAR_SAMPLINGMODE, engine: this._scene.getEngine(), textureType, }); this._postProcesses = [this._horizontalBlurPostprocess, this._verticalBlurPostprocess]; } this._mainTexture.onAfterUnbindObservable.add(() => { this.onBeforeBlurObservable.notifyObservers(this); const internalTexture = this._blurTexture.renderTarget; if (internalTexture) { this._scene.postProcessManager.directRender(this._postProcesses, internalTexture, true); this._engine.unBindFramebuffer(internalTexture, true); } this.onAfterBlurObservable.notifyObservers(this); }); // Prevent autoClear. this._postProcesses.map((pp) => { pp.autoClear = false; }); } /** * @returns whether or not the layer needs stencil enabled during the mesh rendering. */ needStencil() { return this._thinEffectLayer.needStencil(); } /** * 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 * @returns true if ready otherwise, false */ isReady(subMesh, useInstances) { return this._thinEffectLayer.isReady(subMesh, useInstances); } /** * Implementation specific of rendering the generating effect on the main canvas. * @param effect The effect used to render through * @param renderIndex */ _internalRender(effect, renderIndex) { this._thinEffectLayer._internalCompose(effect, renderIndex); } /** * @returns true if the layer contains information to display, otherwise false. */ shouldRender() { return 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 */ _shouldRenderMesh(mesh) { return 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._thinEffectLayer._canRenderMesh(mesh, material); } /** * Adds specific effects defines. * @param defines The defines to add specifics to. */ _addCustomEffectDefines(defines) { this._thinEffectLayer._addCustomEffectDefines(defines); } /** * Sets the required values for both the emissive texture and and the main color. * @param mesh * @param subMesh * @param material */ _setEmissiveTextureAndColor(mesh, subMesh, material) { this._thinEffectLayer._setEmissiveTextureAndColor(mesh, subMesh, material); } /** * Add a mesh in the exclusion list to prevent it to impact or being impacted by the highlight layer. * @param mesh The mesh to exclude from the highlight layer */ addExcludedMesh(mesh) { this._thinEffectLayer.addExcludedMesh(mesh); } /** * Remove a mesh from the exclusion list to let it impact or being impacted by the highlight layer. * @param mesh The mesh to highlight */ removeExcludedMesh(mesh) { this._thinEffectLayer.removeExcludedMesh(mesh); } /** * Determine if a given mesh will be highlighted by the current HighlightLayer * @param mesh mesh to test * @returns true if the mesh will be highlighted by the current HighlightLayer */ hasMesh(mesh) { return this._thinEffectLayer.hasMesh(mesh); } /** * Add a mesh in the highlight layer in order to make it glow with the chosen color. * @param mesh The mesh to highlight * @param color The color of the highlight * @param glowEmissiveOnly Extract the glow from the emissive texture */ addMesh(mesh, color, glowEmissiveOnly = false) { this._thinEffectLayer.addMesh(mesh, color, glowEmissiveOnly); } /** * Remove a mesh from the highlight layer in order to make it stop glowing. * @param mesh The mesh to highlight */ removeMesh(mesh) { this._thinEffectLayer.removeMesh(mesh); } /** * Remove all the meshes currently referenced in the highlight layer */ removeAllMeshes() { this._thinEffectLayer.removeAllMeshes(); } /** * Free any resources and references associated to a mesh. * Internal use * @param mesh The mesh to free. * @internal */ _disposeMesh(mesh) { this._thinEffectLayer._disposeMesh(mesh); } /** * Gets the class name of the effect layer * @returns the string with the class name of the effect layer */ getClassName() { return "HighlightLayer"; } /** * Serializes this Highlight layer * @returns a serialized Highlight layer object */ serialize() { const serializationObject = SerializationHelper.Serialize(this); serializationObject.customType = "BABYLON.HighlightLayer"; // Highlighted meshes serializationObject.meshes = []; const meshes = this._thinEffectLayer._meshes; if (meshes) { for (const m in meshes) { const mesh = meshes[m]; if (mesh) { serializationObject.meshes.push({ glowEmissiveOnly: mesh.glowEmissiveOnly, color: mesh.color.asArray(), meshId: mesh.mesh.id, }); } } } // Excluded meshes serializationObject.excludedMeshes = []; const excludedMeshes = this._thinEffectLayer._excludedMeshes; if (excludedMeshes) { for (const e in excludedMeshes) { const excludedMesh = excludedMeshes[e]; if (excludedMesh) { serializationObject.excludedMeshes.push(excludedMesh.mesh.id); } } } return serializationObject; } /** * Creates a Highlight layer from parsed Highlight layer data * @param parsedHightlightLayer defines the Highlight layer data * @param scene defines the current scene * @param rootUrl defines the root URL containing the Highlight layer information * @returns a parsed Highlight layer */ static Parse(parsedHightlightLayer, scene, rootUrl) { const hl = SerializationHelper.Parse(() => new HighlightLayer(parsedHightlightLayer.name, scene, parsedHightlightLayer.options), parsedHightlightLayer, scene, rootUrl); let index; // Excluded meshes for (index = 0; index < parsedHightlightLayer.excludedMeshes.length; index++) { const mesh = scene.getMeshById(parsedHightlightLayer.excludedMeshes[index]); if (mesh) { hl.addExcludedMesh(mesh); } } // Included meshes for (index = 0; index < parsedHightlightLayer.meshes.length; index++) { const highlightedMesh = parsedHightlightLayer.meshes[index]; const mesh = scene.getMeshById(highlightedMesh.meshId); if (mesh) { hl.addMesh(mesh, Color3.FromArray(highlightedMesh.color), highlightedMesh.glowEmissiveOnly); } } return hl; } } /** * Effect Name of the highlight layer. */ HighlightLayer.EffectName = "HighlightLayer"; __decorate([ serialize() ], HighlightLayer.prototype, "innerGlow", null); __decorate([ serialize() ], HighlightLayer.prototype, "outerGlow", null); __decorate([ serialize() ], HighlightLayer.prototype, "blurHorizontalSize", null); __decorate([ serialize() ], HighlightLayer.prototype, "blurVerticalSize", null); __decorate([ serialize("options") ], HighlightLayer.prototype, "_options", void 0); RegisterClass("BABYLON.HighlightLayer", HighlightLayer); //# sourceMappingURL=highlightLayer.js.map