@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.
329 lines (328 loc) • 13 kB
JavaScript
import { __decorate } from "../tslib.es6.js";
import { serialize, serializeAsColor3 } from "../Misc/decorators.js";
import { Scene } from "../scene.js";
import { EffectLayer } from "./effectLayer.js";
import { RegisterClass } from "../Misc/typeStore.js";
import { SerializationHelper } from "../Misc/decorators.serialization.js";
import { ThinSelectionOutlineLayer } from "./thinSelectionOutlineLayer.js";
import "../Rendering/depthRendererSceneComponent.js";
Scene.prototype.getSelectionOutlineLayerByName = function (name) {
for (let index = 0; index < this.effectLayers?.length; index++) {
if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === SelectionOutlineLayer.EffectName) {
return this.effectLayers[index];
}
}
return null;
};
/**
* The selection outline layer Helps adding a outline effect around a mesh.
*
* Once instantiated in a scene, simply use the addMesh or removeMesh method to add or remove
* outlined meshes to your scene.
*/
export class SelectionOutlineLayer extends EffectLayer {
/**
* Effect Name of the selection outline layer.
*/
static get EffectName() {
return ThinSelectionOutlineLayer.EffectName;
}
/**
* The outline color (default (1, 0.5, 0))
*/
get outlineColor() {
return this._thinEffectLayer.outlineColor;
}
set outlineColor(value) {
this._thinEffectLayer.outlineColor = value;
}
/**
* The thickness of the edges (default: 2.0)
*/
get outlineThickness() {
return this._thinEffectLayer.outlineThickness;
}
set outlineThickness(value) {
this._thinEffectLayer.outlineThickness = value;
}
/**
* The strength of the occlusion effect (default: 0.8)
*/
get occlusionStrength() {
return this._thinEffectLayer.occlusionStrength;
}
set occlusionStrength(value) {
this._thinEffectLayer.occlusionStrength = value;
}
/**
* The occlusion threshold (default: 0.01)
*/
get occlusionThreshold() {
return this._thinEffectLayer.occlusionThreshold;
}
set occlusionThreshold(value) {
this._thinEffectLayer.occlusionThreshold = value;
}
/**
* Instantiates a new selection outline 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 ISelectionOutlineLayerOptions for more information)
*/
constructor(name, scene, options) {
super(name, scene, options !== undefined ? !!options.forceGLSL : false, new ThinSelectionOutlineLayer(name, scene, options));
// Adapt options
this._options = {
mainTextureRatio: 1.0,
mainTextureFixedSize: 0,
alphaBlendingMode: 2,
camera: null,
mainTextureSamples: 1,
renderingGroupId: -1,
mainTextureType: 1,
mainTextureFormat: 7,
forceGLSL: false,
storeCameraSpaceZ: false,
outlineMethod: 0,
...options,
};
// Fall back to a supported mask texture type if the device doesn't support rendering to float framebuffers
// or linear filtering of float textures (e.g. OES_texture_float_linear missing on some iOS versions)
if (this._options.mainTextureType === 1 && !(this._engine.getCaps().textureFloatRender && this._engine.getCaps().textureFloatLinearFiltering)) {
this._options.mainTextureType = 2;
}
if (this._options.mainTextureType === 2 && !this._engine.getCaps().textureHalfFloatRender && !this._options.storeCameraSpaceZ) {
this._options.mainTextureType = 0;
}
// Initialize the layer
this._init(this._options);
// Do not render as long as no meshes have been added
this._shouldRender = false;
this._scene.enableDepthRenderer();
}
/**
* Get the effect name of the layer.
* @returns The effect name
*/
getEffectName() {
return SelectionOutlineLayer.EffectName;
}
_numInternalDraws() {
return 1; // draw depth mask on main pass and outline on merge pass
}
/**
* 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 selection outline layer.
*/
_createTextureAndPostProcesses() {
this._textures = [];
this._thinEffectLayer.bindTexturesForCompose = (effect) => {
effect.setTexture("maskSampler", this._mainTexture);
const depthRenderer = this._scene.enableDepthRenderer();
effect.setTexture("depthSampler", depthRenderer.getDepthMap());
const mainTextureDesiredSize = this._mainTextureDesiredSize;
this._thinEffectLayer.textureWidth = mainTextureDesiredSize.width;
this._thinEffectLayer.textureHeight = mainTextureDesiredSize.height;
};
this._thinEffectLayer._createTextureAndPostProcesses();
this._postProcesses = [];
this._mainTexture.samples = this._options.mainTextureSamples;
this._mainTexture.onAfterUnbindObservable.add(() => {
// glow layer and highlight layer both call this._scene.postProcessManager.directRender
// when you call this._scene.postProcessManager.directRender, it has 4 side effects:
// 1. binds the framebuffer
// 2. setAlphaMode(ALPHA_DISABLE)
// 3. setDepthBuffer(true)
// 4. setDepthWrite(true)
// glow layer and highlight layer are restore framebuffer and depends on other side effects
// but for now 3 and 4 are not needed to resolve the state management issue, so we just restore alpha mode
this._scene.getEngine().setAlphaMode(0);
});
}
/**
* Creates the main texture for the effect layer.
*/
_createMainTexture() {
super._createMainTexture();
// set the render list for selective rendering
this._mainTexture.renderList = this._thinEffectLayer._selection;
}
/**
* @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);
}
/**
* Determine if a given mesh will be highlighted by the current SelectionOutlineLayer
* @param mesh mesh to test
* @returns true if the mesh will be highlighted by the current SelectionOutlineLayer
*/
hasMesh(mesh) {
return this._thinEffectLayer.hasMesh(mesh);
}
/**
* Remove all the meshes currently referenced in the selection outline layer
*/
clearSelection() {
this._thinEffectLayer.clearSelection();
this._mainTexture.renderList = this._thinEffectLayer._selection; // update render list
}
/**
* Adds mesh or group of mesh to the current selection
*
* If a group of meshes is provided, they will outline as a single unit
* @param meshOrGroup Meshes to add to the selection
*/
addSelection(meshOrGroup) {
this._thinEffectLayer.addSelection(meshOrGroup);
}
/**
* 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 "SelectionOutlineLayer";
}
/**
* Serializes this SelectionOutline layer
* @returns a serialized SelectionOutline layer object
*/
serialize() {
const serializationObject = SerializationHelper.Serialize(this);
serializationObject.customType = "BABYLON.SelectionOutlineLayer";
// Selected meshes
serializationObject.selection = [];
const selection = this._thinEffectLayer._selection;
if (selection) {
const meshUniqueIdToSelectionId = this._thinEffectLayer._meshUniqueIdToSelectionId;
// selection can be sparse since _removeMesh can remove entries
const selectionMap = {};
for (let i = 0; i < selection.length; ++i) {
const mesh = selection[i];
const selectionId = meshUniqueIdToSelectionId[mesh.uniqueId];
if (!selectionMap[selectionId]) {
selectionMap[selectionId] = {
meshIds: [],
};
}
selectionMap[selectionId].meshIds.push(mesh.id);
}
serializationObject.selection = selectionMap;
}
return serializationObject;
}
/**
* Creates a SelectionOutline layer from parsed SelectionOutline layer data
* @param parsedSelectionOutlineLayer defines the SelectionOutline layer data
* @param scene defines the current scene
* @param rootUrl defines the root URL containing the SelectionOutline layer information
* @returns a parsed SelectionOutline layer
*/
static Parse(parsedSelectionOutlineLayer, scene, rootUrl) {
const selectionOutlineLayer = SerializationHelper.Parse(() => new SelectionOutlineLayer(parsedSelectionOutlineLayer.name, scene, parsedSelectionOutlineLayer.options), parsedSelectionOutlineLayer, scene, rootUrl);
const selectionMap = parsedSelectionOutlineLayer.selection;
// Selected meshes
for (const outlinedMeshes of Object.values(selectionMap)) {
const meshes = [];
for (let meshIndex = 0; meshIndex < outlinedMeshes.meshIds.length; meshIndex++) {
const meshId = outlinedMeshes.meshIds[meshIndex];
const mesh = scene.getMeshById(meshId);
if (mesh) {
meshes.push(mesh);
}
}
selectionOutlineLayer.addSelection(meshes);
}
return selectionOutlineLayer;
}
}
__decorate([
serializeAsColor3()
], SelectionOutlineLayer.prototype, "outlineColor", null);
__decorate([
serialize()
], SelectionOutlineLayer.prototype, "outlineThickness", null);
__decorate([
serialize()
], SelectionOutlineLayer.prototype, "occlusionStrength", null);
__decorate([
serialize()
], SelectionOutlineLayer.prototype, "occlusionThreshold", null);
__decorate([
serialize("options")
], SelectionOutlineLayer.prototype, "_options", void 0);
RegisterClass("BABYLON.SelectionOutlineLayer", SelectionOutlineLayer);
//# sourceMappingURL=selectionOutlineLayer.js.map