UNPKG

@itwin/core-frontend

Version:
347 lines • 18.8 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 WebGL */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SurfaceGeometry = void 0; exports.wantMaterials = wantMaterials; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const SurfaceParams_1 = require("../../../common/internal/render/SurfaceParams"); const AttributeMap_1 = require("./AttributeMap"); const GL_1 = require("./GL"); const AttributeBuffers_1 = require("./AttributeBuffers"); const System_1 = require("./System"); const MeshGeometry_1 = require("./MeshGeometry"); const MapLayerParams_1 = require("./MapLayerParams"); const internal_1 = require("../../../tile/internal"); const core_geometry_1 = require("@itwin/core-geometry"); /** @internal */ function wantMaterials(vf) { return vf.materials && core_common_1.RenderMode.SmoothShade === vf.renderMode; } function wantLighting(vf) { return core_common_1.RenderMode.SmoothShade === vf.renderMode && vf.lighting; } /** @internal */ class SurfaceGeometry extends MeshGeometry_1.MeshGeometry { _buffers; _indices; hasTextures; textureParams; get lutBuffers() { return this._buffers; } static create(mesh, params) { const indices = params.surface.indices; const indexBuffer = AttributeBuffers_1.BufferHandle.createArrayBuffer(indices.data); const tile = params.tileData; const layerClassifiers = tile?.layerClassifiers; if (!layerClassifiers?.size || !tile || undefined === layerClassifiers) { return undefined !== indexBuffer ? new SurfaceGeometry(indexBuffer, indices.length, mesh, undefined) : undefined; } const transformECEF = tile.ecefTransform; const tileEcefRange = transformECEF.multiplyRange(tile.range); const cartographicRange = new core_common_1.CartographicRange(tileEcefRange, transformECEF); const boundingBox = cartographicRange.getLongitudeLatitudeBoundingBox(); const mapCartoRectangle = internal_1.MapCartoRectangle.fromRadians(boundingBox.low.x, boundingBox.low.y, boundingBox.high.x, boundingBox.high.y); const corners = tile.range.corners(); const normal = core_geometry_1.Vector3d.createCrossProductToPoints(corners[0], corners[1], corners[2])?.normalize(); if (!normal) { return undefined !== indexBuffer ? new SurfaceGeometry(indexBuffer, indices.length, mesh, undefined) : undefined; } const chordHeight = corners[0].distance(corners[3]) / 2; const surfacePlanarTilePatch = new internal_1.PlanarTilePatch(corners, normal, chordHeight); const surfaceProjection = new internal_1.PlanarProjection(surfacePlanarTilePatch); const meshParams = { projection: surfaceProjection, tileRectangle: mapCartoRectangle, tileId: undefined, baseColor: undefined, baseTransparent: false, layerClassifiers }; const layerTextures = []; let sequentialIndex = 0; layerClassifiers?.forEach((layerClassifier) => { layerTextures[sequentialIndex++] = new MapLayerParams_1.ProjectedTexture(layerClassifier, meshParams, meshParams.tileRectangle); }); let surfaceGeometry; if (undefined !== indexBuffer) { const indexCount = indices.length; const hasLayerTextures = layerTextures.length > 0; const layerTextureParams = hasLayerTextures ? MapLayerParams_1.LayerTextureParams.create(layerTextures) : undefined; surfaceGeometry = new SurfaceGeometry(indexBuffer, indexCount, mesh, layerTextureParams); } return surfaceGeometry; } get isDisposed() { return this._buffers.isDisposed && this._indices.isDisposed; } [Symbol.dispose]() { (0, core_bentley_1.dispose)(this._buffers); (0, core_bentley_1.dispose)(this._indices); } collectStatistics(stats) { stats.addSurface(this._indices.bytesUsed); } get isLit() { return SurfaceParams_1.SurfaceType.Lit === this.surfaceType || SurfaceParams_1.SurfaceType.TexturedLit === this.surfaceType; } get isTexturedType() { return SurfaceParams_1.SurfaceType.Textured === this.surfaceType || SurfaceParams_1.SurfaceType.TexturedLit === this.surfaceType; } get hasTexture() { return this.isTexturedType && undefined !== this.texture; } get hasNormalMap() { return this.isLit && this.isTexturedType && undefined !== this.normalMap; } get isGlyph() { return this.mesh.isGlyph; } get alwaysRenderTranslucent() { return this.isGlyph; } get isTileSection() { return undefined !== this.texture && this.texture.isTileSection; } get isClassifier() { return SurfaceParams_1.SurfaceType.VolumeClassifier === this.surfaceType; } get supportsThematicDisplay() { return !this.isGlyph; } get allowColorOverride() { // Text background color should not be overridden by feature symbology overrides - otherwise it becomes unreadable... // We don't actually know if we have text. // We do know that text background color uses blanking fill. So do ImageGraphics, so they're also going to forbid overriding their color. return core_common_1.FillFlags.Blanking !== (this.fillFlags & core_common_1.FillFlags.Blanking); } get asSurface() { return this; } get asEdge() { return undefined; } get asSilhouette() { return undefined; } _draw(numInstances, instanceBuffersContainer) { const system = System_1.System.instance; // If we can't write depth in the fragment shader, use polygonOffset to force blanking regions to draw behind. const offset = 2 /* RenderOrder.BlankingRegion */ === this.renderOrder && !system.supportsLogZBuffer; if (offset) { system.context.enable(GL_1.GL.POLYGON_OFFSET_FILL); system.context.polygonOffset(1.0, 1.0); } const bufs = instanceBuffersContainer !== undefined ? instanceBuffersContainer : this._buffers; bufs.bind(); system.drawArrays(GL_1.GL.PrimitiveType.Triangles, 0, this._numIndices, numInstances); bufs.unbind(); if (offset) system.context.disable(GL_1.GL.POLYGON_OFFSET_FILL); } wantMixMonochromeColor(target) { // Text relies on white-on-white reversal. return !this.isGlyph && (this.isLitSurface || this.wantTextures(target, this.hasTexture)); } get techniqueId() { return 0 /* TechniqueId.Surface */; } get isLitSurface() { return this.isLit; } get hasBakedLighting() { return this.mesh.hasBakedLighting; } get renderOrder() { if (core_common_1.FillFlags.Behind === (this.fillFlags & core_common_1.FillFlags.Behind)) return 2 /* RenderOrder.BlankingRegion */; let order = this.isLit ? 4 /* RenderOrder.LitSurface */ : 3 /* RenderOrder.UnlitSurface */; if (this.isPlanar) order = order | 8 /* RenderOrder.PlanarBit */; return order; } getColor(target) { if (core_common_1.FillFlags.Background === (this.fillFlags & core_common_1.FillFlags.Background)) return target.uniforms.style.backgroundColorInfo; else return this.colorInfo; } getPass(target) { // Classifiers have a dedicated pass if (this.isClassifier) return "classification"; let opaquePass = this.isPlanar ? "opaque-planar" : "opaque"; // When reading pixels, glyphs are always opaque. Otherwise always transparent (for anti-aliasing). if (this.isGlyph) return target.isReadPixelsInProgress ? opaquePass : "translucent"; const vf = target.currentViewFlags; // When rendering thematic isolines, we need translucency because they have anti-aliasing. const thematic = target.wantThematicDisplay && this.supportsThematicDisplay ? target.uniforms.thematic.thematicDisplay : undefined; if (thematic && target.uniforms.thematic.wantIsoLines) return "translucent"; // In wireframe, unless fill is explicitly enabled for planar region, surface does not draw if (core_common_1.RenderMode.Wireframe === vf.renderMode && !this.mesh.isTextureAlwaysDisplayed) { const fillFlags = this.fillFlags; const showFill = core_common_1.FillFlags.Always === (fillFlags & core_common_1.FillFlags.Always) || (vf.fill && core_common_1.FillFlags.ByView === (fillFlags & core_common_1.FillFlags.ByView)); if (!showFill) return "none"; } // If transparency disabled by render mode or view flag, always draw opaque. if (!vf.transparency || core_common_1.RenderMode.SolidFill === vf.renderMode || core_common_1.RenderMode.HiddenLine === vf.renderMode) return opaquePass; // A gradient texture applied by analysis style always fully determines the transparency of the surface. if (this.hasScalarAnimation && undefined !== target.analysisTexture) { (0, core_bentley_1.assert)(undefined !== target.analysisStyle?.thematic); switch (target.analysisStyle.thematic.thematicSettings.textureTransparency) { case core_common_1.TextureTransparency.Translucent: return "translucent"; case core_common_1.TextureTransparency.Opaque: return opaquePass; case core_common_1.TextureTransparency.Mixed: return `${opaquePass}-translucent`; } } // We have 3 sources of alpha: the material, the texture, and the color. // Base alpha comes from the material if it overrides it; otherwise from the color. // The texture's alpha is multiplied by the base alpha. // So we must draw in the translucent pass if the texture has transparency OR the base alpha is less than 1. let hasAlpha = false; const mat = wantMaterials(vf) ? this.mesh.materialInfo : undefined; if (undefined !== mat && mat.overridesAlpha) hasAlpha = mat.hasTranslucency; else hasAlpha = this.getColor(target).hasTranslucency; // Thematic gradient can optionally multiply gradient alpha with surface alpha. if (thematic && thematic.gradientSettings.transparencyMode === core_common_1.ThematicGradientTransparencyMode.MultiplySurfaceAndGradient) { switch (thematic.gradientSettings.textureTransparency) { case core_common_1.TextureTransparency.Opaque: // This surface's alpha gets multiplied by 1 - gradient colors are all opaque. return hasAlpha ? "translucent" : opaquePass; case core_common_1.TextureTransparency.Translucent: // This surface's alpha gets multiplied by < 1 - gradient colors are all translucent. return "translucent"; case core_common_1.TextureTransparency.Mixed: // The gradient contains a mix of translucent and opaque colors. return hasAlpha ? "translucent" : `${opaquePass}-translucent`; } } if (!hasAlpha) { const tex = this.wantTextures(target, true) ? this.texture : undefined; switch (tex?.transparency) { case core_common_1.TextureTransparency.Translucent: hasAlpha = true; break; case core_common_1.TextureTransparency.Mixed: opaquePass = `${opaquePass}-translucent`; break; } } return hasAlpha ? "translucent" : opaquePass; } _wantWoWReversal(target) { if (this.isGlyph) { // Raster text is always subject to white-on-white reversal. return true; } const fillFlags = this.fillFlags; if (core_common_1.FillFlags.None !== (fillFlags & core_common_1.FillFlags.Background)) { return false; // fill color explicitly from background } if (this.wantTextures(target, this.hasTexture)) { // Don't invert white pixels of textures. return false; } const vf = target.currentViewFlags; if (core_common_1.RenderMode.Wireframe === vf.renderMode) { // Fill displayed even in wireframe? return core_common_1.FillFlags.None !== (fillFlags & core_common_1.FillFlags.Always); } if (vf.visibleEdges) { return false; // never invert surfaces when edges are displayed } if (this.isLit && wantLighting(vf)) { return false; // the lit color won't be pure white anyway. } return true; } get materialInfo() { return this.mesh.materialInfo; } useTexture(params) { return this.wantTextures(params.target, this.hasTexture); } useNormalMap(params) { return this.wantNormalMaps(params.target, this.hasNormalMap); } computeSurfaceFlags(params, flags) { const target = params.target; const vf = target.currentViewFlags; const useMaterial = wantMaterials(vf); flags[3 /* SurfaceBitIndex.IgnoreMaterial */] = useMaterial ? 0 : 1; flags[9 /* SurfaceBitIndex.HasMaterialAtlas */] = useMaterial && this.hasMaterialAtlas ? 1 : 0; flags[1 /* SurfaceBitIndex.ApplyLighting */] = 0; flags[6 /* SurfaceBitIndex.HasColorAndNormal */] = 0; if (this.isLit) { flags[2 /* SurfaceBitIndex.HasNormals */] = 1; if (wantLighting(vf)) flags[1 /* SurfaceBitIndex.ApplyLighting */] = 1; // Textured meshes store normal in place of color index. // Untextured lit meshes store normal where textured meshes would store UV coords. // Tell shader where to find normal. if (!this.isTexturedType) { flags[6 /* SurfaceBitIndex.HasColorAndNormal */] = 1; } } else { flags[2 /* SurfaceBitIndex.HasNormals */] = 0; } flags[0 /* SurfaceBitIndex.HasTexture */] = this.useTexture(params) ? 1 : 0; flags[8 /* SurfaceBitIndex.HasNormalMap */] = this.useNormalMap(params) ? 1 : 0; flags[10 /* SurfaceBitIndex.UseConstantLodTextureMapping */] = this.mesh.textureUsesConstantLod ? 1 : 0; flags[11 /* SurfaceBitIndex.UseConstantLodNormalMapMapping */] = this.mesh.normalMapUsesConstantLod ? 1 : 0; // The transparency threshold controls how transparent a surface must be to allow light to pass through; more opaque surfaces cast shadows. flags[4 /* SurfaceBitIndex.TransparencyThreshold */] = params.target.isDrawingShadowMap ? 1 : 0; flags[5 /* SurfaceBitIndex.BackgroundFill */] = 0; switch (params.renderPass) { // NB: We need this for opaque pass due to SolidFill (must compute transparency, discard below threshold, render opaque at or above threshold) case 2 /* RenderPass.OpaqueLinear */: case 3 /* RenderPass.OpaquePlanar */: case 5 /* RenderPass.OpaqueGeneral */: case 8 /* RenderPass.Translucent */: case 12 /* RenderPass.WorldOverlay */: case 1 /* RenderPass.OpaqueLayers */: case 7 /* RenderPass.TranslucentLayers */: case 11 /* RenderPass.OverlayLayers */: { const mode = vf.renderMode; if (!this.isGlyph && (core_common_1.RenderMode.HiddenLine === mode || core_common_1.RenderMode.SolidFill === mode)) { flags[4 /* SurfaceBitIndex.TransparencyThreshold */] = 1; if (core_common_1.RenderMode.HiddenLine === mode && core_common_1.FillFlags.Always !== (this.fillFlags & core_common_1.FillFlags.Always)) { // fill flags test for text - doesn't render with bg fill in hidden line mode. flags[5 /* SurfaceBitIndex.BackgroundFill */] = 1; } break; } } } } constructor(indices, numIndices, mesh, textureParams) { super(mesh, numIndices); this.textureParams = textureParams; this._buffers = AttributeBuffers_1.BuffersContainer.create(); const attrPos = AttributeMap_1.AttributeMap.findAttribute("a_pos", 0 /* TechniqueId.Surface */, false); (0, core_bentley_1.assert)(undefined !== attrPos); this._buffers.addBuffer(indices, [AttributeBuffers_1.BufferParameters.create(attrPos.location, 3, GL_1.GL.DataType.UnsignedByte, false, 0, 0, false)]); this._indices = indices; this.hasTextures = undefined !== this.textureParams && this.textureParams.params.some((x) => undefined !== x.texture); } wantTextures(target, surfaceTextureExists) { if (this.hasScalarAnimation && undefined !== target.analysisTexture) return true; if (!surfaceTextureExists) return false; if (this.mesh.isTextureAlwaysDisplayed) return true; if (this.supportsThematicDisplay && target.wantThematicDisplay) return false; const fill = this.fillFlags; const flags = target.currentViewFlags; // ###TODO need to distinguish between gradient fill and actual textures... switch (flags.renderMode) { case core_common_1.RenderMode.SmoothShade: return flags.textures; case core_common_1.RenderMode.Wireframe: return core_common_1.FillFlags.Always === (fill & core_common_1.FillFlags.Always) || (flags.fill && core_common_1.FillFlags.ByView === (fill & core_common_1.FillFlags.ByView)); default: return core_common_1.FillFlags.Always === (fill & core_common_1.FillFlags.Always); } } wantNormalMaps(target, normalMapExists) { if (!normalMapExists || !target.displayNormalMaps) return false; const flags = target.currentViewFlags; switch (flags.renderMode) { case core_common_1.RenderMode.SmoothShade: return flags.textures; default: return false; } } } exports.SurfaceGeometry = SurfaceGeometry; //# sourceMappingURL=SurfaceGeometry.js.map