/** * The OutlineRenderer draws solid color outlines around the silhouettes of entities, for example * to highlight objects that are selected or hovered in an editor. Each entity can be outlined in * its own color. * * The outlines are generated in three steps: * * - An internal camera renders the mesh instances of the added entities into an offscreen texture * matching the resolution of the scene camera, with each object drawn in its outline color. * - The edges of the objects in the texture are detected and expanded to form the outlines. * - The outlines are composited on top of the scene, just before the scene camera renders the * layer passed to {@link OutlineRenderer#frameUpdate}. * * The outlines are drawn over everything the scene camera has rendered up to that layer, so they * remain visible when the outlined objects are occluded by other objects. Anything rendered in * that layer or after it, such as gizmos, is drawn on top of the outlines. * * {@link OutlineRenderer#frameUpdate} needs to be called every frame to keep the outlines in sync * with the scene camera. Only render and model components are outlined, and the outline color is * applied to mesh instances using a {@link StandardMaterial}. * * Relevant Engine API examples: * * - [Outlines Colored](https://playcanvas.github.io/#/graphics/outlines-colored) * - [Editor](https://playcanvas.github.io/#/misc/editor) * * @example * // Create a layer used to render the outlined objects. It is added to the layer composition, but * // not to the scene camera, so that the camera does not render the outlined objects a second time. * const outlineLayer = new Layer({ name: 'OutlineLayer' }); * app.scene.layers.push(outlineLayer); * * // Create the outline renderer * const outlineRenderer = new OutlineRenderer(app, outlineLayer); * * // Outline an entity and its descendants in red, and another entity in white * outlineRenderer.addEntity(entity1, Color.RED); * outlineRenderer.addEntity(entity2, Color.WHITE); * * // Each frame, composite the outlines into the scene before the scene camera renders the opaque * // part of the 'Immediate' layer * const immediateLayer = app.scene.layers.getLayerByName('Immediate'); * app.on('update', () => { * outlineRenderer.frameUpdate(cameraEntity, immediateLayer, false); * }); * * // Later, stop outlining the first entity * outlineRenderer.removeEntity(entity1); * * @category Graphics */ export class OutlineRenderer { /** * Create a new OutlineRenderer. * * @param {AppBase} app - The application. * @param {Layer} [renderingLayer] - The layer the outlined mesh instances are added to, and * which the internal outline camera renders. It must be part of the scene's layer composition. * Defaults to the 'Immediate' layer. As the scene camera renders the 'Immediate' layer by * default, the outlined objects are then rendered by the scene camera a second time - to avoid * this, supply a dedicated layer which is not rendered by any other camera. * @param {number} [priority] - The priority of the internal outline camera. It needs to render * before the scene camera, so it has to be smaller than the priority of the scene camera. * Defaults to -1. */ constructor(app: AppBase, renderingLayer?: Layer, priority?: number); app: AppBase; renderingLayer: Layer; rt: RenderTarget; outlineCameraEntity: Entity; outlineShaderPass: number; postRender: (cameraComponent: any) => void; blendCamera: import("../../index.js").CameraComponent; blendLayer: Layer; blendLayerTransparent: boolean; preRenderLayer: (cameraComponent: any, layer: any, transparent: any) => void; outlinedMeshInstances: Set; tempRt: RenderTarget; blendState: BlendState; shaderExtend: import("../../index.js").Shader; shaderBlend: import("../../index.js").Shader; quadRenderer: QuadRender; /** * Destroy the outline renderer and its resources. All entities are removed from the outline * renderer first. */ destroy(): void; /** * Collect the mesh instances of an entity's render and model components. * * @param {Entity} entity - The entity to collect from. * @param {boolean} recursive - Whether to include the entity's descendants. * @param {boolean} [includeDisabled] - Whether to include components that are not rendered. * Defaults to false, which is what an entity being added wants: a disabled component's mesh * instances are removed from the scene's layers, but the outline layer keeps its own list, so * including them would outline objects that are not drawn. Removal passes true, so an entity * disabled after it was added can still be removed. * @returns {MeshInstance[]} The mesh instances. * @ignore */ getMeshInstances(entity: Entity, recursive: boolean, includeDisabled?: boolean): MeshInstance[]; /** * Add an entity to the outline renderer, to draw an outline around it. The mesh instances of * the entity's render and model components are outlined, including those of its descendants * unless `recursive` is false. Adding an entity that is already outlined changes its outline * color. * * Render and model components that are not currently rendered, because they or their entity * are disabled, are skipped - this is evaluated when the entity is added. * * An entity should be outlined by a single outline renderer at a time. The outline color is * stored on its mesh instances, so they cannot be outlined by more than one renderer, and * removing them from one renderer would remove their outline from the other as well. * * @param {Entity} entity - The entity to add. * @param {Color} color - The color of the outline. The alpha component is ignored. * @param {boolean} [recursive] - Whether to also add the mesh instances of the entity's * descendants. Defaults to true. * @example * // outline an entity and its descendants in orange * outlineRenderer.addEntity(entity, new Color(1, 0.5, 0)); */ addEntity(entity: Entity, color: Color, recursive?: boolean): void; /** * Remove an entity from the outline renderer, to stop drawing its outline. This also works for * an entity that has been disabled since it was added. * * @param {Entity} entity - The entity to remove. * @param {boolean} [recursive] - Whether to also remove the mesh instances of the entity's * descendants. Defaults to true. * @example * outlineRenderer.removeEntity(entity); */ removeEntity(entity: Entity, recursive?: boolean): void; /** * Remove all entities from the outline renderer, for example to clear the selection. * * @example * // outline only the newly selected entity * outlineRenderer.removeAllEntities(); * outlineRenderer.addEntity(selectedEntity, Color.WHITE); */ removeAllEntities(): void; /** * Remove outlined mesh instances from the rendering layer and delete their outline color. * * @param {MeshInstance[]} meshInstances - The mesh instances, all added by this renderer. * @ignore */ removeMeshInstances(meshInstances: MeshInstance[]): void; blendOutlines(): void; onPostRender(): void; createRenderTarget(name: any, width: any, height: any, depth: any): RenderTarget; updateRenderTarget(sceneCamera: any): void; /** * Update the outline renderer. This needs to be called once per frame, after the scene camera * has been positioned, for example from the application's `update` event, which fires after * scripts have been updated. It matches the internal outline camera to the scene camera's * transform, projection, clip planes and resolution, and schedules the outlines to be * composited into the scene for this frame. * * The outlines are composited just before the scene camera renders the opaque or transparent * part of `blendLayer`, so that part of the layer, and everything rendered after it, is drawn * on top of the outlines. The scene camera needs to render `blendLayer`, otherwise the * outlines are not visible. * * @param {Entity} sceneCameraEntity - The entity with the camera component used to render the * scene. * @param {Layer} blendLayer - The layer before which the outlines are composited. * @param {boolean} blendLayerTransparent - True to composite the outlines before the * transparent part of `blendLayer`, false to composite them before its opaque part. * @example * const immediateLayer = app.scene.layers.getLayerByName('Immediate'); * app.on('update', () => { * outlineRenderer.frameUpdate(cameraEntity, immediateLayer, false); * }); */ frameUpdate(sceneCameraEntity: Entity, blendLayer: Layer, blendLayerTransparent: boolean): void; } import type { AppBase } from '../../framework/app-base.js'; import type { Layer } from "../../scene/layer.js"; import { RenderTarget } from '../../platform/graphics/render-target.js'; import { Entity } from '../../framework/entity.js'; import { BlendState } from '../../platform/graphics/blend-state.js'; import { QuadRender } from '../../scene/graphics/quad-render.js'; import type { MeshInstance } from '../../scene/mesh-instance.js'; import { Color } from '../../core/math/color.js';