UNPKG

@itwin/core-frontend

Version:
393 lines (374 loc) • 19.6 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. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); exports.volClassOpaqueColor = void 0; exports.addColorPlanarClassifier = addColorPlanarClassifier; exports.addFeaturePlanarClassifier = addFeaturePlanarClassifier; exports.addHilitePlanarClassifier = addHilitePlanarClassifier; exports.addOverrideClassifierColor = addOverrideClassifierColor; /** @packageDocumentation * @module WebGL */ const core_bentley_1 = require("@itwin/core-bentley"); const core_geometry_1 = require("@itwin/core-geometry"); const core_common_1 = require("@itwin/core-common"); const Matrix_1 = require("../Matrix"); const PlanarClassifier_1 = require("../PlanarClassifier"); const RenderFlags_1 = require("../RenderFlags"); const Texture_1 = require("../Texture"); const Common_1 = require("./Common"); const FeatureSymbology_1 = require("./FeatureSymbology"); const Fragment_1 = require("./Fragment"); const Vertex_1 = require("./Vertex"); exports.volClassOpaqueColor = ` vec4 volClassColor(vec4 baseColor, float depth) { if (depth <= TEXTURE(s_pClassSampler, windowCoordsToTexCoords(gl_FragCoord.xy)).r) discard; return vec4(baseColor.rgb, 1.0); } `; const volClassTranslucentColor = ` vec4 volClassColor(vec4 baseColor, float depth) { return vec4(baseColor.rgb, depth); // This will never be called, so we use depth here to avoid a compile error } `; const applyPlanarClassificationPrelude = ` const float dimScale = .7; vec2 classPos = v_pClassPos.xy / v_pClassPosW; bool isOutside = classPos.x < 0.0 || classPos.x > 1.0 || classPos.y < 0.0 || classPos.y > 1.0; if (u_pClassColorParams.x > kClassifierDisplay_Element) { // texture/terrain drape. if (u_pClassColorParams.x > kTextureDrape) { return volClassColor(baseColor, depth); } if (isOutside) discard; vec3 rgb = TEXTURE(s_pClassSampler, classPos.xy).rgb; return vec4(rgb, baseColor.a); } float imageCount = u_pClassColorParams.z; // If imageCount is less than zero - the mask sense is inverted - inside rather than outside. (masks only) bool doInvert = false; if (imageCount < 0.0) { imageCount = - imageCount; doInvert = true; } vec4 colorTexel = vec4(0); vec4 maskTexel = vec4(0); bool doMask = imageCount != kTextureContentClassifierOnly; bool doClassify = imageCount != kTextureContentMaskOnly; if (!isOutside) { if (imageCount == kTextureContentClassifierOnly) { colorTexel = TEXTURE(s_pClassSampler, vec2(classPos.x, classPos.y / imageCount)); } else if (imageCount == kTextureContentMaskOnly) { maskTexel = TEXTURE(s_pClassSampler, vec2(classPos.x, classPos.y)); } else if (imageCount == kTextureContentClassifierAndMask) { colorTexel = TEXTURE(s_pClassSampler, vec2(classPos.x, classPos.y / imageCount)); maskTexel = TEXTURE(s_pClassSampler, vec2(classPos.x, (2.0 + classPos.y) / imageCount)); } if (colorTexel.b >= 0.5) { if (u_shaderFlags[kShaderBit_IgnoreNonLocatable]) { discard; return vec4(0.0); } colorTexel.b = (colorTexel.b * 255.0 - 128.0) / 127.0; } else { colorTexel.b *= 255.0 / 127.0; } } if (doMask) { bool masked = !isOutside && (maskTexel.r + maskTexel.g + maskTexel.b + maskTexel.a) > 0.0; if (doInvert) masked = !masked; if (masked) { float maskTransparency = u_pClassColorParams.w < 0.0 ? (1.0 - maskTexel.a) : u_pClassColorParams.w; if (maskTransparency <= 0.0) { discard; return vec4(0); } baseColor.a = baseColor.a * maskTransparency; } if (!doClassify) return baseColor; } bool isClassified = !isOutside && (colorTexel.r + colorTexel.g + colorTexel.b + colorTexel.a > 0.0); float param = isClassified ? u_pClassColorParams.x : u_pClassColorParams.y; if (kClassifierDisplay_Off == param) { discard; return vec4(0); } `; // Currently we discard if classifier is pure black (acts as clipping mask). // These could be more efficiently handled with masks. const applyPlanarClassificationColor = applyPlanarClassificationPrelude + // eslint-disable-line prefer-template ` float colorMix = u_pClassPointCloud ? .65 : .35; vec4 classColor; if (kClassifierDisplay_On == param) classColor = baseColor; else if (!isClassified || kClassifierDisplay_Dimmed == param) classColor = vec4(baseColor.rgb * dimScale, baseColor.a); else if (kClassifierDisplay_Hilite == param) classColor = vec4(mix(baseColor.rgb, u_hilite_settings[0], u_hilite_settings[2][0]), baseColor.a); else { if (colorTexel.b > colorTexel.a) { discard; return vec4(0.0); } // NB: colorTexel contains pre-multiplied alpha. We know it is greater than zero from above. float alpha = colorTexel.a * baseColor.a; vec3 rgb = colorTexel.rgb / colorTexel.a; rgb = mix(baseColor.rgb, rgb, colorMix); classColor = vec4(rgb, alpha); } if (kClassifierDisplay_Element != param && isClassified) { if (colorTexel.r > colorTexel.a && kClassifierDisplay_Hilite != param) classColor = vec4(mix(baseColor.rgb, u_hilite_settings[0], u_hilite_settings[2][0]), 1.0); if (colorTexel.g > colorTexel.a) classColor = applyClassifierFlash(classColor); } return classColor; `; const applyPlanarClassificationColorForThematic = applyPlanarClassificationPrelude + // eslint-disable-line prefer-template ` vec4 classColor = baseColor; if (kClassifierDisplay_Element == param) { if (colorTexel.b > colorTexel.a) { discard; return vec4(0.0); } // We stashed the element alpha in blue channel. Make sure to handle pre-multiplied alpha. baseColor.rgb = baseColor.rgb / baseColor.a; classColor = vec4(baseColor.rgb, colorTexel.b); classColor.rgb *= classColor.a; colorTexel.a = 0.5; // make conditions below potentially pass } if (isClassified) { if (colorTexel.r > colorTexel.a && kClassifierDisplay_Hilite != param) classColor = vec4(mix(baseColor.rgb, u_hilite_settings[0], u_hilite_settings[2][0]), 1.0); if (colorTexel.g > colorTexel.a) classColor = applyClassifierFlash(classColor); } return classColor; `; const overrideFeatureId = ` if (u_pClassColorParams.x != kClassifierDisplay_Element) return currentId; vec2 classPos = v_pClassPos / v_pClassPosW; vec4 featureTexel = TEXTURE(s_pClassSampler, vec2(classPos.x, (1.0 + classPos.y) / u_pClassColorParams.z)); return (featureTexel == vec4(0)) ? currentId : addUInt32s(u_batchBase, featureTexel * 255.0) / 255.0; `; const computeClassifiedHiliteColor = ` vec2 classPos = v_pClassPos / v_pClassPosW; return TEXTURE(s_pClassHiliteSampler, classPos); `; const computeClassifiedSurfaceHiliteColor = ` if (isSurfaceBitSet(kSurfaceBit_HasTexture) && TEXTURE(s_texture, v_texCoord).a <= 0.15) return vec4(0.0); ${computeClassifiedHiliteColor}`; const computeClassifierPos = "vec4 classProj = u_pClassProj * rawPosition; v_pClassPos = classProj.xy;"; const computeInstancedClassifierPos = "vec4 classProj = u_pClassProj * g_instancedRtcMatrix * rawPosition; v_pClassPos = classProj.xy;"; const computeClassifierPosW = "v_pClassPosW = classProj.w;"; const scratchBytes = new Uint8Array(4); const scratchBatchBaseId = new Uint32Array(scratchBytes.buffer); const scratchBatchBaseComponents = [0, 0, 0, 0]; const scratchColorParams = new Float32Array(4); // Unclassified scale, classified base scale, classified classifier scale, content/image count... MaskOnly = 1, ClassifierOnly = 2, ClassifierAndMask = 3 const scratchModel = core_geometry_1.Matrix4d.createIdentity(); const scratchModelProjection = core_geometry_1.Matrix4d.createIdentity(); const scratchMatrix = new Matrix_1.Matrix4(); function addPlanarClassifierCommon(builder) { const vert = builder.vert; vert.addUniform("u_pClassProj", 7 /* VariableType.Mat4 */, (prog) => { prog.addGraphicUniform("u_pClassProj", (uniform, params) => { const source = params.target.currentPlanarClassifierOrDrape; (0, core_bentley_1.assert)(undefined !== source || undefined !== params.target.activeVolumeClassifierTexture); if (undefined !== source) { source.projectionMatrix.multiplyMatrixMatrix(core_geometry_1.Matrix4d.createTransform(params.target.currentTransform, scratchModel), scratchModelProjection); scratchMatrix.initFromMatrix4d(scratchModelProjection); } else scratchMatrix.initIdentity(); // needs to be identity for volume classifiers uniform.setMatrix4(scratchMatrix); }); }); if (vert.usesInstancedGeometry) (0, Vertex_1.addInstancedRtcMatrix)(vert); builder.addInlineComputedVarying("v_pClassPos", 3 /* VariableType.Vec2 */, vert.usesInstancedGeometry ? computeInstancedClassifierPos : computeClassifierPos); builder.addInlineComputedVarying("v_pClassPosW", 2 /* VariableType.Float */, computeClassifierPosW); addPlanarClassifierConstants(builder.frag); } function addPlanarClassifierConstants(builder) { builder.addDefine("kClassifierDisplay_Off", core_common_1.SpatialClassifierInsideDisplay.Off.toFixed(1)); builder.addDefine("kClassifierDisplay_On", core_common_1.SpatialClassifierInsideDisplay.On.toFixed(1)); builder.addDefine("kClassifierDisplay_Dimmed", core_common_1.SpatialClassifierInsideDisplay.Dimmed.toFixed(1)); builder.addDefine("kClassifierDisplay_Hilite", core_common_1.SpatialClassifierInsideDisplay.Hilite.toFixed(1)); builder.addDefine("kClassifierDisplay_Element", core_common_1.SpatialClassifierInsideDisplay.ElementColor.toFixed(1)); const td = core_common_1.SpatialClassifierInsideDisplay.ElementColor + 1; builder.addDefine("kTextureDrape", td.toFixed(1)); builder.addDefine("kTextureContentClassifierOnly", PlanarClassifier_1.PlanarClassifierContent.ClassifierOnly.toFixed(1)); builder.addDefine("kTextureContentMaskOnly", PlanarClassifier_1.PlanarClassifierContent.MaskOnly.toFixed(1)); builder.addDefine("kTextureContentClassifierAndMask", PlanarClassifier_1.PlanarClassifierContent.ClassifierAndMask.toFixed(1)); } /** @internal */ function addColorPlanarClassifier(builder, translucent, isThematic) { addPlanarClassifierCommon(builder); const frag = builder.frag; frag.addUniform("s_pClassSampler", 8 /* VariableType.Sampler2D */, (prog) => { prog.addGraphicUniform("s_pClassSampler", (uniform, params) => { const source = params.target.currentPlanarClassifierOrDrape; const volClass = params.target.activeVolumeClassifierTexture; (0, core_bentley_1.assert)(undefined !== source || undefined !== volClass); if (source) { (0, core_bentley_1.assert)(undefined !== source.texture); source.texture.texture.bindSampler(uniform, RenderFlags_1.TextureUnit.PlanarClassification); } else Texture_1.Texture2DHandle.bindSampler(uniform, (0, core_bentley_1.expectDefined)(volClass), RenderFlags_1.TextureUnit.PlanarClassification); }); }); frag.addUniform("u_pClassColorParams", 5 /* VariableType.Vec4 */, (prog) => { prog.addGraphicUniform("u_pClassColorParams", (uniform, params) => { const source = params.target.currentPlanarClassifierOrDrape; const volClass = params.target.activeVolumeClassifierTexture; (0, core_bentley_1.assert)(undefined !== source || undefined !== volClass); if (undefined !== source) { source.getParams(scratchColorParams); } else { scratchColorParams[0] = 6.0; // Volume classifier, by element color. scratchColorParams[1] = 0.5; // used for alpha value scratchColorParams[2] = 0.0; // Not used for volume. scratchColorParams[3] = 0.0; // Not used for volume. } uniform.setUniform4fv(scratchColorParams); }); }); if (isThematic === 0 /* IsThematic.No */) { frag.addUniform("u_pClassPointCloud", 0 /* VariableType.Boolean */, (prog) => { prog.addGraphicUniform("u_pClassPointCloud", (uniform, params) => { const classifier = params.target.currentPlanarClassifier; const isPointCloud = undefined !== classifier && classifier.isClassifyingPointCloud; uniform.setUniform1i(isPointCloud ? 1 : 0); }); }); } (0, FeatureSymbology_1.addClassifierFlash)(frag); if (translucent) // We will never call the shaders for volume classifiers with translucency, // so use a different version of the function which does not use glFragCoord to reduce the varyings count frag.addFunction(volClassTranslucentColor); else { (0, Fragment_1.addWindowToTexCoords)(frag); frag.addFunction(exports.volClassOpaqueColor); } (0, Common_1.addShaderFlags)(builder); frag.set(13 /* FragmentShaderComponent.ApplyPlanarClassifier */, (isThematic === 0 /* IsThematic.No */) ? applyPlanarClassificationColor : applyPlanarClassificationColorForThematic); } /** @internal */ function addFeaturePlanarClassifier(builder) { const frag = builder.frag; frag.addUniform("u_batchBase", 5 /* VariableType.Vec4 */, (prog) => { prog.addGraphicUniform("u_batchBase", (uniform, params) => { const classifier = params.target.currentPlanarClassifier; if (classifier !== undefined) { scratchBatchBaseId[0] = classifier.baseBatchId; scratchBatchBaseComponents[0] = scratchBytes[0]; scratchBatchBaseComponents[1] = scratchBytes[1]; scratchBatchBaseComponents[2] = scratchBytes[2]; scratchBatchBaseComponents[3] = scratchBytes[3]; } uniform.setUniform4fv(scratchBatchBaseComponents); }); }); frag.set(19 /* FragmentShaderComponent.OverrideFeatureId */, overrideFeatureId); frag.addFunction(Common_1.addUInt32s); } /** @internal */ function addHilitePlanarClassifier(builder, supportTextures = true) { addPlanarClassifierCommon(builder); const frag = builder.frag; frag.addUniform("s_pClassHiliteSampler", 8 /* VariableType.Sampler2D */, (prog) => { prog.addGraphicUniform("s_pClassHiliteSampler", (uniform, params) => { const classifier = (0, core_bentley_1.expectDefined)(params.target.currentPlanarClassifier); (0, core_bentley_1.assert)(undefined !== classifier && undefined !== classifier.hiliteTexture); classifier.hiliteTexture.texture.bindSampler(uniform, RenderFlags_1.TextureUnit.PlanarClassificationHilite); }); }); frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, supportTextures ? computeClassifiedSurfaceHiliteColor : computeClassifiedHiliteColor); } // NonLocatable flag is put in upper bit of blue component when drawing the classification texture. const encodeNonLocatableWithFeatures = ` vec4 encodeNonLocatable(vec4 clr) { float encoded_b = (floor(clr.b * 127.0) + float(extractNthBit(floor(v_feature_emphasis + 0.5), kEmphBit_NonLocatable)) * 128.0) / 255.0; return vec4(clr.r, clr.g, encoded_b, clr.a); } `; const encodeNonLocatable = ` vec4 encodeNonLocatable(vec4 clr) { float encoded_b = floor(clr.b * 127.0) / 255.0; return vec4(clr.r, clr.g, encoded_b, clr.a); } `; const overrideClassifierColorPrelude = ` if (0.0 == u_planarClassifierInsideMode) return currentColor; if (0.0 == currentColor.a) return encodeNonLocatable(vec4(0.0, 0.0, 1.0, 0.5)); `; const overrideClassifierEmphasis = ` if (kClassifierDisplay_Element != u_planarClassifierInsideMode) { float emph = floor(v_feature_emphasis + 0.5); if (0.0 != emph) return encodeNonLocatable(vec4(extractNthBit(emph, kEmphBit_Hilite), extractNthBit(emph, kEmphBit_Flash), 0.0, 0.5)); } `; const overrideClassifierColorPostlude = ` return encodeNonLocatable(currentColor); `; const overrideClassifierWithFeatures = overrideClassifierColorPrelude + overrideClassifierEmphasis + overrideClassifierColorPostlude; const overrideClassifierForClip = overrideClassifierColorPrelude + overrideClassifierColorPostlude; const overrideClassifierColorPreludeForThematic = ` if (0.0 == u_planarClassifierInsideMode) return currentColor; if (0.0 == currentColor.a) return encodeNonLocatable(vec4(0.0, 0.0, 1.0, 0.5)); bool isElem = kClassifierDisplay_Element == u_planarClassifierInsideMode; `; const overrideClassifierEmphasisForThematic = ` float emph = floor(v_feature_emphasis + 0.5); if (0.0 != emph) return encodeNonLocatable(vec4(extractNthBit(emph, kEmphBit_Hilite), extractNthBit(emph, kEmphBit_Flash), isElem ? currentColor.a : 0.0, isElem ? 1.0 : 0.5)); else if (kClassifierDisplay_Element == u_planarClassifierInsideMode) return encodeNonLocatable(vec4(0.0, 0.0, currentColor.a, 1.0)); `; // Thematic classifiers use alpha of 1 to blend; we just want thematic colors to largely win out except when selecting and flashing classifiers. const overrideClassifierColorPostludeClipForThematic = ` return encodeNonLocatable(isElem ? vec4(0.0, 0.0, 1.0, 1.0) : currentColor); `; const overrideClassifierWithFeaturesForThematic = overrideClassifierColorPreludeForThematic + overrideClassifierEmphasisForThematic + overrideClassifierColorPostlude; const overrideClassifierForClipForThematic = overrideClassifierColorPreludeForThematic + overrideClassifierColorPostludeClipForThematic; /** The classified geometry needs some information about the classifier geometry. The classified fragment shader outputs special values that do not represent valid RGB+A combinations when using * pre-multiplied alpha. The alpha channel will be 0.5, and the red, green, and/or blue channels will be 1.0: * - Red: hilited. * - Green: flashed. * - Blue: fully-transparent. Indicates clipping mask (discard the classified pixel). * @internal */ function addOverrideClassifierColor(builder, isThematic) { addPlanarClassifierConstants(builder.frag); builder.frag.addUniform("u_planarClassifierInsideMode", 2 /* VariableType.Float */, (prog) => { prog.addGraphicUniform("u_planarClassifierInsideMode", (uniform, params) => { const classifier = params.target.currentlyDrawingClassifier; const override = undefined !== classifier ? classifier.insideDisplay : 0; uniform.setUniform1f(override); }); }); const haveOverrides = undefined !== builder.frag.find("v_feature_emphasis"); builder.frag.addFunction(haveOverrides ? encodeNonLocatableWithFeatures : encodeNonLocatable); if (isThematic === 0 /* IsThematic.No */) builder.frag.set(21 /* FragmentShaderComponent.OverrideColor */, haveOverrides ? overrideClassifierWithFeatures : overrideClassifierForClip); else builder.frag.set(21 /* FragmentShaderComponent.OverrideColor */, haveOverrides ? overrideClassifierWithFeaturesForThematic : overrideClassifierForClipForThematic); } //# sourceMappingURL=PlanarClassification.js.map