UNPKG

@itwin/core-frontend

Version:
256 lines • 10.9 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module WebGL */ import { assert, Id64 } from "@itwin/core-bentley"; import { PackedFeature } from "@itwin/core-common"; import { isFeatureHilited } from "./FeatureOverrides"; import { System } from "./System"; import { TechniqueFlags } from "./TechniqueFlags"; /* eslint-disable no-restricted-syntax */ /** @internal */ export class ShaderProgramParams { _target; _renderPass = 255 /* RenderPass.None */; get target() { assert(undefined !== this._target); return this._target; } get renderPass() { return this._renderPass; } get projectionMatrix() { return this.target.uniforms.getProjectionMatrix32(this.isViewCoords); } bindProjectionMatrix(uniform) { this.target.uniforms.bindProjectionMatrix(uniform, this.isViewCoords); } get isViewCoords() { return 13 /* RenderPass.ViewOverlay */ === this.renderPass || 0 /* RenderPass.Background */ === this.renderPass; } get isOverlayPass() { return 12 /* RenderPass.WorldOverlay */ === this.renderPass || 13 /* RenderPass.ViewOverlay */ === this.renderPass; } get context() { return System.instance.context; } init(target, pass = 5 /* RenderPass.OpaqueGeneral */) { this._renderPass = pass; this._target = target; } } /** @internal */ export class DrawParams { _programParams; _geometry; get geometry() { assert(undefined !== this._geometry); return this._geometry; } get programParams() { assert(undefined !== this._programParams); return this._programParams; } get target() { return this.programParams.target; } get renderPass() { return this.programParams.renderPass; } get projectionMatrix() { return this.programParams.projectionMatrix; } get isViewCoords() { return this.programParams.isViewCoords || this.target.currentBranch.forceViewCoords; } get isOverlayPass() { return this.programParams.isOverlayPass; } get context() { return this.programParams.context; } init(programParams, geometry) { this._programParams = programParams; this._geometry = geometry; } } /** Represents a command to be executed within a RenderPass. The most common command is * to draw a primitive; others involve state changes such as pushing/popping transforms * and symbology overrides, which require that commands be executed in order. * @internal */ export var DrawOpCode; (function (DrawOpCode) { DrawOpCode["Primitive"] = "drawPrimitive"; DrawOpCode["PushBranch"] = "pushBranch"; DrawOpCode["PopBranch"] = "popBranch"; DrawOpCode["PushBatch"] = "pushBatch"; DrawOpCode["PopBatch"] = "popBatch"; DrawOpCode["PushState"] = "pushState"; DrawOpCode["PushClip"] = "pushClip"; DrawOpCode["PopClip"] = "popClip"; })(DrawOpCode || (DrawOpCode = {})); /** @internal */ export class PopBatchCommand { opcode = "popBatch"; constructor() { } static instance = new PopBatchCommand(); execute(exec) { exec.target.popBatch(); } } /** @internal */ export class PushBatchCommand { batch; opcode = "pushBatch"; constructor(batch) { this.batch = batch; } execute(exec) { exec.target.pushBatch(this.batch); } } /** @internal */ export class PushStateCommand { state; opcode = "pushState"; constructor(state) { this.state = state; } execute(exec) { exec.target.pushState(this.state); } } /** @internal */ export class PushBranchCommand { branch; opcode = "pushBranch"; constructor(branch) { this.branch = branch; } execute(exec) { exec.pushBranch(this.branch); } } /** @internal */ export class PopBranchCommand { opcode = "popBranch"; constructor() { } static instance = new PopBranchCommand(); execute(exec) { exec.popBranch(); } } /** @internal */ export class PushClipCommand { clip; opcode = "pushClip"; constructor(clip) { this.clip = clip; } execute(exec) { exec.target.uniforms.branch.clipStack.push(this.clip); } } /** @internal */ export class PopClipCommand { opcode = "popClip"; constructor() { } static instance = new PopClipCommand(); execute(exec) { exec.target.uniforms.branch.clipStack.pop(); } } /** @internal */ export class PrimitiveCommand { primitive; opcode = "drawPrimitive"; constructor(primitive) { this.primitive = primitive; } static _scratchTechniqueFlags = new TechniqueFlags(); execute(exec) { if (exec.target.isGeometryOutsideActiveVolume(this.primitive.cachedGeometry)) return; const techniqueId = this.primitive.techniqueId; if (-1 /* TechniqueId.Invalid */ === techniqueId) return; const target = exec.target; const thematic = this.primitive.cachedGeometry.supportsThematicDisplay && target.wantThematicDisplay; const shadowable = (techniqueId === 0 /* TechniqueId.Surface */ || techniqueId === 7 /* TechniqueId.RealityMesh */) && target.solarShadowMap.isReady && target.currentViewFlags.shadows && !thematic; const isShadowable = shadowable ? 1 /* IsShadowable.Yes */ : 0 /* IsShadowable.No */; let isThematic = thematic ? 1 /* IsThematic.Yes */ : 0 /* IsThematic.No */; const isClassified = (undefined !== target.currentPlanarClassifierOrDrape || undefined !== target.activeVolumeClassifierTexture) ? 1 /* IsClassified.Yes */ : 0 /* IsClassified.No */; const isInstanced = this.primitive.isInstanced ? 1 /* IsInstanced.Yes */ : 0 /* IsInstanced.No */; const isAnimated = this.primitive.hasAnimation ? 1 /* IsAnimated.Yes */ : 0 /* IsAnimated.No */; // Point clouds do not support hillshade or slope mode for thematic display. if (isThematic && (undefined !== this.primitive.cachedGeometry.asPointCloud) && (target.uniforms.thematic.wantSlopeMode || target.uniforms.thematic.wantHillShadeMode)) isThematic = 0 /* IsThematic.No */; const wiremesh = target.currentViewFlags.wiremesh && (techniqueId === 0 /* TechniqueId.Surface */ || techniqueId === 7 /* TechniqueId.RealityMesh */); const isWiremesh = wiremesh ? 1 /* IsWiremesh.Yes */ : 0 /* IsWiremesh.No */; const flags = PrimitiveCommand._scratchTechniqueFlags; const posType = this.primitive.cachedGeometry.usesQuantizedPositions ? "quantized" : "unquantized"; const enableAtmosphere = target.wantAtmosphere ? 1 /* EnableAtmosphere.Yes */ : 0 /* EnableAtmosphere.No */; flags.init(target, exec.renderPass, isInstanced, isAnimated, isClassified, isShadowable, isThematic, isWiremesh, posType, enableAtmosphere); const technique = target.techniques.getTechnique(techniqueId); const program = technique.getShader(flags); if (exec.setProgram(program)) exec.target.compositor.drawPrimitive(this.primitive, exec, program.outputsToPick); } get hasFeatures() { return this.primitive.hasFeatures; } get renderOrder() { return this.primitive.renderOrder; } getPass(target) { return this.primitive.getPass(target); } } /** Extracts the commands for rendering the flashed classifier (if any) from the by-index set of volume classifier commands. * NB: Cmds will be sets of some pushes, a primitive, and then some pops (equal to number of pushes). * The primitive should be right in the middle of a set. We need to find the set which matches the flashID. * @internal */ export function extractFlashedVolumeClassifierCommands(flashedId, cmds, numCmdsPerClassifier) { if (!Id64.isValid(flashedId) || 0 === numCmdsPerClassifier) return undefined; const firstPrim = (numCmdsPerClassifier - 1) / 2; for (let i = firstPrim; i < cmds.length; i += numCmdsPerClassifier) { assert("drawPrimitive" === cmds[i].opcode, "Command list not configured as expected."); const pc = cmds[i]; const surface = pc.primitive.cachedGeometry.asSurface; if (undefined !== surface && undefined !== surface.mesh.uniformFeatureId) { let j = i - 1; while (j >= 0 && "pushBatch" !== cmds[j].opcode) // Find batch for this primitive j--; if (j < 0) continue; const pushBatch = cmds[j]; const elemId = pushBatch.batch.featureTable.findElementId(surface.mesh.uniformFeatureId); if (undefined !== elemId && elemId === flashedId) { return cmds.slice(i - firstPrim, i + firstPrim + 1); } } } return undefined; } const scratchFeature = PackedFeature.create(); /** @internal */ export function extractHilitedVolumeClassifierCommands(hilites, cmds) { // TODO: This could really be done at the time the HiliteClassification render pass commands are being generated // by just not putting the ones which are not hilited into the ClassificationHilite command list. const result = []; let batch; for (const cmd of cmds) { switch (cmd.opcode) { case "popBranch": if (result.length > 0 && "pushBranch" === result[result.length - 1].opcode) { result.pop(); // remove empty push/pop pairs continue; } break; case "popBatch": batch = undefined; if (result.length > 0 && "pushBatch" === result[result.length - 1].opcode) { result.pop(); // remove empty push/pop pairs continue; } break; case "pushBatch": batch = cmd.batch; break; case "drawPrimitive": if (undefined !== batch) { // Skip any primitives that are not hilited. const surface = cmd.primitive.cachedGeometry.asSurface; if (undefined === surface || undefined === surface.mesh.uniformFeatureId) continue; const feature = batch.featureTable.getPackedFeature(surface.mesh.uniformFeatureId, scratchFeature); if (undefined === feature || !isFeatureHilited(feature, hilites, hilites.models.hasId(Id64.fromUint32PairObject(feature.modelId)))) continue; break; } } result.push(cmd); } return result; } //# sourceMappingURL=DrawCommand.js.map