@itwin/core-frontend
Version:
iTwin.js frontend components
416 lines • 19 kB
JavaScript
"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.SceneContext = exports.DecorateContext = exports.DynamicsContext = exports.RenderContext = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const DecorationsCache_1 = require("./DecorationsCache");
const IModelApp_1 = require("./IModelApp");
const Scene_1 = require("./render/Scene");
const internal_1 = require("./tile/internal");
const Viewport_1 = require("./Viewport");
const GraphicType_1 = require("./common/render/GraphicType");
/** Provides context for producing [[RenderGraphic]]s for drawing within a [[Viewport]].
* @public
* @extensions
*/
class RenderContext {
/** ViewFlags extracted from the context's [[Viewport]]. */
viewFlags;
_viewport;
/** Frustum extracted from the context's [[Viewport]]. */
frustum;
/** Frustum planes extracted from the context's [[Viewport]]. */
frustumPlanes;
constructor(vp, frustum) {
this._viewport = vp;
this.viewFlags = vp.viewFlags;
this.frustum = frustum ? frustum : vp.getFrustum();
this.frustumPlanes = core_common_1.FrustumPlanes.fromFrustum(this.frustum);
}
/** Given a point in world coordinates, determine approximately how many pixels it occupies on screen based on this context's frustum. */
getPixelSizeAtPoint(inPoint) {
return this.viewport.viewingSpace.getPixelSizeAtPoint(inPoint);
}
/** The [[Viewport]] associated with this context. */
get viewport() {
return this._viewport;
}
/** The [[RenderSystem]] being used to produce graphics for this context. */
get renderSystem() {
return this.target.renderSystem;
}
/** @internal */
get target() { return this.viewport.target; }
/** @internal */
_createGraphicBuilder(options) {
return this.target.createGraphicBuilder({ ...options, viewport: this.viewport });
}
/** Create a builder for creating a [[GraphicType.Scene]] [[RenderGraphic]] for rendering within this context's [[Viewport]].
* @param transform the local-to-world transform in which the builder's geometry is to be defined.
* @returns A builder for creating a [[GraphicType.Scene]] [[RenderGraphic]] for rendering within this context's [[Viewport]].
*/
createSceneGraphicBuilder(transform) {
return this._createGraphicBuilder({ type: GraphicType_1.GraphicType.Scene, placement: transform });
}
/** Create a graphic from a [[GraphicBranch]]. */
createGraphicBranch(branch, location, opts) {
return this.target.renderSystem.createGraphicBranch(branch, location, opts);
}
/** Create a [[RenderGraphic]] which groups a set of graphics into a node in a scene graph, applying to each a transform and optional clip volume and symbology overrides.
* @param branch Contains the group of graphics and the symbology overrides.
* @param location the local-to-world transform applied to the grouped graphics.
* @returns A RenderGraphic suitable for drawing the scene graph node within this context's [[Viewport]].
* @see [[RenderSystem.createBranch]]
*/
createBranch(branch, location) { return this.createGraphicBranch(branch, location); }
/** Given the size of a logical pixel in meters, convert it to the size of a physical pixel in meters, if [[RenderSystem.dpiAwareLOD]] is `true`.
* Used when computing LOD for graphics.
* @internal
*/
adjustPixelSizeForLOD(cssPixelSize) {
return this.viewport.target.adjustPixelSizeForLOD(cssPixelSize);
}
}
exports.RenderContext = RenderContext;
/** Provides context for an [[InteractiveTool]] to display decorations representing its current state.
* @see [[InteractiveTool.onDynamicFrame]]
* @public
*/
class DynamicsContext extends RenderContext {
_foreground;
_overlay;
/** Add a graphic to the list of dynamic graphics to be drawn in this context's [[Viewport]].
* These graphics are drawn as [[GraphicType.Scene]].
* @see [[addOverlay]] to add a graphic to be drawn as an overlay instead.
*/
addGraphic(graphic) {
this.add(graphic, false);
}
/** Add a graphic to the list of dynamic graphics to be drawn in this context's [[Viewport]].
* These graphics are drawn as part of the viewport's scene as described by [[GraphicType.Scene]], except
* that they always draw on top of other graphics as with [[GraphicType.WorldOverlay]].
* @see [[addGraphic]] to add an ordinary scene graphic instead.
*/
addOverlay(graphic) {
this.add(graphic, true);
}
/** @internal */
add(graphic, isOverlay) {
const key = isOverlay ? "_overlay" : "_foreground";
const list = this[key] ?? (this[key] = []);
list.push(graphic);
}
/** @internal */
changeDynamics() {
this.viewport.changeDynamics(this._foreground, this._overlay);
}
/** Create a builder for producing a [[RenderGraphic]] appropriate for rendering within this context's [[Viewport]].
* @param options Options describing how to create the builder.
* @returns A builder that produces a [[RenderGraphic]].
*/
createGraphic(options) {
return this._createGraphicBuilder(options);
}
}
exports.DynamicsContext = DynamicsContext;
/** Provides context for a [[ViewportDecorator]] to add [[Decorations]] to be rendered within a [[Viewport]].
* @public
* @extensions
*/
class DecorateContext extends RenderContext {
_decorations;
_cache;
_curCacheableDecorator;
/** The [[ScreenViewport]] in which this context's [[Decorations]] will be drawn. */
get viewport() {
return super.viewport;
}
/** @internal */
constructor(vp, decorations, cache) {
super(vp);
this._decorations = decorations;
this._cache = cache;
}
/** Create a new DecorateContext.
* @param args Describes the inputs to the context.
* @note Typically the [[ScreenViewport]] takes care of creating the context for you.
* @public
*/
static create(args) {
return new DecorateContext(args.viewport, args.output, args.cache ?? new DecorationsCache_1.DecorationsCache());
}
/** Create a builder for creating a [[RenderGraphic]] of the specified type appropriate for rendering within this context's [[Viewport]].
* @param type The type of builder to create.
* @param transform the local-to-world transform in which the builder's geometry is to be defined.
* @param id 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 this context's [[Viewport]].
* @see [[IModelConnection.transientIds]] for obtaining an ID for a pickable decoration.
* @see [[createGraphic]] for more options.
*/
createGraphicBuilder(type, transform, id) {
return this.createGraphic({ type, placement: transform, pickable: undefined !== id ? { id } : undefined });
}
/** Create a builder for producing a [[RenderGraphic]] appropriate for rendering within this context's [[Viewport]].
* @param options Options describing how to create the builder.
* @returns A builder that produces a [[RenderGraphic]].
*/
createGraphic(options) {
return this._createGraphicBuilder(options);
}
/** @internal */
addFromDecorator(decorator) {
(0, core_bentley_1.assert)(undefined === this._curCacheableDecorator);
try {
if (decorator.useCachedDecorations) {
const cached = this._cache.get(decorator);
if (cached) {
this.restoreCache(cached);
return;
}
this._curCacheableDecorator = decorator;
}
decorator.decorate(this);
}
finally {
this._curCacheableDecorator = undefined;
}
}
/** Restores decorations onto this context from the specified array of cached decorations. */
restoreCache(cachedDecorations) {
cachedDecorations.forEach((cachedDecoration) => {
switch (cachedDecoration.type) {
case "graphic":
this.addDecoration(cachedDecoration.graphicType, cachedDecoration.graphicOwner);
break;
case "canvas":
this.addCanvasDecoration(cachedDecoration.canvasDecoration, cachedDecoration.atFront);
break;
case "html":
this.addHtmlDecoration(cachedDecoration.htmlElement);
break;
}
});
}
_appendToCache(decoration) {
(0, core_bentley_1.assert)(undefined !== this._curCacheableDecorator);
this._cache.add(this._curCacheableDecorator, decoration);
}
/** Calls [[GraphicBuilder.finish]] on the supplied builder to obtain a [[RenderGraphic]], then adds the graphic to the appropriate list of
* [[Decorations]].
* @param builder The builder from which to extract the graphic.
* @note The builder should not be used after calling this method.
*/
addDecorationFromBuilder(builder) {
this.addDecoration(builder.type, builder.finish());
}
/** Adds a graphic to the set of [[Decorations]] to be drawn in this context's [[ScreenViewport]].
* @param The type of the graphic, which determines to which list of decorations it is added.
* @param decoration The decoration graphic to add.
* @note The type must match the type with which the [[RenderGraphic]]'s [[GraphicBuilder]] was constructed.
* @see [[DecorateContext.addDecorationFromBuilder]] for a more convenient API.
*/
addDecoration(type, decoration) {
if (this._curCacheableDecorator) {
const graphicOwner = this.target.renderSystem.createGraphicOwner(decoration);
this._appendToCache({ type: "graphic", graphicOwner, graphicType: type });
decoration = graphicOwner;
}
switch (type) {
case GraphicType_1.GraphicType.Scene:
if (undefined === this._decorations.normal)
this._decorations.normal = [];
this._decorations.normal.push(decoration);
break;
case GraphicType_1.GraphicType.WorldDecoration:
if (!this._decorations.world)
this._decorations.world = [];
this._decorations.world.push(decoration);
break;
case GraphicType_1.GraphicType.WorldOverlay:
if (!this._decorations.worldOverlay)
this._decorations.worldOverlay = [];
this._decorations.worldOverlay.push(decoration);
break;
case GraphicType_1.GraphicType.ViewOverlay:
if (!this._decorations.viewOverlay)
this._decorations.viewOverlay = [];
this._decorations.viewOverlay.push(decoration);
break;
case GraphicType_1.GraphicType.ViewBackground:
this.setViewBackground(decoration);
break;
}
}
/** Add a [[CanvasDecoration]] to be drawn in this context's [[ScreenViewport]]. */
addCanvasDecoration(decoration, atFront = false) {
if (this._curCacheableDecorator)
this._appendToCache({ type: "canvas", canvasDecoration: decoration, atFront });
if (undefined === this._decorations.canvasDecorations)
this._decorations.canvasDecorations = [];
const list = this._decorations.canvasDecorations;
if (0 === list.length || true === atFront)
list.push(decoration);
else
list.unshift(decoration);
}
/** Add an HTMLElement to be drawn as a decoration in this context's [[ScreenViewport]]. */
addHtmlDecoration(decoration) {
if (this._curCacheableDecorator)
this._appendToCache({ type: "html", htmlElement: decoration });
// an element decoration being added might already be on the decorationDiv, just marked for removal
if (decoration[Viewport_1.ELEMENT_MARKED_FOR_REMOVAL]) {
decoration[Viewport_1.ELEMENT_MARKED_FOR_REMOVAL] = false;
}
else if (decoration.parentElement !== this.viewport.decorationDiv) {
this.viewport.decorationDiv.appendChild(decoration);
}
}
/** @internal */
drawStandardGrid(gridOrigin, rMatrix, spacing, gridsPerRef, _isoGrid = false, _fixedRepetitions) {
const vp = this.viewport;
if (vp.viewingGlobe)
return;
const color = vp.getContrastToBackgroundColor();
const planarGrid = this.viewport.target.renderSystem.createPlanarGrid(vp.getFrustum(), { origin: gridOrigin, rMatrix, spacing, gridsPerRef, color });
if (planarGrid) {
this.addDecoration(GraphicType_1.GraphicType.WorldDecoration, planarGrid);
}
}
/** Display skyBox graphic that encompasses entire scene and rotates with camera.
* @see [[RenderSystem.createSkyBox]].
*/
setSkyBox(graphic) {
this._decorations.skyBox = graphic;
}
/** Set the graphic to be displayed behind all other geometry as the background of this context's [[ScreenViewport]]. */
setViewBackground(graphic) {
this._decorations.viewBackground = graphic;
}
}
exports.DecorateContext = DecorateContext;
/** Context used to create the scene to be drawn in a [[Viewport]]. The scene consists of a set of [[RenderGraphic]]s produced by the
* [[TileTree]]s visible within the viewport. Creating the scene may result in the enqueueing of requests for [[Tile]] content which
* should be displayed in the viewport but are not yet loaded.
* @public
*/
class SceneContext extends RenderContext {
_missingChildTiles = false;
/** The graphics comprising the scene. */
scene = new Scene_1.Scene();
/** @internal */
missingTiles = new Set();
/** @internal */
markChildrenLoading() {
this._missingChildTiles = true;
}
/** @internal */
get hasMissingTiles() {
return this._missingChildTiles || this.missingTiles.size > 0;
}
_viewingSpace;
_graphicType = internal_1.TileGraphicType.Scene;
constructor(vp, frustum) {
super(vp, frustum);
}
/** The viewed volume containing the scene. */
get viewingSpace() {
return undefined !== this._viewingSpace ? this._viewingSpace : this.viewport.viewingSpace;
}
/** @internal */
get graphicType() { return this._graphicType; }
/** Add the specified graphic to the scene. */
outputGraphic(graphic) {
switch (this._graphicType) {
case internal_1.TileGraphicType.BackgroundMap:
this.backgroundGraphics.push(graphic);
break;
case internal_1.TileGraphicType.Overlay:
this.overlayGraphics.push(graphic);
break;
default:
this.graphics.push(graphic);
break;
}
}
/** Indicate that the specified tile is desired for the scene but is not yet ready. A request to load its contents will later be enqueued. */
insertMissingTile(tile) {
switch (tile.loadStatus) {
case internal_1.TileLoadStatus.NotLoaded:
case internal_1.TileLoadStatus.Queued:
case internal_1.TileLoadStatus.Loading:
this.missingTiles.add(tile);
break;
}
}
/** @internal */
requestMissingTiles() {
IModelApp_1.IModelApp.tileAdmin.requestTiles(this.viewport, this.missingTiles);
}
/** @internal */
addPlanarClassifier(classifiedModelId, classifierTree, planarClipMask) {
// Target may have the classifier from a previous frame; if not we must create one.
let classifier = this.viewport.target.getPlanarClassifier(classifiedModelId);
if (undefined === classifier)
classifier = this.viewport.target.createPlanarClassifier(classifierTree?.activeClassifier);
// Either way, we need to collect the graphics to draw for this frame, and record that we did so.
if (undefined !== classifier) {
this.planarClassifiers.set(classifiedModelId, classifier);
classifier.setSource(classifierTree, planarClipMask);
}
return classifier;
}
/** @internal */
getPlanarClassifierForModel(modelId) {
return this.planarClassifiers.get(modelId);
}
/** @internal */
addBackgroundDrapedModel(drapedTreeRef, _heightRange) {
const drapedTree = drapedTreeRef.treeOwner.tileTree;
if (undefined === drapedTree)
return undefined;
const id = drapedTree.modelId;
let drape = this.getTextureDrapeForModel(id);
if (undefined !== drape)
return drape;
drape = this.viewport.target.getTextureDrape(id);
if (undefined === drape && this.viewport.backgroundDrapeMap)
drape = this.viewport.target.renderSystem.createBackgroundMapDrape(drapedTreeRef, this.viewport.backgroundDrapeMap);
if (undefined !== drape)
this.textureDrapes.set(id, drape);
return drape;
}
/** @internal */
getTextureDrapeForModel(modelId) {
return this.textureDrapes.get(modelId);
}
/** @internal */
withGraphicType(type, func) {
const prevType = this._graphicType;
this._graphicType = type;
func();
this._graphicType = prevType;
}
/** The graphics in the scene that will be drawn with depth. */
get graphics() { return this.scene.foreground; }
/** The graphics that will be drawn behind everything else in the scene. */
get backgroundGraphics() { return this.scene.background; }
/** The graphics that will be drawn in front of everything else in the scene. */
get overlayGraphics() { return this.scene.overlay; }
/** @internal */
get planarClassifiers() { return this.scene.planarClassifiers; }
/** @internal */
get textureDrapes() { return this.scene.textureDrapes; }
/** @internal */
setVolumeClassifier(classifier, modelId) {
this.scene.volumeClassifier = { classifier, modelId };
}
}
exports.SceneContext = SceneContext;
//# sourceMappingURL=ViewContext.js.map