UNPKG

@itwin/core-frontend

Version:
519 lines • 26.4 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Rendering */ Object.defineProperty(exports, "__esModule", { value: true }); exports.RenderSystem = exports.PlanarGridTransparency = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const core_geometry_1 = require("@itwin/core-geometry"); const IModelApp_1 = require("../IModelApp"); const internal_1 = require("../tile/internal"); const ToolAdmin_1 = require("../tools/ToolAdmin"); const ImageUtil_1 = require("../common/ImageUtil"); const PointStringParams_1 = require("../common/internal/render/PointStringParams"); const PolylineParams_1 = require("../common/internal/render/PolylineParams"); const GraphicBranch_1 = require("./GraphicBranch"); const RenderGraphic_1 = require("./RenderGraphic"); const VertexTableBuilder_1 = require("../common/internal/render/VertexTableBuilder"); const Symbols_1 = require("../common/internal/Symbols"); // cSpell:ignore deserializing subcat uninstanced wiremesh qorigin trimesh /** Default implementation of RenderGraphicOwner. */ class GraphicOwner extends RenderGraphic_1.RenderGraphicOwner { _graphic; constructor(_graphic) { super(); this._graphic = _graphic; } get graphic() { return this._graphic; } } /** Transparency settings for planar grid display. * @alpha */ class PlanarGridTransparency { /** Transparency for the grid plane. This should generally be fairly high to avoid obscuring other geometry */ planeTransparency = .9; /** Transparency of the grid lines. This should be higher than the plane, but less than reference line transparency */ lineTransparency = .75; /** Transparency of the reference lines. This should be less than plane or line transparency so that reference lines are more prominent */ refTransparency = .5; } exports.PlanarGridTransparency = PlanarGridTransparency; /** A RenderSystem provides access to resources used by the internal WebGL-based rendering system. * An application rarely interacts directly with the RenderSystem; instead it interacts with types like [[Viewport]] which * coordinate with the RenderSystem on the application's behalf. * @see [Display system overview]($docs/learning/display/index.md) * @see [[IModelApp.renderSystem]]. * @public * @extensions */ class RenderSystem { /** Options used to initialize the RenderSystem. These are primarily used for feature-gating. * This object is frozen and cannot be modified after the RenderSystem is created. * @internal */ options; /** Antialias samples to use on all subsequently created render targets. * Default value: undefined (no antialiasing) * @beta */ antialiasSamples; /** Initialize the RenderSystem with the specified options. * @note The RenderSystem takes ownership of the supplied Options and freezes it. * @internal */ constructor(options) { this.options = undefined !== options ? options : {}; Object.freeze(this.options); if (undefined !== this.options.disabledExtensions) Object.freeze(this.options.disabledExtensions); } [Symbol.dispose]() { this.dispose(); // eslint-disable-line @typescript-eslint/no-deprecated } /** The maximum permitted width or height of a texture supported by this render system. */ get maxTextureSize() { return 0; } /** @internal */ get supportsCreateImageBitmap() { return false; } /** @internal */ get dpiAwareLOD() { return true === this.options.dpiAwareLOD; } /** @internal */ get isMobile() { return false; } /** Find a previously-created [RenderMaterial]($common) by its ID. * @param _key The unique ID of the material within the context of the IModelConnection. Typically an element ID. * @param _imodel The IModelConnection with which the material is associated. * @returns A previously-created material matching the specified ID, or undefined if no such material exists. */ findMaterial(_key, _imodel) { return undefined; } /** Create a [RenderMaterial]($common). * @see [[CreateRenderMaterialArgs]] for a description of the material parameters. */ createRenderMaterial(_args) { return undefined; } /** Creates a [[GraphicBuilder]] for creating a [[RenderGraphic]]. * @param placement The local-to-world transform in which the builder's geometry is to be defined. * @param type The type of builder to create. * @param viewport The viewport in which the resultant [[RenderGraphic]] will be rendered. * @param pickableId If the decoration is to be pickable, a unique identifier to associate with the resultant [[RenderGraphic]]. * @returns A builder for creating a [[RenderGraphic]] of the specified type appropriate for rendering within the specified viewport. * @see [[IModelConnection.transientIds]] for obtaining an ID for a pickable decoration. * @see [[RenderContext.createGraphicBuilder]]. * @see [[Decorator]] */ createGraphicBuilder(placement, type, viewport, pickableId) { const pickable = undefined !== pickableId ? { id: pickableId } : undefined; return this.createGraphic({ type, viewport, placement, pickable }); } /** Obtain an object capable of producing a custom screen-space effect to be applied to the image rendered by a [[Viewport]]. * @returns undefined if screen-space effects are not supported by this RenderSystem. */ createScreenSpaceEffectBuilder(_params) { return undefined; } /** @internal */ createTriMesh(args, instances) { const params = (0, VertexTableBuilder_1.createMeshParams)(args, this.maxTextureSize, IModelApp_1.IModelApp.tileAdmin.edgeOptions.type !== "non-indexed"); return this.createMesh(params, instances); } /** @internal */ createMeshGraphics(mesh, instances) { const meshArgs = mesh.toMeshArgs(); if (meshArgs) { return this.createTriMesh(meshArgs, instances); } const polylineArgs = mesh.toPolylineArgs(); return polylineArgs ? this.createIndexedPolylines(polylineArgs, instances) : undefined; } /** @internal */ createGeometryFromMesh(mesh, viOrigin, tileData) { const meshArgs = mesh.toMeshArgs(); if (meshArgs) { const meshParams = (0, VertexTableBuilder_1.createMeshParams)(meshArgs, this.maxTextureSize, IModelApp_1.IModelApp.tileAdmin.edgeOptions.type !== "non-indexed"); meshParams.tileData = tileData; return this.createMeshGeometry(meshParams, viOrigin); } const plArgs = mesh.toPolylineArgs(); if (!plArgs) { return undefined; } if (plArgs.flags.isDisjoint) { const psParams = (0, PointStringParams_1.createPointStringParams)(plArgs, this.maxTextureSize); return psParams ? this.createPointStringGeometry(psParams, viOrigin) : undefined; } const plParams = (0, PolylineParams_1.createPolylineParams)(plArgs, this.maxTextureSize); return plParams ? this.createPolylineGeometry(plParams, viOrigin) : undefined; } /** @internal */ createIndexedPolylines(args, instances) { if (args.flags.isDisjoint) { const pointStringParams = (0, PointStringParams_1.createPointStringParams)(args, this.maxTextureSize); return undefined !== pointStringParams ? this.createPointString(pointStringParams, instances) : undefined; } else { const polylineParams = (0, PolylineParams_1.createPolylineParams)(args, this.maxTextureSize); return undefined !== polylineParams ? this.createPolyline(polylineParams, instances) : undefined; } } /** @internal */ createMeshGeometry(_params, _viewIndependentOrigin) { return undefined; } /** @internal */ createPolylineGeometry(_params, _viewIndependentOrigin) { return undefined; } /** @internal */ createPointStringGeometry(_params, _viewIndependentOrigin) { return undefined; } /** @internal */ createPointCloudGeometry(_args) { return undefined; } /** @internal */ createRealityMeshGeometry(_params, _disableTextureDisposal = false) { return undefined; } /** @internal */ createAreaPattern(_params) { return undefined; } /** Create a [[RenderInstances]] from a [[RenderInstancesParams]], to be supplied to [[createGraphicFromTemplate]] via [[CreateGraphicFromTempalateArgs.instances]]. * @beta */ createRenderInstances(_params) { return undefined; } createGraphicFromGeometry(createGeometry, instancesOrOrigin) { let viOrigin; let instances; if (instancesOrOrigin instanceof core_geometry_1.Point3d) viOrigin = instancesOrOrigin; else instances = instancesOrOrigin; const geom = createGeometry(viOrigin); return geom ? this.createRenderGraphic(geom, instances) : undefined; } /** @internal */ createMesh(params, instances) { return this.createGraphicFromGeometry((viOrigin) => this.createMeshGeometry(params, viOrigin), instances); } /** @internal */ createPolyline(params, instances) { return this.createGraphicFromGeometry((origin) => this.createPolylineGeometry(params, origin), instances); } /** @internal */ createPointString(params, instances) { return this.createGraphicFromGeometry((origin) => this.createPointStringGeometry(params, origin), instances); } /** @internal */ createTerrainMesh(_params, _transform, _disableTextureDisposal = false) { return undefined; } /** @internal */ createRealityMeshGraphic(_params, _disableTextureDisposal = false) { return undefined; } /** @internal */ createRealityMesh(realityMesh, disableTextureDisposal = false) { const geom = this.createRealityMeshGeometry(realityMesh, disableTextureDisposal); return geom ? this.createRenderGraphic(geom) : undefined; } /** @internal */ get maxRealityImageryLayers() { return 0; } /** @internal */ createPointCloud(args, _imodel) { const geom = this.createPointCloudGeometry(args); return geom ? this.createRenderGraphic(geom) : undefined; } /** Create a clip volume to clip geometry. * @note The clip volume takes ownership of the ClipVector, which must not be subsequently mutated. * @param _clipVector Defines how the volume clips geometry. * @returns A clip volume, or undefined if, e.g., the clip vector does not clip anything. */ createClipVolume(_clipVector) { return undefined; } /** @internal */ createPlanarGrid(_frustum, _grid) { return undefined; } /** @internal */ createBackgroundMapDrape(_drapedTree, _mapTree) { return undefined; } /** @internal */ createTile(tileTexture, corners, featureIndex) { // corners // [0] [1] // [2] [3] // Quantize the points according to their range const points = new core_common_1.QPoint3dList(core_common_1.QParams3d.fromRange(core_geometry_1.Range3d.create(...corners))); for (let i = 0; i < 4; i++) points.add(corners[i]); // Now remove the translation from the quantized points and put it into a transform instead. // This prevents graphical artifacts when quantization origin is large relative to quantization scale. // ###TODO: Would be better not to create a branch for every tile. const qorigin = points.params.origin; const transform = core_geometry_1.Transform.createTranslationXYZ(qorigin.x, qorigin.y, qorigin.z); qorigin.setZero(); const features = new core_common_1.FeatureIndex(); if (undefined !== featureIndex) { features.featureID = featureIndex; features.type = core_common_1.FeatureIndexType.Uniform; } const rasterTile = { points, vertIndices: [0, 1, 2, 2, 1, 3], isPlanar: true, features, colors: new core_common_1.ColorIndex(), fillFlags: core_common_1.FillFlags.None, textureMapping: { uvParams: [new core_geometry_1.Point2d(0, 0), new core_geometry_1.Point2d(1, 0), new core_geometry_1.Point2d(0, 1), new core_geometry_1.Point2d(1, 1)], texture: tileTexture, }, }; const trimesh = this.createTriMesh(rasterTile); if (undefined === trimesh) return undefined; const branch = new GraphicBranch_1.GraphicBranch(true); branch.add(trimesh); return this.createBranch(branch, transform); } /** Create a Graphic for a [[SkyBox]] which encompasses the entire scene, rotating with the camera. * @internal */ createSkyBox(_params) { return undefined; } /** Create a RenderGraphic consisting of a list of Graphics, with optional transform and symbology overrides applied to the list */ createBranch(branch, transform, options) { return this.createGraphicBranch(branch, transform, options); } /** Create a node in the scene graph corresponding to a transform node in the scene's schedule script. * Nodes under this branch will only be drawn if they belong to the specified transform node. * This allows the graphics in a single Tile to be efficiently drawn with different transforms applied by different nodes. * The node Id is either the Id of a single transform node in the script, of 0xffffffff to indicate all nodes that have no transform applied to them. * @internal */ createAnimationTransformNode(graphic, _nodeId) { return graphic; } /** Return a Promise which when resolved indicates that all pending external textures have finished loading from the backend. */ async waitForAllExternalTextures() { return Promise.resolve(); } /** @internal */ get hasExternalTextureRequests() { return false; } /** Create a graphic that assumes ownership of another graphic. * @param ownedGraphic The RenderGraphic to be owned. * @returns The owning graphic that exposes a `disposeGraphic` method for explicitly disposing of the owned graphic. * @see [[RenderGraphicOwner]] for details regarding ownership semantics. * @public */ createGraphicOwner(ownedGraphic) { return new GraphicOwner(ownedGraphic); } /** Create a "layer" containing the graphics belonging to it. A layer has a unique identifier and all of its geometry lies in an XY plane. * Different layers can be drawn coincident with one another; their draw order can be controlled by a per-layer priority value so that one layer draws * on top of another. Layers cannot nest inside other layers. Multiple GraphicLayers can exist with the same ID; they are treated as belonging to the same layer. * A GraphicLayer must be contained (perhaps indirectly) inside a GraphicLayerContainer. * @see [[createGraphicLayerContainer]] * @internal */ createGraphicLayer(graphic, _layerId) { return graphic; } /** Create a graphic that can contain [[GraphicLayer]]s. * @internal */ createGraphicLayerContainer(graphic, _drawAsOverlay, _transparency, _elevation) { return graphic; } /** Find a previously-created [[RenderTexture]] by its key. * @param _key The unique key of the texture within the context of the IModelConnection. Typically an element Id. * @param _imodel The IModelConnection with which the texture is associated. * @returns A previously-created texture matching the specified key, or undefined if no such texture exists. */ findTexture(_key, _imodel) { return undefined; } /** Find or create a [[RenderTexture]] from a persistent texture element. * @param id The ID of the texture element. * @param iModel The IModel containing the texture element. * @returns A Promise resolving to the created RenderTexture or to undefined if the texture could not be created. * @note If the texture is successfully created, it will be cached on the IModelConnection such that it can later be retrieved by its ID using [[RenderSystem.findTexture]]. * @see [[RenderSystem.loadTextureImage]]. */ async loadTexture(id, iModel) { let texture = this.findTexture(id.toString(), iModel); if (undefined === texture) { const image = await this.loadTextureImage(id, iModel); if (undefined !== image) { // This will return a pre-existing RenderTexture if somebody else loaded it while we were awaiting the image. texture = this.createTexture({ type: core_common_1.RenderTexture.Type.Normal, ownership: { key: id, iModel }, image: { source: image.image, transparency: core_common_1.ImageSourceFormat.Png === image.format ? core_common_1.TextureTransparency.Mixed : core_common_1.TextureTransparency.Opaque, }, }); } } return texture; } /** * Load a texture image given the ID of a texture element. * @param id The ID of the texture element. * @param iModel The IModel containing the texture element. * @returns A Promise resolving to a TextureImage created from the texture element's data, or to undefined if the TextureImage could not be created. * @see [[RenderSystem.loadTexture]] * @internal */ async loadTextureImage(id, iModel) { const elemProps = await iModel.elements.getProps(id); if (1 !== elemProps.length) return undefined; const textureProps = elemProps[0]; if (undefined === textureProps.data || "string" !== typeof (textureProps.data) || undefined === textureProps.format || "number" !== typeof (textureProps.format)) return undefined; const format = textureProps.format; if (!(0, core_common_1.isValidImageSourceFormat)(format)) return undefined; const imageSource = new core_common_1.ImageSource((0, core_bentley_1.base64StringToUint8Array)(textureProps.data), format); const image = await (0, ImageUtil_1.imageElementFromImageSource)(imageSource); return { image, format }; } /** Obtain a texture created from a gradient. * @param _symb The description of the gradient. * @param _imodel The IModelConnection with which the texture is associated. * @returns A texture created from the gradient image, or undefined if the texture could not be created. * @note If a texture matching the specified gradient is already cached on the iModel, it will be returned. * Otherwise, if an iModel is supplied, the newly-created texture will be cached on the iModel such that subsequent calls with an equivalent gradient and the * same iModel will return the cached texture instead of creating a new one. */ getGradientTexture(_symb, _imodel) { return undefined; } /** Create a texture from an ImageSource. */ async createTextureFromSource(args) { try { // JPEGs don't support transparency. const transparency = core_common_1.ImageSourceFormat.Jpeg === args.source.format ? core_common_1.TextureTransparency.Opaque : (args.transparency ?? core_common_1.TextureTransparency.Mixed); const image = await (0, ImageUtil_1.imageElementFromImageSource)(args.source); if (!IModelApp_1.IModelApp.hasRenderSystem) return undefined; return this.createTexture({ type: args.type, ownership: args.ownership, image: { source: image, transparency, }, }); } catch { return undefined; } } /** Create a new texture by its element ID. This texture will be retrieved asynchronously from the backend. A placeholder image will be associated with the texture until the requested image data loads. */ createTextureFromElement(_id, _imodel, _params, _format) { return undefined; } createTexture(_args) { return undefined; } /** Create a new texture from a cube of HTML images. * @internal */ createTextureFromCubeImages(_posX, _negX, _posY, _negY, _posZ, _negZ, _imodel, _params) { return undefined; } /** @internal */ onInitialized() { } /** @internal */ get supportsLogZBuffer() { return false !== this.options.logarithmicDepthBuffer; } /** Obtain an object that can be used to control various debugging features. Returns `undefined` if debugging features are unavailable for this `RenderSystem`. * @internal */ get debugControl() { return undefined; } /** @internal */ collectStatistics(_stats) { } /** A function that is invoked after the WebGL context is lost. Context loss is almost always caused by excessive consumption of GPU memory. * After context loss occurs, the RenderSystem will be unable to interact with WebGL by rendering viewports, creating graphics and textures, etc. * By default, this function invokes [[ToolAdmin.exceptionHandler]] with a brief message describing what occurred. * An application can override this behavior as follows: * ```ts * RenderSystem.contextLossHandler = (): Promise<any> => { * // your implementation here. * } * ``` * @note Context loss is reported by the browser some short time *after* it has occurred. It is not possible to determine the specific cause. * @see [[TileAdmin.gpuMemoryLimit]] to limit the amount of GPU memory consumed thereby reducing the likelihood of context loss. * @see [[TileAdmin.totalTileContentBytes]] for the amount of GPU memory allocated for tile graphics. */ static async contextLossHandler() { const msg = IModelApp_1.IModelApp.localization.getLocalizedString("iModelJs:Errors.WebGLContextLost"); return ToolAdmin_1.ToolAdmin.exceptionHandler(msg); } /** Convert a [[GraphicDescription]] produced by a [[GraphicDescriptionBuilder]] into a [[RenderGraphic]]. * @beta */ createGraphicFromDescription(args) { return (0, internal_1.createGraphicFromDescription)(args.description, args.context, this); } /** Convert a [[GraphicDescription]] produced by a [[GraphicDescriptionBuilder]] into a [[GraphicTemplate]]. * @beta */ createTemplateFromDescription(args) { return (0, internal_1.createGraphicTemplateFromDescription)(args.description, args.context, this); } /** Obtain the JSON representation of a [[WorkerGraphicDescriptionContext]] for the specified `iModel` that can be forwarded to a Worker for use with a [[GraphicDescriptionBuilder]]. * @beta */ createWorkerGraphicDescriptionContextProps(iModel) { const props = { [Symbols_1._implementationProhibited]: undefined, transientIds: iModel.transientIds.fork(), constraints: { [Symbols_1._implementationProhibited]: undefined, maxTextureSize: this.maxTextureSize, }, }; return props; } /** Synchronize changes made to a [[WorkerGraphicDescriptionContext]] on a Worker with the state of the `iModel` from which it was created. * @beta */ async resolveGraphicDescriptionContext(props, iModel) { const impl = props; if (typeof impl.transientIds !== "object" || !Array.isArray(impl.textures)) { throw new Error("Invalid GraphicDescriptionContextProps"); } if (impl.resolved) { throw new Error("resolveGraphicDescriptionContext can only be called once for a given GraphicDescriptionContextProps"); } const textures = new Map(); await Promise.allSettled(impl.textures.map(async (tex, i) => { let texture; if (tex.source.gradient) { texture = this.getGradientTexture(core_common_1.Gradient.Symb.fromJSON(tex.source.gradient)); } else if (tex.source.imageSource) { texture = await this.createTextureFromSource({ source: new core_common_1.ImageSource(tex.source.imageSource, tex.source.format), type: tex.type, transparency: tex.transparency, }); } else if (tex.source.imageBuffer) { texture = this.createTexture({ type: tex.type, image: { source: core_common_1.ImageBuffer.create(tex.source.imageBuffer, tex.source.format, tex.source.width), transparency: tex.transparency, }, }); } else if (tex.source.url) { const image = await (0, ImageUtil_1.tryImageElementFromUrl)(tex.source.url); if (image) { texture = this.createTexture({ type: tex.type, image: { source: image, transparency: tex.transparency, }, }); } } if (texture) { textures.set(i.toString(10), texture); } })); const remap = iModel.transientIds.merge(impl.transientIds); impl.resolved = true; return { [Symbols_1._implementationProhibited]: undefined, remapTransientLocalId: (source) => remap(source), [Symbols_1._textures]: textures, }; } } exports.RenderSystem = RenderSystem; //# sourceMappingURL=RenderSystem.js.map