@itwin/core-frontend
Version:
iTwin.js frontend components
731 lines (699 loc) • 35.6 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 WebGL
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.mixFeatureColor = exports.readDepthAndOrder = void 0;
exports.addOvrFlagConstants = addOvrFlagConstants;
exports.addFeatureIndex = addFeatureIndex;
exports.addMaxAlpha = addMaxAlpha;
exports.addSurfaceHiliter = addSurfaceHiliter;
exports.addHiliter = addHiliter;
exports.addRenderOrderConstants = addRenderOrderConstants;
exports.addRenderOrder = addRenderOrder;
exports.addPixelWidthFactor = addPixelWidthFactor;
exports.addFeatureId = addFeatureId;
exports.addSurfaceDiscard = addSurfaceDiscard;
exports.addClassifierFlash = addClassifierFlash;
exports.addFeatureSymbology = addFeatureSymbology;
exports.addUniformHiliter = addUniformHiliter;
exports.addUniformFeatureSymbology = addUniformFeatureSymbology;
const core_bentley_1 = require("@itwin/core-bentley");
const RenderFlags_1 = require("../RenderFlags");
const Common_1 = require("./Common");
const Decode_1 = require("./Decode");
const Fragment_1 = require("./Fragment");
const LookupTable_1 = require("./LookupTable");
const RenderPass_1 = require("./RenderPass");
const Vertex_1 = require("./Vertex");
/** @internal */
function addOvrFlagConstants(builder) {
// NB: These are the bit positions of each flag in OvrFlags enum - not the flag values
builder.addBitFlagConstant("kOvrBit_LineRgb", 0);
builder.addBitFlagConstant("kOvrBit_Rgb", 1);
builder.addBitFlagConstant("kOvrBit_Alpha", 2);
builder.addBitFlagConstant("kOvrBit_LineAlpha", 3);
builder.addBitFlagConstant("kOvrBit_Flashed", 4);
builder.addBitFlagConstant("kOvrBit_NonLocatable", 5);
builder.addBitFlagConstant("kOvrBit_LineCode", 6);
builder.addBitFlagConstant("kOvrBit_Weight", 7);
// NB: We treat the 16-bit flags as 2 bytes - so subtract 8 from each of these bit indices.
builder.addBitFlagConstant("kOvrBit_Hilited", 0);
builder.addBitFlagConstant("kOvrBit_Emphasized", 1);
builder.addBitFlagConstant("kOvrBit_ViewIndependentTransparency", 2);
builder.addBitFlagConstant("kOvrBit_InvisibleDuringPick", 3);
builder.addBitFlagConstant("kOvrBit_Visibility", 4);
builder.addBitFlagConstant("kOvrBit_IgnoreMaterial", 5);
}
const computeLUTFeatureIndex = `g_featureAndMaterialIndex.xyz`;
const computeInstanceFeatureIndex = `g_isAreaPattern ? u_patternFeatureId : a_featureId`;
function computeFeatureIndex(vertex) {
if (vertex.usesInstancedGeometry) {
vertex.addUniform("u_patternFeatureId", 4 /* VariableType.Vec3 */, (prog) => {
prog.addGraphicUniform("u_patternFeatureId", (uniform, params) => {
const id = params.geometry.asInstanced?.patternFeatureId;
(0, core_bentley_1.assert)(undefined !== id);
if (id)
uniform.setUniform3fv(id);
});
});
return `g_featureIndex = ${computeInstanceFeatureIndex};`;
}
return vertex.usesVertexTable ? `g_featureIndex = ${computeLUTFeatureIndex};` : "";
}
function getFeatureIndex(vertex) {
return `
float getFeatureIndex() {
${computeFeatureIndex(vertex)}
return decodeUInt24(g_featureIndex);
}
`;
}
const nthFeatureBitSet = `
bool nthFeatureBitSet(float flags, uint n) {
return 0u == (u_globalOvrFlags & n) && nthBitSet(flags, n);
}
`;
const extractNthFeatureBit = `
float extractNthFeatureBit(float flags, uint n) {
return 0u == (u_globalOvrFlags & n) && nthBitSet(flags, n) ? 1.0 : 0.0;
}
`;
const computeFeatureTextureCoords = `
vec2 computeFeatureTextureCoords() { return compute_feature_coords(getFeatureIndex()); }
`;
const getFirstFeatureRgba = `
vec4 getFirstFeatureRgba() {
feature_texCoord = computeFeatureTextureCoords();
return TEXTURE(u_featureLUT, feature_texCoord);
}
`;
const getSecondFeatureRgba = `
vec4 getSecondFeatureRgba(bool isLinear) {
vec2 coord = feature_texCoord;
coord.x += g_feature_stepX * (isLinear ? 2.0 : 1.0);
return TEXTURE(u_featureLUT, coord);
}
`;
const computeLineWeight = `
float computeLineWeight() {
return linear_feature_overrides.x > 0.5 ? linear_feature_overrides.y : g_lineWeight;
}
`;
const computeLineCode = `
float computeLineCode() {
return linear_feature_overrides.z > 0.5 ? linear_feature_overrides.w : g_lineCode;
}
`;
function addFeatureIndex(vert) {
vert.addGlobal("g_featureIndex", 4 /* VariableType.Vec3 */);
vert.addFunction(Decode_1.decodeUint24);
vert.addFunction(getFeatureIndex(vert));
}
// Discards vertex if feature is invisible; or rendering opaque during translucent pass or vice-versa
// (The latter occurs when some translucent feature is overridden to be opaque, or vice-versa)
const checkVertexDiscard = `
if (feature_invisible)
return true;
bool hasAlpha = 1.0 == u_hasAlpha;
if (feature_alpha > 0.0)
hasAlpha = feature_alpha <= s_maxAlpha;
int discardFlags = u_transparencyDiscardFlags;
bool discardViewIndependentDuringOpaque = discardFlags >= 4;
if (discardViewIndependentDuringOpaque)
discardFlags = discardFlags - 4;
bool isOpaquePass = (kRenderPass_OpaqueLinear <= u_renderPass && kRenderPass_OpaqueGeneral >= u_renderPass);
bool discardTranslucentDuringOpaquePass = 1 == discardFlags || 3 == discardFlags || (feature_viewIndependentTransparency && discardViewIndependentDuringOpaque);
if (isOpaquePass && !discardTranslucentDuringOpaquePass)
return false;
bool isTranslucentPass = kRenderPass_Translucent == u_renderPass;
bool discardOpaqueDuringTranslucentPass = 2 == discardFlags || 3 == discardFlags;
if (isTranslucentPass && !discardOpaqueDuringTranslucentPass)
return false;
return (isOpaquePass && hasAlpha) || (isTranslucentPass && !hasAlpha);
`;
function addTransparencyDiscardFlags(vert) {
// Even when transparency view flag is off, we need to allow features to override transparency, because it
// is used when applying transparency threshold. However, we need to ensure we don't DISCARD transparent stuff during
// opaque pass if transparency is off (see checkVertexDiscard). Especially important for transparency threshold and readPixels().
// Also, if we override raster text to be opaque we must still draw it in the translucent pass.
// Finally, if the transparency override is view-independent (i.e., ignores view flags and render mode) we want to discard it during opaque pass
// unless we're reading pixels.
// So we have a bit field:
// 1: discard translucent during opaque.
// 2: discard opaque during translucent.
// 4: discard view-independent translucent during opaque.
vert.addUniform("u_transparencyDiscardFlags", 1 /* VariableType.Int */, (prog) => {
prog.addGraphicUniform("u_transparencyDiscardFlags", (uniform, params) => {
let flags = 0;
// Textured surfaces may render in both passes. If so, it's up to fragment shader to handle discard.
const pass = params.geometry.getPass(params.target);
if (!RenderFlags_1.Pass.rendersOpaqueAndTranslucent(pass)) {
// During readPixels() we force transparency off. Make sure to ignore a Branch that turns it back on.
if (!params.target.isReadPixelsInProgress)
flags = params.target.currentViewFlags.transparency ? 1 : 4;
if (!params.geometry.alwaysRenderTranslucent)
flags += 2;
}
uniform.setUniform1i(flags);
});
});
}
function addCommon(builder, mode, opts, wantGlobalOvrFlags = true) {
if (0 /* FeatureMode.None */ === mode)
return false;
const vert = builder.vert;
addFeatureIndex(vert);
const haveOverrides = 0 /* FeatureSymbologyOptions.None */ !== (opts & 4 /* FeatureSymbologyOptions.HasOverrides */);
if (!haveOverrides) {
// For pick output we must compute g_featureIndex...
if (1 /* FeatureMode.Pick */ === mode)
vert.set(3 /* VertexShaderComponent.ComputeFeatureOverrides */, computeFeatureIndex(vert));
return true;
}
const wantWeight = 0 /* FeatureSymbologyOptions.None */ !== (opts & 1 /* FeatureSymbologyOptions.Weight */);
const wantLineCode = 0 /* FeatureSymbologyOptions.None */ !== (opts & 2 /* FeatureSymbologyOptions.LineCode */);
const wantColor = 0 /* FeatureSymbologyOptions.None */ !== (opts & 8 /* FeatureSymbologyOptions.Color */);
const wantAlpha = 0 /* FeatureSymbologyOptions.None */ !== (opts & 16 /* FeatureSymbologyOptions.Alpha */);
(0, core_bentley_1.assert)(wantColor || !wantAlpha);
(0, Common_1.addExtractNthBit)(vert);
addOvrFlagConstants(vert);
vert.addGlobal("linear_feature_overrides", 5 /* VariableType.Vec4 */, "vec4(0.0)");
vert.addGlobal("feature_ignore_material", 0 /* VariableType.Boolean */, "false");
if (wantWeight || wantLineCode) {
if (wantLineCode)
(0, Vertex_1.replaceLineCode)(vert, computeLineCode);
if (wantWeight) {
(0, Vertex_1.replaceLineWeight)(vert, computeLineWeight);
}
}
if (wantGlobalOvrFlags) {
const bitmapType = 10 /* VariableType.Uint */;
vert.addFunction(nthFeatureBitSet);
vert.addFunction(extractNthFeatureBit);
vert.addUniform("u_globalOvrFlags", bitmapType, (prog) => {
prog.addGraphicUniform("u_globalOvrFlags", (uniform, params) => {
let flags = 0.0;
if (params.geometry.isEdge) {
const settings = params.target.currentEdgeSettings;
flags = settings.computeOvrFlags(params.renderPass, params.target.currentViewFlags);
}
if (!params.geometry.allowColorOverride)
flags |= 6 /* OvrFlags.Rgba */;
uniform.setUniformBitflags(flags);
});
});
}
(0, LookupTable_1.addLookupTable)(vert, "feature", "3.0");
vert.addGlobal("feature_texCoord", 3 /* VariableType.Vec2 */);
vert.addFunction(computeFeatureTextureCoords);
vert.addFunction(getFirstFeatureRgba);
vert.addUniform("u_featureLUT", 8 /* VariableType.Sampler2D */, (prog) => {
prog.addGraphicUniform("u_featureLUT", (uniform, params) => {
params.target.uniforms.batch.bindLUT(uniform);
});
});
vert.addUniform("u_featureParams", 3 /* VariableType.Vec2 */, (prog) => {
prog.addGraphicUniform("u_featureParams", (uniform, params) => {
params.target.uniforms.batch.bindLUTParams(uniform);
});
});
if (wantColor) {
vert.addFunction(getSecondFeatureRgba);
if (wantAlpha) {
addMaxAlpha(vert);
(0, RenderPass_1.addRenderPass)(vert);
(0, Vertex_1.addAlpha)(vert);
addTransparencyDiscardFlags(vert);
vert.set(9 /* VertexShaderComponent.CheckForDiscard */, checkVertexDiscard);
}
}
return true;
}
function addMaxAlpha(builder) {
const minTransparency = 15.0; // NB: See DisplayParams.getMinTransparency() - this must match!
const maxAlpha = (255 - minTransparency) / 255;
builder.addConstant("s_maxAlpha", 2 /* VariableType.Float */, maxAlpha.toString());
}
/** @internal */
function addEmphasisFlags(builder) {
// Must be kept in sync with EmphasisFlags enum.
builder.addBitFlagConstant("kEmphBit_Hilite", 0);
builder.addBitFlagConstant("kEmphBit_Emphasize", 1);
builder.addBitFlagConstant("kEmphBit_Flash", 2);
builder.addBitFlagConstant("kEmphBit_NonLocatable", 3);
builder.addConstant("kEmphFlag_Hilite", 2 /* VariableType.Float */, "1.0");
builder.addConstant("kEmphFlag_Emphasize", 2 /* VariableType.Float */, "2.0");
builder.addConstant("kEmphFlag_Flash", 2 /* VariableType.Float */, "4.0");
builder.addConstant("kEmphFlag_NonLocatable", 2 /* VariableType.Float */, "8.0");
}
function addHiliteSettings(frag, wantFlashMode) {
frag.addUniform("u_hilite_settings", 6 /* VariableType.Mat3 */, (prog) => {
prog.addProgramUniform("u_hilite_settings", (uniform, params) => {
params.target.uniforms.hilite.bindFeatureSettings(uniform);
});
});
if (wantFlashMode) {
frag.addUniform("u_flash_mode", 2 /* VariableType.Float */, (prog) => {
prog.addGraphicUniform("u_flash_mode", (uniform, params) => {
uniform.setUniform1f(params.geometry.getFlashMode(params));
});
});
}
}
// If feature is not hilited, discard it.
const checkVertexHiliteDiscard = "return 0.0 == v_feature_hilited;";
// The result is a mask in which each pixel's r=1 if hilited and g=1 if emphasized (and not hilited).
const computeHiliteColor = `
float flags = floor(v_feature_hilited + 0.5);
float hilited = extractNthBit(flags, kEmphBit_Hilite);
float emphasized = extractNthBit(flags, kEmphBit_Emphasize);
return vec4(hilited, emphasized, 0.0, 0.0);
`;
const computeSurfaceHiliteColor = `
if (isSurfaceBitSet(kSurfaceBit_HasTexture) && TEXTURE(s_texture, v_texCoord).a <= 0.15)
return vec4(0.0);
${computeHiliteColor}`;
const computeHiliteOverrides = `
vec4 value = getFirstFeatureRgba();
float emphFlags = value.g * 256.0;
v_feature_hilited = kEmphFlag_Hilite * extractNthBit(emphFlags, kOvrBit_Hilited) + kEmphFlag_Emphasize * extractNthBit(emphFlags, kOvrBit_Emphasized);
`;
const computeHiliteOverridesWithWeight = `${computeHiliteOverrides}
float flags = value.r * 256.0;
linear_feature_overrides = vec4(nthFeatureBitSet(flags, kOvrBit_Weight),
value.a * 256.0,
nthFeatureBitSet(flags, kOvrBit_LineCode),
value.b * 256.0);
`;
/** @internal */
function addSurfaceHiliter(builder, wantWeight = false) {
addHiliter(builder, wantWeight);
builder.frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeSurfaceHiliteColor);
}
/** @internal */
function addHiliter(builder, wantWeight = false) {
let opts = 4 /* FeatureSymbologyOptions.HasOverrides */;
if (wantWeight)
opts |= 1 /* FeatureSymbologyOptions.Weight */; // hiliter never needs line code or color...
if (!addCommon(builder, 2 /* FeatureMode.Overrides */, opts, wantWeight))
return;
builder.addVarying("v_feature_hilited", 2 /* VariableType.Float */);
addEmphasisFlags(builder.vert);
builder.vert.set(3 /* VertexShaderComponent.ComputeFeatureOverrides */, wantWeight ? computeHiliteOverridesWithWeight : computeHiliteOverrides);
builder.vert.set(9 /* VertexShaderComponent.CheckForDiscard */, checkVertexHiliteDiscard);
addEmphasisFlags(builder.frag);
(0, Common_1.addExtractNthBit)(builder.frag);
builder.frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeHiliteColor);
builder.frag.set(18 /* FragmentShaderComponent.AssignFragData */, Fragment_1.assignFragColor);
}
function addSamplers(frag, testFeatureId) {
if (testFeatureId) {
frag.addUniform("u_pickFeatureId", 8 /* VariableType.Sampler2D */, (prog) => {
prog.addProgramUniform("u_pickFeatureId", (uniform, params) => {
params.target.compositor.featureIds.bindSampler(uniform, RenderFlags_1.TextureUnit.PickFeatureId);
});
}, 3 /* VariablePrecision.High */);
}
frag.addUniform("u_pickDepthAndOrder", 8 /* VariableType.Sampler2D */, (prog) => {
prog.addProgramUniform("u_pickDepthAndOrder", (uniform, params) => {
params.target.compositor.depthAndOrder.bindSampler(uniform, RenderFlags_1.TextureUnit.PickDepthAndOrder);
});
}, 3 /* VariablePrecision.High */);
}
/** @internal */
exports.readDepthAndOrder = `
vec2 readDepthAndOrder(vec2 tc) {
vec4 pdo = TEXTURE(u_pickDepthAndOrder, tc);
float order = floor(pdo.x * 16.0 + 0.5);
return vec2(order, decodeDepthRgb(pdo.yzw));
}
`;
const checkForEarlySurfaceDiscard = `
float factor = float(u_renderPass <= kRenderPass_Translucent); // never discard during specific passes
float term = 0.0;
vec2 tc = windowCoordsToTexCoords(gl_FragCoord.xy);
vec2 depthAndOrder = readDepthAndOrder(tc);
float surfaceDepth = computeLinearDepth(v_eyeSpace.z);
term += float(depthAndOrder.x > u_renderOrder && abs(depthAndOrder.y - surfaceDepth) < 4.0e-5);
return factor * term > 0.0;
`;
const checkForEarlySurfaceDiscardWithFeatureID = `
// No normals => unlt => reality model => no edges.
if (u_renderPass > kRenderPass_Translucent || u_renderPass == kRenderPass_Layers || !u_surfaceFlags[kSurfaceBitIndex_HasNormals])
return false;
vec2 tc = windowCoordsToTexCoords(gl_FragCoord.xy);
vec2 depthAndOrder = readDepthAndOrder(tc);
if (depthAndOrder.x <= u_renderOrder)
return false;
// Calculate depthTolerance for letting edges show through their own surfaces
float perspectiveFrustum = step(kFrustumType_Perspective, u_frustum.z);
vec4 eyeDirAndWidthFactor = mix(vec4(0.0, 0.0, 1.0, u_pixelWidthFactor), vec4(normalize(-v_eyeSpace.xyz), -v_eyeSpace.z * u_pixelWidthFactor), perspectiveFrustum);
vec3 eyeDir = eyeDirAndWidthFactor.xyz;
float dtWidthFactor = eyeDirAndWidthFactor.w;
// Compute depth tolerance based on angle of triangle to screen
float isSilhouette = float(depthAndOrder.x == kRenderOrder_Silhouette);
float dSq = dot(eyeDir, v_n);
dSq *= 0.5 + 0.4 * (1.0 - isSilhouette);
dSq = dSq * dSq;
dSq = max(dSq, 0.0001);
dSq = min(dSq, 0.999);
float depthTolerance = dtWidthFactor * v_lineWeight * sqrt((1.0 - dSq) / dSq);
depthTolerance *= 1.0 + .333 * isSilhouette;
// Make sure stuff behind camera doesn't get pushed in front of it
depthTolerance = max(depthTolerance, 0.0);
// Convert depthTolerance from eye space to linear depth
depthTolerance /= (u_frustum.y - u_frustum.x);
float surfaceDepth = computeLinearDepth(v_eyeSpace.z);
float depthDelta = abs(depthAndOrder.y - surfaceDepth);
if (depthDelta > depthTolerance)
return false;
// Does pick buffer contain same feature?
vec4 featId = TEXTURE(u_pickFeatureId, tc);
// Converting to ints to test since varying floats can be interpolated incorrectly
ivec4 featId_i = ivec4(featId * 255.0 + 0.5);
ivec4 feature_id_i = ivec4(feature_id * 255.0 + 0.5);
if (featId_i == feature_id_i)
return true;
// In 2d, display priority controls draw order of different elements.
if (!u_checkInterElementDiscard)
return false;
// Use a tighter tolerance for two different elements since we're only fighting roundoff error.
return depthDelta <= 4.0e-5;
`;
// This only adds the constants that are actually used in shader code.
function addRenderOrderConstants(builder) {
builder.addConstant("kRenderOrder_BlankingRegion", 2 /* VariableType.Float */, 2 /* RenderOrder.BlankingRegion */.toFixed(1));
builder.addConstant("kRenderOrder_Linear", 2 /* VariableType.Float */, 5 /* RenderOrder.Linear */.toFixed(1));
builder.addConstant("kRenderOrder_PlanarLinear", 2 /* VariableType.Float */, 13 /* RenderOrder.PlanarLinear */.toFixed(1));
builder.addConstant("kRenderOrder_Edge", 2 /* VariableType.Float */, 6 /* RenderOrder.Edge */.toFixed(1));
builder.addConstant("kRenderOrder_PlanarEdge", 2 /* VariableType.Float */, 14 /* RenderOrder.PlanarEdge */.toFixed(1));
builder.addConstant("kRenderOrder_Silhouette", 2 /* VariableType.Float */, 7 /* RenderOrder.Silhouette */.toFixed(1));
builder.addConstant("kRenderOrder_PlanarSilhouette", 2 /* VariableType.Float */, 15 /* RenderOrder.PlanarSilhouette */.toFixed(1));
builder.addConstant("kRenderOrder_UnlitSurface", 2 /* VariableType.Float */, 3 /* RenderOrder.UnlitSurface */.toFixed(1));
builder.addConstant("kRenderOrder_LitSurface", 2 /* VariableType.Float */, 4 /* RenderOrder.LitSurface */.toFixed(1));
builder.addConstant("kRenderOrder_PlanarUnlitSurface", 2 /* VariableType.Float */, 11 /* RenderOrder.PlanarUnlitSurface */.toFixed(1));
builder.addConstant("kRenderOrder_PlanarLitSurface", 2 /* VariableType.Float */, 12 /* RenderOrder.PlanarLitSurface */.toFixed(1));
builder.addConstant("kRenderOrder_PlanarBit", 2 /* VariableType.Float */, 8 /* RenderOrder.PlanarBit */.toFixed(1));
builder.addConstant("kRenderOrder_Background", 2 /* VariableType.Float */, 1 /* RenderOrder.Background */.toFixed(1));
}
/** @internal */
function addRenderOrder(builder) {
builder.addUniform("u_renderOrder", 2 /* VariableType.Float */, (prog) => {
prog.addGraphicUniform("u_renderOrder", (uniform, params) => {
const order = params.target.drawingBackgroundForReadPixels ? 1 /* RenderOrder.Background */ : params.geometry.renderOrder;
uniform.setUniform1f(order);
});
});
}
function addPixelWidthFactor(builder) {
builder.addUniform("u_pixelWidthFactor", 2 /* VariableType.Float */, (prog) => {
prog.addGraphicUniform("u_pixelWidthFactor", (uniform, params) => {
params.target.uniforms.bindPixelWidthFactor(uniform);
});
});
}
function addBatchId(builder) {
builder.addUniform("u_batch_id", 5 /* VariableType.Vec4 */, (prog) => {
prog.addGraphicUniform("u_batch_id", (uniform, params) => {
params.target.uniforms.batch.bindBatchId(uniform);
});
}, 3 /* VariablePrecision.High */);
}
const computeIdVert = `v_feature_id = addUInt32s(u_batch_id, vec4(g_featureIndex, 0.0)) / 255.0;`;
const computeIdFrag = `
vec4 featureIndex = vec4(floor(v_feature_index + 0.5), 0.0);
feature_id = addUInt32s(u_batch_id, featureIndex) / 255.0;
`;
/** @internal */
function addFeatureId(builder, computeInFrag) {
const vert = builder.vert;
const frag = builder.frag;
frag.addGlobal("feature_id", 5 /* VariableType.Vec4 */);
if (!computeInFrag) {
vert.addFunction(Common_1.addUInt32s);
addBatchId(vert);
builder.addInlineComputedVarying("v_feature_id", 5 /* VariableType.Vec4 */, computeIdVert);
frag.addInitializer("feature_id = v_feature_id;");
}
else {
frag.addFunction(Common_1.addUInt32s);
builder.addInlineComputedVarying("v_feature_index", 4 /* VariableType.Vec3 */, "v_feature_index = g_featureIndex;");
addBatchId(frag);
frag.addInitializer(computeIdFrag);
}
}
// Discard vertex if transparency is less than the display style's transparency threshold, IFF the specific bit is set. The bit is set if:
// - Solid Fill or Hidden Line mode; or
// - Shaded mode and generating shadow map (sufficiently transparent surfaces receive but do not cast shadows).
const isBelowTransparencyThreshold = `
return v_color.a < u_transparencyThreshold && u_surfaceFlags[kSurfaceBitIndex_TransparencyThreshold];
`;
/** @internal */
function addSurfaceDiscard(builder, flags) {
const feat = flags.featureMode;
const isEdgeTestNeeded = flags.isEdgeTestNeeded;
const isClassified = flags.isClassified;
const computeIdInFrag = !flags.isTranslucent && 0 !== flags.isClassified && 2 /* FeatureMode.Overrides */ === feat;
const frag = builder.frag;
const vert = builder.vert;
vert.set(12 /* VertexShaderComponent.CheckForLateDiscard */, isBelowTransparencyThreshold);
vert.addUniform("u_transparencyThreshold", 2 /* VariableType.Float */, (prog) => {
prog.addGraphicUniform("u_transparencyThreshold", (uniform, params) => {
uniform.setUniform1f(params.target.currentTransparencyThreshold);
});
});
if (isEdgeTestNeeded) {
(0, Fragment_1.addWindowToTexCoords)(frag);
if (!flags.isHilite)
(0, Common_1.addEyeSpace)(builder);
if (0 /* FeatureMode.None */ === feat) {
addSamplers(frag, false);
frag.addFunction(Fragment_1.computeLinearDepth);
frag.addFunction(Decode_1.decodeDepthRgb);
frag.addFunction(exports.readDepthAndOrder);
frag.set(0 /* FragmentShaderComponent.CheckForEarlyDiscard */, checkForEarlySurfaceDiscard);
}
else {
frag.addUniform("u_checkInterElementDiscard", 0 /* VariableType.Boolean */, (prog) => {
prog.addGraphicUniform("u_checkInterElementDiscard", (uniform, params) => {
uniform.setUniform1i(params.target.uniforms.branch.top.is3d ? 1 : 0);
});
});
addFeatureIndex(vert);
(0, Vertex_1.addLineWeight)(vert);
addSamplers(frag, true);
addRenderOrderConstants(frag);
addPixelWidthFactor(frag);
frag.addFunction(Fragment_1.computeLinearDepth);
frag.addFunction(Decode_1.decodeDepthRgb);
frag.addFunction(exports.readDepthAndOrder);
frag.set(0 /* FragmentShaderComponent.CheckForEarlyDiscard */, checkForEarlySurfaceDiscardWithFeatureID);
builder.addInlineComputedVarying("v_lineWeight", 2 /* VariableType.Float */, "v_lineWeight = computeLineWeight();");
addFeatureId(builder, computeIdInFrag);
}
addRenderOrder(frag);
(0, RenderPass_1.addRenderPass)(frag);
}
else if (isClassified && 0 /* FeatureMode.None */ !== feat) {
addFeatureIndex(vert);
addFeatureId(builder, computeIdInFrag);
if (!flags.isTranslucent)
addRenderOrder(frag);
}
}
// bool feature_invisible = false;
// vec3 feature_rgb; // if not overridden, .r < 0; else rgb color override
// float feature_alpha // alpha if overridden, else < 0
// varying float v_feature_emphasis // bitmask - see kEmph_* constants
// vec4 linear_feature_overrides; // x: weight overridden y: weight z: line code overridden w: line code
const computeFeatureOverrides = `
feature_rgb = vec3(-1.0);
feature_alpha = -1.0;
vec4 value = getFirstFeatureRgba();
float emphFlags = value.y * 256.0;
if (nthFeatureBitSet(emphFlags, kOvrBit_InvisibleDuringPick)) {
feature_invisible = true;
return;
}
v_feature_emphasis = kEmphFlag_Hilite * extractNthBit(emphFlags, kOvrBit_Hilited) + kEmphFlag_Emphasize * extractNthBit(emphFlags, kOvrBit_Emphasized);
float flags = value.x * 256.0;
if (0.0 == flags && 0.0 == emphFlags)
return; // nothing overridden for this feature
bool nonLocatable = (u_shaderFlags[kShaderBit_IgnoreNonLocatable] ? nthFeatureBitSet(flags, kOvrBit_NonLocatable) : false);
v_feature_emphasis += kEmphFlag_NonLocatable * float(nthFeatureBitSet(flags, kOvrBit_NonLocatable));
bool invisible = nthFeatureBitSet(emphFlags, kOvrBit_Visibility);
feature_invisible = invisible || nonLocatable;
if (feature_invisible)
return;
bool isLinear = u_renderOrder == kRenderOrder_Linear || u_renderOrder == kRenderOrder_PlanarLinear || u_renderOrder == kRenderOrder_PlanarEdge;
bool rgbOverridden = isLinear ? nthFeatureBitSet(flags, kOvrBit_LineRgb) : nthFeatureBitSet(flags, kOvrBit_Rgb);
bool alphaOverridden = isLinear ? nthFeatureBitSet(flags, kOvrBit_LineAlpha) : nthFeatureBitSet(flags, kOvrBit_Alpha);
if (alphaOverridden || rgbOverridden) {
vec4 rgba = getSecondFeatureRgba(isLinear);
if (rgbOverridden)
feature_rgb = rgba.rgb;
if (alphaOverridden) {
feature_alpha = rgba.a;
feature_viewIndependentTransparency = nthFeatureBitSet(emphFlags, kOvrBit_ViewIndependentTransparency);
}
}
linear_feature_overrides = vec4(nthFeatureBitSet(flags, kOvrBit_Weight),
value.w * 256.0,
nthFeatureBitSet(flags, kOvrBit_LineCode),
value.z * 256.0);
feature_ignore_material = nthFeatureBitSet(emphFlags, kOvrBit_IgnoreMaterial);
use_material = use_material && !feature_ignore_material;
v_feature_emphasis += kEmphFlag_Flash * extractNthFeatureBit(flags, kOvrBit_Flashed);
`;
// feature_rgb.r = -1.0 if rgb color not overridden for feature.
// feature_alpha = -1.0 if alpha not overridden for feature.
const applyFeatureColor = `
vec3 rgb = mix(baseColor.rgb, feature_rgb.rgb, step(0.0, feature_rgb.r));
float alpha = mix(baseColor.a, feature_alpha, step(0.0, feature_alpha));
return vec4(rgb, alpha);
`;
// feature_rgb.r = -1.0 if rgb color not overridden for feature, else mix based on u_overrrideColorMix.
// feature_alpha = -1.0 if alpha not overridden for feature.
exports.mixFeatureColor = `
vec3 rgb = mix(baseColor.rgb, mix(baseColor.rgb, feature_rgb.rgb, u_overrideColorMix), step(0.0, feature_rgb.r));
float alpha = mix(baseColor.a, feature_alpha, step(0.0, feature_alpha));
return vec4(rgb, alpha);
`;
const applyFlash = `
float flashHilite = floor(v_feature_emphasis + 0.5);
return doApplyFlash(flashHilite, baseColor);
`;
const doApplyFlash = `
vec4 doApplyFlash(float flags, vec4 baseColor) {
bool isFlashed = nthBitSet(flags, kEmphBit_Flash);
bool isHilited = nthBitSet(flags, kEmphBit_Hilite);
bool isEmphasized = !isHilited && nthBitSet(flags, kEmphBit_Emphasize);
vec3 hiliteRgb = isEmphasized ? u_hilite_settings[1] : u_hilite_settings[0];
isHilited = isEmphasized || isHilited;
float hiliteRatio = isHilited ? (isEmphasized ? u_hilite_settings[2][1] : u_hilite_settings[2][0]) : 0.0;
baseColor.rgb = mix(baseColor.rgb, hiliteRgb, hiliteRatio);
const float maxBrighten = 0.2;
float brighten = isFlashed ? u_flash_intensity * maxBrighten : 0.0;
vec3 brightRgb = baseColor.rgb + brighten;
const float maxTween = 0.75;
float hiliteFraction = isFlashed ? u_flash_intensity * maxTween : 0.0;
vec3 tweenRgb = baseColor.rgb * (1.0 - hiliteFraction);
tweenRgb += u_hilite_settings[0] * hiliteFraction;
return vec4(mix(tweenRgb, brightRgb, u_flash_mode), baseColor.a);
}
`;
const doClassifierFlash = `
vec4 applyClassifierFlash(vec4 baseColor) {
const float maxBrighten = 0.2;
float brighten = u_flash_intensity * maxBrighten;
vec3 brightRgb = baseColor.rgb + brighten;
return vec4(brightRgb, baseColor.a);
}
`;
/** @internal */
function addClassifierFlash(frag) {
addFlashIntensity(frag);
addHiliteSettings(frag, false);
frag.addFunction(doClassifierFlash);
}
function addFlashIntensity(frag) {
frag.addUniform("u_flash_intensity", 2 /* VariableType.Float */, (prog) => {
prog.addProgramUniform("u_flash_intensity", (uniform, params) => {
uniform.setUniform1f(params.target.flashIntensity);
});
});
}
function addApplyFlash(frag) {
addHiliteSettings(frag, true);
addEmphasisFlags(frag);
(0, Common_1.addExtractNthBit)(frag);
frag.addFunction(doApplyFlash);
frag.set(12 /* FragmentShaderComponent.ApplyFlash */, applyFlash);
addFlashIntensity(frag);
}
/** @internal */
function addFeatureSymbology(builder, feat, opts) {
if (!addCommon(builder, feat, opts) || 0 /* FeatureSymbologyOptions.None */ === opts)
return;
(0, core_bentley_1.assert)((4 /* FeatureSymbologyOptions.HasOverrides */ | 8 /* FeatureSymbologyOptions.Color */) === (opts & (4 /* FeatureSymbologyOptions.HasOverrides */ | 8 /* FeatureSymbologyOptions.Color */)));
builder.addGlobal("feature_rgb", 4 /* VariableType.Vec3 */);
builder.addGlobal("feature_alpha", 2 /* VariableType.Float */);
builder.addVarying("v_feature_emphasis", 2 /* VariableType.Float */);
const vert = builder.vert;
vert.addGlobal("feature_invisible", 0 /* VariableType.Boolean */, "false");
vert.addGlobal("feature_viewIndependentTransparency", 0 /* VariableType.Boolean */, "false");
addEmphasisFlags(vert);
vert.addGlobal("use_material", 0 /* VariableType.Boolean */, "true");
addRenderOrder(vert);
addRenderOrderConstants(vert);
vert.set(3 /* VertexShaderComponent.ComputeFeatureOverrides */, computeFeatureOverrides);
vert.set(7 /* VertexShaderComponent.ApplyFeatureColor */, applyFeatureColor);
addApplyFlash(builder.frag);
}
/** If we're running the hilite shader for a uniform feature, it follows that the feature must be hilited.
* So the hilite shader simply needs to output '1' for every fragment.
* @internal
*/
function addUniformHiliter(builder) {
builder.frag.addUniform("v_feature_hilited", 2 /* VariableType.Float */, (prog) => {
prog.addGraphicUniform("v_feature_hilited", (uniform, params) => {
params.target.uniforms.batch.bindUniformSymbologyFlags(uniform);
});
});
addEmphasisFlags(builder.frag);
(0, Common_1.addExtractNthBit)(builder.frag);
builder.frag.set(1 /* FragmentShaderComponent.ComputeBaseColor */, computeHiliteColor);
builder.frag.set(18 /* FragmentShaderComponent.AssignFragData */, Fragment_1.assignFragColor);
}
/** For a uniform feature table, the feature ID output to pick buffers is equal to the batch ID.
* The following symbology overrides are supported:
* - Visibility - implcitly, because if the feature is invisible its geometry will never be drawn.
* - Flash
* - Hilite
* - Color and Transparency- only for point clouds currently which set addFeatureColor to true.
* This shader could be simplified, but want to share code with the non-uniform versions...hence uniforms/globals with "v_" prefix typically used for varyings on no prefix...
* @internal
*/
function addUniformFeatureSymbology(builder, addFeatureColor) {
builder.vert.addGlobal("g_featureIndex", 4 /* VariableType.Vec3 */, "vec3(0.0)", true);
builder.frag.addUniform("v_feature_emphasis", 2 /* VariableType.Float */, (prog) => {
prog.addGraphicUniform("v_feature_emphasis", (uniform, params) => {
params.target.uniforms.batch.bindUniformSymbologyFlags(uniform);
});
});
if (addFeatureColor) {
builder.vert.addUniform("feature_rgb", 4 /* VariableType.Vec3 */, (prog) => {
prog.addGraphicUniform("feature_rgb", (uniform, params) => {
params.target.uniforms.batch.bindUniformColorOverride(uniform);
});
});
builder.vert.addUniform("feature_alpha", 2 /* VariableType.Float */, (prog) => {
prog.addGraphicUniform("feature_alpha", (uniform, params) => {
params.target.uniforms.batch.bindUniformTransparencyOverride(uniform);
});
});
builder.vert.set(7 /* VertexShaderComponent.ApplyFeatureColor */, applyFeatureColor);
(0, Vertex_1.addAlpha)(builder.vert);
addMaxAlpha(builder.vert);
(0, RenderPass_1.addRenderPass)(builder.vert);
addTransparencyDiscardFlags(builder.vert);
builder.vert.set(9 /* VertexShaderComponent.CheckForDiscard */, checkVertexDiscard);
}
else {
builder.vert.set(9 /* VertexShaderComponent.CheckForDiscard */, "return feature_invisible;");
}
// Non-Locatable... Discard if picking
builder.vert.addUniform("feature_invisible", 0 /* VariableType.Boolean */, (prog) => {
prog.addGraphicUniform("feature_invisible", (uniform, params) => {
params.target.uniforms.batch.bindUniformNonLocatable(uniform, params.target.drawNonLocatable);
});
});
builder.vert.addGlobal("feature_viewIndependentTransparency", 0 /* VariableType.Boolean */, "false");
addApplyFlash(builder.frag);
}
//# sourceMappingURL=FeatureSymbology.js.map