| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132 |
- import Cartesian2 from "../Core/Cartesian2.js";
- import Check from "../Core/Check.js";
- import clone from "../Core/clone.js";
- import Color from "../Core/Color.js";
- import combine from "../Core/combine.js";
- import defined from "../Core/defined.js";
- import deprecationWarning from "../Core/deprecationWarning.js";
- import destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import CesiumMath from "../Core/Math.js";
- import RuntimeError from "../Core/RuntimeError.js";
- import ContextLimits from "../Renderer/ContextLimits.js";
- import DrawCommand from "../Renderer/DrawCommand.js";
- import Pass from "../Renderer/Pass.js";
- import RenderState from "../Renderer/RenderState.js";
- import ShaderSource from "../Renderer/ShaderSource.js";
- import BatchTexture from "./BatchTexture.js";
- import BatchTableHierarchy from "./BatchTableHierarchy.js";
- import BlendingState from "./BlendingState.js";
- import Cesium3DTileColorBlendMode from "./Cesium3DTileColorBlendMode.js";
- import CullFace from "./CullFace.js";
- import getBinaryAccessor from "./getBinaryAccessor.js";
- import StencilConstants from "./StencilConstants.js";
- import StencilFunction from "./StencilFunction.js";
- import StencilOperation from "./StencilOperation.js";
- import addAllToArray from "../Core/addAllToArray.js";
-
- const DEFAULT_COLOR_VALUE = BatchTexture.DEFAULT_COLOR_VALUE;
- const DEFAULT_SHOW_VALUE = BatchTexture.DEFAULT_SHOW_VALUE;
-
- /**
- * @private
- * @constructor
- */
- function Cesium3DTileBatchTable(
- content,
- featuresLength,
- batchTableJson,
- batchTableBinary,
- colorChangedCallback,
- ) {
- /**
- * @readonly
- */
- this.featuresLength = featuresLength;
-
- let extensions;
- if (defined(batchTableJson)) {
- extensions = batchTableJson.extensions;
- }
- this._extensions = extensions ?? {};
-
- const properties = initializeProperties(batchTableJson);
- this._properties = properties;
-
- this._batchTableHierarchy = initializeHierarchy(
- this,
- batchTableJson,
- batchTableBinary,
- );
-
- const binaryProperties = getBinaryProperties(
- featuresLength,
- properties,
- batchTableBinary,
- );
- this._binaryPropertiesByteLength =
- countBinaryPropertyMemory(binaryProperties);
- this._batchTableBinaryProperties = binaryProperties;
-
- this._content = content;
-
- this._batchTexture = new BatchTexture({
- featuresLength: featuresLength,
- colorChangedCallback: colorChangedCallback,
- owner: content,
- statistics: content.tileset.statistics,
- });
- }
-
- // This can be overridden for testing purposes
- Cesium3DTileBatchTable._deprecationWarning = deprecationWarning;
-
- Object.defineProperties(Cesium3DTileBatchTable.prototype, {
- /**
- * Size of the batch table, including the batch table hierarchy's binary
- * buffers and any binary properties. JSON data is not counted.
- *
- * @memberof Cesium3DTileBatchTable.prototype
- * @type {number}
- * @readonly
- * @private
- */
- batchTableByteLength: {
- get: function () {
- let totalByteLength = this._binaryPropertiesByteLength;
-
- if (defined(this._batchTableHierarchy)) {
- totalByteLength += this._batchTableHierarchy.byteLength;
- }
-
- totalByteLength += this._batchTexture.byteLength;
-
- return totalByteLength;
- },
- },
- });
-
- function initializeProperties(jsonHeader) {
- const properties = {};
-
- if (!defined(jsonHeader)) {
- return properties;
- }
-
- for (const propertyName in jsonHeader) {
- if (
- jsonHeader.hasOwnProperty(propertyName) &&
- propertyName !== "HIERARCHY" && // Deprecated HIERARCHY property
- propertyName !== "extensions" &&
- propertyName !== "extras"
- ) {
- properties[propertyName] = clone(jsonHeader[propertyName], true);
- }
- }
-
- return properties;
- }
-
- function initializeHierarchy(batchTable, jsonHeader, binaryBody) {
- if (!defined(jsonHeader)) {
- return;
- }
-
- let hierarchy = batchTable._extensions["3DTILES_batch_table_hierarchy"];
-
- const legacyHierarchy = jsonHeader.HIERARCHY;
- if (defined(legacyHierarchy)) {
- Cesium3DTileBatchTable._deprecationWarning(
- "batchTableHierarchyExtension",
- "The batch table HIERARCHY property has been moved to an extension. Use extensions.3DTILES_batch_table_hierarchy instead.",
- );
- batchTable._extensions["3DTILES_batch_table_hierarchy"] = legacyHierarchy;
- hierarchy = legacyHierarchy;
- }
-
- if (!defined(hierarchy)) {
- return;
- }
-
- return new BatchTableHierarchy({
- extension: hierarchy,
- binaryBody: binaryBody,
- });
- }
-
- function getBinaryProperties(featuresLength, properties, binaryBody) {
- let binaryProperties;
- for (const name in properties) {
- if (properties.hasOwnProperty(name)) {
- const property = properties[name];
- const byteOffset = property.byteOffset;
- if (defined(byteOffset)) {
- // This is a binary property
- const componentType = property.componentType;
- const type = property.type;
- if (!defined(componentType)) {
- throw new RuntimeError("componentType is required.");
- }
- if (!defined(type)) {
- throw new RuntimeError("type is required.");
- }
- if (!defined(binaryBody)) {
- throw new RuntimeError(
- `Property ${name} requires a batch table binary.`,
- );
- }
-
- const binaryAccessor = getBinaryAccessor(property);
- const componentCount = binaryAccessor.componentsPerAttribute;
- const classType = binaryAccessor.classType;
- const typedArray = binaryAccessor.createArrayBufferView(
- binaryBody.buffer,
- binaryBody.byteOffset + byteOffset,
- featuresLength,
- );
-
- if (!defined(binaryProperties)) {
- binaryProperties = {};
- }
-
- // Store any information needed to access the binary data, including the typed array,
- // componentCount (e.g. a VEC4 would be 4), and the type used to pack and unpack (e.g. Cartesian4).
- binaryProperties[name] = {
- typedArray: typedArray,
- componentCount: componentCount,
- type: classType,
- };
- }
- }
- }
- return binaryProperties;
- }
-
- function countBinaryPropertyMemory(binaryProperties) {
- if (!defined(binaryProperties)) {
- return 0;
- }
-
- let byteLength = 0;
- for (const name in binaryProperties) {
- if (binaryProperties.hasOwnProperty(name)) {
- byteLength += binaryProperties[name].typedArray.byteLength;
- }
- }
- return byteLength;
- }
-
- Cesium3DTileBatchTable.getBinaryProperties = function (
- featuresLength,
- batchTableJson,
- batchTableBinary,
- ) {
- return getBinaryProperties(featuresLength, batchTableJson, batchTableBinary);
- };
-
- Cesium3DTileBatchTable.prototype.setShow = function (batchId, show) {
- this._batchTexture.setShow(batchId, show);
- };
-
- Cesium3DTileBatchTable.prototype.setAllShow = function (show) {
- this._batchTexture.setAllShow(show);
- };
-
- Cesium3DTileBatchTable.prototype.getShow = function (batchId) {
- return this._batchTexture.getShow(batchId);
- };
-
- Cesium3DTileBatchTable.prototype.setColor = function (batchId, color) {
- this._batchTexture.setColor(batchId, color);
- };
-
- Cesium3DTileBatchTable.prototype.setAllColor = function (color) {
- this._batchTexture.setAllColor(color);
- };
-
- Cesium3DTileBatchTable.prototype.getColor = function (batchId, result) {
- return this._batchTexture.getColor(batchId, result);
- };
-
- Cesium3DTileBatchTable.prototype.getPickColor = function (batchId) {
- return this._batchTexture.getPickColor(batchId);
- };
-
- const scratchColor = new Color();
-
- Cesium3DTileBatchTable.prototype.applyStyle = function (style) {
- if (!defined(style)) {
- this.setAllColor(DEFAULT_COLOR_VALUE);
- this.setAllShow(DEFAULT_SHOW_VALUE);
- return;
- }
-
- const content = this._content;
- const length = this.featuresLength;
- for (let i = 0; i < length; ++i) {
- const feature = content.getFeature(i);
- const color = defined(style.color)
- ? (style.color.evaluateColor(feature, scratchColor) ??
- DEFAULT_COLOR_VALUE)
- : DEFAULT_COLOR_VALUE;
- const show = defined(style.show)
- ? (style.show.evaluate(feature) ?? DEFAULT_SHOW_VALUE)
- : DEFAULT_SHOW_VALUE;
- this.setColor(i, color);
- this.setShow(i, show);
- }
- };
-
- function getBinaryProperty(binaryProperty, index) {
- const typedArray = binaryProperty.typedArray;
- const componentCount = binaryProperty.componentCount;
- if (componentCount === 1) {
- return typedArray[index];
- }
- return binaryProperty.type.unpack(typedArray, index * componentCount);
- }
-
- function setBinaryProperty(binaryProperty, index, value) {
- const typedArray = binaryProperty.typedArray;
- const componentCount = binaryProperty.componentCount;
- if (componentCount === 1) {
- typedArray[index] = value;
- } else {
- binaryProperty.type.pack(value, typedArray, index * componentCount);
- }
- }
-
- function checkBatchId(batchId, featuresLength) {
- if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) {
- throw new DeveloperError(
- `batchId is required and must be between zero and featuresLength - 1 (${featuresLength}` -
- +").",
- );
- }
- }
-
- Cesium3DTileBatchTable.prototype.isClass = function (batchId, className) {
- //>>includeStart('debug', pragmas.debug);
- checkBatchId(batchId, this.featuresLength);
- Check.typeOf.string("className", className);
- //>>includeEnd('debug');
-
- const hierarchy = this._batchTableHierarchy;
- if (!defined(hierarchy)) {
- return false;
- }
-
- return hierarchy.isClass(batchId, className);
- };
-
- Cesium3DTileBatchTable.prototype.isExactClass = function (batchId, className) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.string("className", className);
- //>>includeEnd('debug');
-
- return this.getExactClassName(batchId) === className;
- };
-
- Cesium3DTileBatchTable.prototype.getExactClassName = function (batchId) {
- //>>includeStart('debug', pragmas.debug);
- checkBatchId(batchId, this.featuresLength);
- //>>includeEnd('debug');
-
- const hierarchy = this._batchTableHierarchy;
- if (!defined(hierarchy)) {
- return undefined;
- }
-
- return hierarchy.getClassName(batchId);
- };
-
- Cesium3DTileBatchTable.prototype.hasProperty = function (batchId, name) {
- //>>includeStart('debug', pragmas.debug);
- checkBatchId(batchId, this.featuresLength);
- Check.typeOf.string("name", name);
- //>>includeEnd('debug');
-
- return (
- defined(this._properties[name]) ||
- (defined(this._batchTableHierarchy) &&
- this._batchTableHierarchy.hasProperty(batchId, name))
- );
- };
-
- /**
- * @private
- */
- Cesium3DTileBatchTable.prototype.hasPropertyBySemantic = function () {
- // Cesium 3D Tiles 1.0 formats do not have semantics
- return false;
- };
-
- Cesium3DTileBatchTable.prototype.getPropertyIds = function (batchId, results) {
- //>>includeStart('debug', pragmas.debug);
- checkBatchId(batchId, this.featuresLength);
- //>>includeEnd('debug');
-
- results = defined(results) ? results : [];
- results.length = 0;
-
- const scratchPropertyIds = Object.keys(this._properties);
- addAllToArray(results, scratchPropertyIds);
-
- if (defined(this._batchTableHierarchy)) {
- const propertyIds = this._batchTableHierarchy.getPropertyIds(
- batchId,
- scratchPropertyIds,
- );
- addAllToArray(results, propertyIds);
- }
-
- return results;
- };
-
- /**
- * @private
- */
- Cesium3DTileBatchTable.prototype.getPropertyBySemantic = function (
- batchId,
- name,
- ) {
- // Cesium 3D Tiles 1.0 formats do not have semantics
- return undefined;
- };
-
- Cesium3DTileBatchTable.prototype.getProperty = function (batchId, name) {
- //>>includeStart('debug', pragmas.debug);
- checkBatchId(batchId, this.featuresLength);
- Check.typeOf.string("name", name);
- //>>includeEnd('debug');
-
- if (defined(this._batchTableBinaryProperties)) {
- const binaryProperty = this._batchTableBinaryProperties[name];
- if (defined(binaryProperty)) {
- return getBinaryProperty(binaryProperty, batchId);
- }
- }
-
- const propertyValues = this._properties[name];
- if (defined(propertyValues)) {
- return clone(propertyValues[batchId], true);
- }
-
- if (defined(this._batchTableHierarchy)) {
- const hierarchyProperty = this._batchTableHierarchy.getProperty(
- batchId,
- name,
- );
- if (defined(hierarchyProperty)) {
- return hierarchyProperty;
- }
- }
-
- return undefined;
- };
-
- Cesium3DTileBatchTable.prototype.setProperty = function (batchId, name, value) {
- const featuresLength = this.featuresLength;
- //>>includeStart('debug', pragmas.debug);
- checkBatchId(batchId, featuresLength);
- Check.typeOf.string("name", name);
- //>>includeEnd('debug');
-
- if (defined(this._batchTableBinaryProperties)) {
- const binaryProperty = this._batchTableBinaryProperties[name];
- if (defined(binaryProperty)) {
- setBinaryProperty(binaryProperty, batchId, value);
- return;
- }
- }
-
- if (defined(this._batchTableHierarchy)) {
- if (this._batchTableHierarchy.setProperty(batchId, name, value)) {
- return;
- }
- }
-
- let propertyValues = this._properties[name];
- if (!defined(propertyValues)) {
- // Property does not exist. Create it.
- this._properties[name] = new Array(featuresLength);
- propertyValues = this._properties[name];
- }
-
- propertyValues[batchId] = clone(value, true);
- };
-
- function getGlslComputeSt(batchTable) {
- // GLSL batchId is zero-based: [0, featuresLength - 1]
- if (batchTable._batchTexture.textureDimensions.y === 1) {
- return (
- "uniform vec4 tile_textureStep; \n" +
- "vec2 computeSt(float batchId) \n" +
- "{ \n" +
- " float stepX = tile_textureStep.x; \n" +
- " float centerX = tile_textureStep.y; \n" +
- " return vec2(centerX + (batchId * stepX), 0.5); \n" +
- "} \n"
- );
- }
-
- return (
- "uniform vec4 tile_textureStep; \n" +
- "uniform vec2 tile_textureDimensions; \n" +
- "vec2 computeSt(float batchId) \n" +
- "{ \n" +
- " float stepX = tile_textureStep.x; \n" +
- " float centerX = tile_textureStep.y; \n" +
- " float stepY = tile_textureStep.z; \n" +
- " float centerY = tile_textureStep.w; \n" +
- " float xId = mod(batchId, tile_textureDimensions.x); \n" +
- " float yId = floor(batchId / tile_textureDimensions.x); \n" +
- " return vec2(centerX + (xId * stepX), centerY + (yId * stepY)); \n" +
- "} \n"
- );
- }
-
- Cesium3DTileBatchTable.prototype.getVertexShaderCallback = function (
- handleTranslucent,
- batchIdAttributeName,
- diffuseAttributeOrUniformName,
- ) {
- if (this.featuresLength === 0) {
- return;
- }
-
- const that = this;
- return function (source) {
- // If the color blend mode is HIGHLIGHT, the highlight color will always be applied in the fragment shader.
- // No need to apply the highlight color in the vertex shader as well.
- const renamedSource = modifyDiffuse(
- source,
- diffuseAttributeOrUniformName,
- false,
- );
- let newMain;
-
- if (ContextLimits.maximumVertexTextureImageUnits > 0) {
- // When VTF is supported, perform per-feature show/hide in the vertex shader
- newMain = "";
- if (handleTranslucent) {
- newMain += "uniform bool tile_translucentCommand; \n";
- }
- newMain +=
- `${
- "uniform sampler2D tile_batchTexture; \n" +
- "out vec4 tile_featureColor; \n" +
- "out vec2 tile_featureSt; \n" +
- "void main() \n" +
- "{ \n" +
- " vec2 st = computeSt("
- }${batchIdAttributeName}); \n` +
- ` vec4 featureProperties = texture(tile_batchTexture, st); \n` +
- ` tile_color(featureProperties); \n` +
- ` float show = ceil(featureProperties.a); \n` + // 0 - false, non-zero - true
- ` gl_Position *= show; \n`; // Per-feature show/hide
- if (handleTranslucent) {
- newMain +=
- " bool isStyleTranslucent = (featureProperties.a != 1.0); \n" +
- " if (czm_pass == czm_passTranslucent) \n" +
- " { \n" +
- " if (!isStyleTranslucent && !tile_translucentCommand) \n" + // Do not render opaque features in the translucent pass
- " { \n" +
- " gl_Position *= 0.0; \n" +
- " } \n" +
- " } \n" +
- " else \n" +
- " { \n" +
- " if (isStyleTranslucent) \n" + // Do not render translucent features in the opaque pass
- " { \n" +
- " gl_Position *= 0.0; \n" +
- " } \n" +
- " } \n";
- }
- newMain +=
- " tile_featureColor = featureProperties; \n" +
- " tile_featureSt = st; \n" +
- "}";
- } else {
- // When VTF is not supported, color blend mode MIX will look incorrect due to the feature's color not being available in the vertex shader
- newMain =
- `${
- "out vec2 tile_featureSt; \n" +
- "void main() \n" +
- "{ \n" +
- " tile_color(vec4(1.0)); \n" +
- " tile_featureSt = computeSt("
- }${batchIdAttributeName}); \n` + `}`;
- }
-
- return `${renamedSource}\n${getGlslComputeSt(that)}${newMain}`;
- };
- };
-
- function getDefaultShader(source, applyHighlight) {
- source = ShaderSource.replaceMain(source, "tile_main");
-
- if (!applyHighlight) {
- return (
- `${source}void tile_color(vec4 tile_featureColor) \n` +
- `{ \n` +
- ` tile_main(); \n` +
- `} \n`
- );
- }
-
- // The color blend mode is intended for the RGB channels so alpha is always just multiplied.
- // out_FragColor is multiplied by the tile color only when tile_colorBlend is 0.0 (highlight)
- return (
- `${source}uniform float tile_colorBlend; \n` +
- `void tile_color(vec4 tile_featureColor) \n` +
- `{ \n` +
- ` tile_main(); \n` +
- ` tile_featureColor = czm_gammaCorrect(tile_featureColor); \n` +
- ` out_FragColor.a *= tile_featureColor.a; \n` +
- ` float highlight = ceil(tile_colorBlend); \n` +
- ` out_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n` +
- `} \n`
- );
- }
-
- function replaceDiffuseTextureCalls(source, diffuseAttributeOrUniformName) {
- const functionCall = `texture(${diffuseAttributeOrUniformName}`;
-
- let fromIndex = 0;
- let startIndex = source.indexOf(functionCall, fromIndex);
- let endIndex;
-
- while (startIndex > -1) {
- let nestedLevel = 0;
- for (let i = startIndex; i < source.length; ++i) {
- const character = source.charAt(i);
- if (character === "(") {
- ++nestedLevel;
- } else if (character === ")") {
- --nestedLevel;
- if (nestedLevel === 0) {
- endIndex = i + 1;
- break;
- }
- }
- }
- const extractedFunction = source.slice(startIndex, endIndex);
- const replacedFunction = `tile_diffuse_final(${extractedFunction}, tile_diffuse)`;
-
- source =
- source.slice(0, startIndex) + replacedFunction + source.slice(endIndex);
- fromIndex = startIndex + replacedFunction.length;
- startIndex = source.indexOf(functionCall, fromIndex);
- }
-
- return source;
- }
-
- function modifyDiffuse(source, diffuseAttributeOrUniformName, applyHighlight) {
- // If the glTF does not specify the _3DTILESDIFFUSE semantic, return the default shader.
- // Otherwise if _3DTILESDIFFUSE is defined prefer the shader below that can switch the color mode at runtime.
- if (!defined(diffuseAttributeOrUniformName)) {
- return getDefaultShader(source, applyHighlight);
- }
-
- // Find the diffuse uniform. Examples matches:
- // uniform vec3 u_diffuseColor;
- // uniform sampler2D diffuseTexture;
- let regex = new RegExp(
- `(uniform|attribute|in)\\s+(vec[34]|sampler2D)\\s+${diffuseAttributeOrUniformName};`,
- );
- const uniformMatch = source.match(regex);
-
- if (!defined(uniformMatch)) {
- // Could not find uniform declaration of type vec3, vec4, or sampler2D
- return getDefaultShader(source, applyHighlight);
- }
-
- const declaration = uniformMatch[0];
- const type = uniformMatch[2];
-
- source = ShaderSource.replaceMain(source, "tile_main");
- source = source.replace(declaration, ""); // Remove uniform declaration for now so the replace below doesn't affect it
-
- // If the tile color is white, use the source color. This implies the feature has not been styled.
- // Highlight: tile_colorBlend is 0.0 and the source color is used
- // Replace: tile_colorBlend is 1.0 and the tile color is used
- // Mix: tile_colorBlend is between 0.0 and 1.0, causing the source color and tile color to mix
- const finalDiffuseFunction =
- "bool isWhite(vec3 color) \n" +
- "{ \n" +
- " return all(greaterThan(color, vec3(1.0 - czm_epsilon3))); \n" +
- "} \n" +
- "vec4 tile_diffuse_final(vec4 sourceDiffuse, vec4 tileDiffuse) \n" +
- "{ \n" +
- " vec4 blendDiffuse = mix(sourceDiffuse, tileDiffuse, tile_colorBlend); \n" +
- " vec4 diffuse = isWhite(tileDiffuse.rgb) ? sourceDiffuse : blendDiffuse; \n" +
- " return vec4(diffuse.rgb, sourceDiffuse.a); \n" +
- "} \n";
-
- // The color blend mode is intended for the RGB channels so alpha is always just multiplied.
- // out_FragColor is multiplied by the tile color only when tile_colorBlend is 0.0 (highlight)
- const highlight =
- " tile_featureColor = czm_gammaCorrect(tile_featureColor); \n" +
- " out_FragColor.a *= tile_featureColor.a; \n" +
- " float highlight = ceil(tile_colorBlend); \n" +
- " out_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n";
-
- let setColor;
- if (type === "vec3" || type === "vec4") {
- const sourceDiffuse =
- type === "vec3"
- ? `vec4(${diffuseAttributeOrUniformName}, 1.0)`
- : diffuseAttributeOrUniformName;
- const replaceDiffuse =
- type === "vec3" ? "tile_diffuse.xyz" : "tile_diffuse";
- regex = new RegExp(diffuseAttributeOrUniformName, "g");
- source = source.replace(regex, replaceDiffuse);
- setColor =
- ` vec4 source = ${sourceDiffuse}; \n` +
- ` tile_diffuse = tile_diffuse_final(source, tile_featureColor); \n` +
- ` tile_main(); \n`;
- } else if (type === "sampler2D") {
- // Handles any number of nested parentheses
- // E.g. texture(u_diffuse, uv)
- // E.g. texture(u_diffuse, computeUV(index))
- source = replaceDiffuseTextureCalls(source, diffuseAttributeOrUniformName);
- setColor =
- " tile_diffuse = tile_featureColor; \n" + " tile_main(); \n";
- }
-
- source =
- `${
- "uniform float tile_colorBlend; \n" + "vec4 tile_diffuse = vec4(1.0); \n"
- }${finalDiffuseFunction}${declaration}\n${source}\n` +
- `void tile_color(vec4 tile_featureColor) \n` +
- `{ \n${setColor}`;
-
- if (applyHighlight) {
- source += highlight;
- }
-
- source += "} \n";
- return source;
- }
-
- Cesium3DTileBatchTable.prototype.getFragmentShaderCallback = function (
- handleTranslucent,
- diffuseAttributeOrUniformName,
- hasPremultipliedAlpha,
- ) {
- if (this.featuresLength === 0) {
- return;
- }
- return function (source) {
- source = modifyDiffuse(source, diffuseAttributeOrUniformName, true);
- if (ContextLimits.maximumVertexTextureImageUnits > 0) {
- // When VTF is supported, per-feature show/hide already happened in the fragment shader
- source +=
- "uniform sampler2D tile_pickTexture; \n" +
- "in vec2 tile_featureSt; \n" +
- "in vec4 tile_featureColor; \n" +
- "void main() \n" +
- "{ \n" +
- " tile_color(tile_featureColor); \n";
-
- if (hasPremultipliedAlpha) {
- source += " out_FragColor.rgb *= out_FragColor.a; \n";
- }
-
- source += "}";
- } else {
- if (handleTranslucent) {
- source += "uniform bool tile_translucentCommand; \n";
- }
- source +=
- "uniform sampler2D tile_pickTexture; \n" +
- "uniform sampler2D tile_batchTexture; \n" +
- "in vec2 tile_featureSt; \n" +
- "void main() \n" +
- "{ \n" +
- " vec4 featureProperties = texture(tile_batchTexture, tile_featureSt); \n" +
- " if (featureProperties.a == 0.0) { \n" + // show: alpha == 0 - false, non-zeo - true
- " discard; \n" +
- " } \n";
-
- if (handleTranslucent) {
- source +=
- " bool isStyleTranslucent = (featureProperties.a != 1.0); \n" +
- " if (czm_pass == czm_passTranslucent) \n" +
- " { \n" +
- " if (!isStyleTranslucent && !tile_translucentCommand) \n" + // Do not render opaque features in the translucent pass
- " { \n" +
- " discard; \n" +
- " } \n" +
- " } \n" +
- " else \n" +
- " { \n" +
- " if (isStyleTranslucent) \n" + // Do not render translucent features in the opaque pass
- " { \n" +
- " discard; \n" +
- " } \n" +
- " } \n";
- }
-
- source += " tile_color(featureProperties); \n";
-
- if (hasPremultipliedAlpha) {
- source += " out_FragColor.rgb *= out_FragColor.a; \n";
- }
-
- source += "} \n";
- }
- return source;
- };
- };
-
- function getColorBlend(batchTable) {
- const tileset = batchTable._content.tileset;
- const colorBlendMode = tileset.colorBlendMode;
- const colorBlendAmount = tileset.colorBlendAmount;
- if (colorBlendMode === Cesium3DTileColorBlendMode.HIGHLIGHT) {
- return 0.0;
- }
- if (colorBlendMode === Cesium3DTileColorBlendMode.REPLACE) {
- return 1.0;
- }
- if (colorBlendMode === Cesium3DTileColorBlendMode.MIX) {
- // The value 0.0 is reserved for highlight, so clamp to just above 0.0.
- return CesiumMath.clamp(colorBlendAmount, CesiumMath.EPSILON4, 1.0);
- }
- //>>includeStart('debug', pragmas.debug);
- throw new DeveloperError(`Invalid color blend mode "${colorBlendMode}".`);
- //>>includeEnd('debug');
- }
-
- Cesium3DTileBatchTable.prototype.getUniformMapCallback = function () {
- if (this.featuresLength === 0) {
- return;
- }
-
- const that = this;
- return function (uniformMap) {
- const batchUniformMap = {
- tile_batchTexture: function () {
- // PERFORMANCE_IDEA: we could also use a custom shader that avoids the texture read.
- return (
- that._batchTexture.batchTexture ?? that._batchTexture.defaultTexture
- );
- },
- tile_textureDimensions: function () {
- return that._batchTexture.textureDimensions;
- },
- tile_textureStep: function () {
- return that._batchTexture.textureStep;
- },
- tile_colorBlend: function () {
- return getColorBlend(that);
- },
- tile_pickTexture: function () {
- return that._batchTexture.pickTexture;
- },
- };
-
- return combine(uniformMap, batchUniformMap);
- };
- };
-
- Cesium3DTileBatchTable.prototype.getPickId = function () {
- return "texture(tile_pickTexture, tile_featureSt)";
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- const StyleCommandsNeeded = {
- ALL_OPAQUE: 0,
- ALL_TRANSLUCENT: 1,
- OPAQUE_AND_TRANSLUCENT: 2,
- };
-
- Cesium3DTileBatchTable.prototype.addDerivedCommands = function (
- frameState,
- commandStart,
- ) {
- const commandList = frameState.commandList;
- const commandEnd = commandList.length;
- const tile = this._content._tile;
- const finalResolution = tile._finalResolution;
- const tileset = tile.tileset;
- const bivariateVisibilityTest =
- tileset.isSkippingLevelOfDetail &&
- tileset.hasMixedContent &&
- frameState.context.stencilBuffer;
- const styleCommandsNeeded = getStyleCommandsNeeded(this);
-
- for (let i = commandStart; i < commandEnd; ++i) {
- const command = commandList[i];
- if (command.pass === Pass.COMPUTE) {
- continue;
- }
-
- let derivedCommands = command.derivedCommands.tileset;
- if (!defined(derivedCommands) || command.dirty) {
- derivedCommands = {};
- command.derivedCommands.tileset = derivedCommands;
- derivedCommands.originalCommand = deriveCommand(command);
- command.dirty = false;
- }
- const originalCommand = derivedCommands.originalCommand;
-
- if (
- styleCommandsNeeded !== StyleCommandsNeeded.ALL_OPAQUE &&
- command.pass !== Pass.TRANSLUCENT
- ) {
- if (!defined(derivedCommands.translucent)) {
- derivedCommands.translucent = deriveTranslucentCommand(originalCommand);
- }
- }
-
- if (
- styleCommandsNeeded !== StyleCommandsNeeded.ALL_TRANSLUCENT &&
- command.pass !== Pass.TRANSLUCENT
- ) {
- if (!defined(derivedCommands.opaque)) {
- derivedCommands.opaque = deriveOpaqueCommand(originalCommand);
- }
-
- if (bivariateVisibilityTest) {
- if (!finalResolution) {
- if (!defined(derivedCommands.zback)) {
- derivedCommands.zback = deriveZBackfaceCommand(
- frameState.context,
- originalCommand,
- );
- }
- tileset._backfaceCommands.push(derivedCommands.zback);
- }
- if (
- !defined(derivedCommands.stencil) ||
- tile._selectionDepth !==
- getLastSelectionDepth(derivedCommands.stencil)
- ) {
- if (command.renderState.depthMask) {
- derivedCommands.stencil = deriveStencilCommand(
- originalCommand,
- tile._selectionDepth,
- );
- } else {
- // Ignore if tile does not write depth
- derivedCommands.stencil = derivedCommands.opaque;
- }
- }
- }
- }
-
- const opaqueCommand = bivariateVisibilityTest
- ? derivedCommands.stencil
- : derivedCommands.opaque;
- const translucentCommand = derivedCommands.translucent;
-
- // If the command was originally opaque:
- // * If the styling applied to the tile is all opaque, use the opaque command
- // (with one additional uniform needed for the shader).
- // * If the styling is all translucent, use new (cached) derived commands (front
- // and back faces) with a translucent render state.
- // * If the styling causes both opaque and translucent features in this tile,
- // then use both sets of commands.
- if (command.pass !== Pass.TRANSLUCENT) {
- if (styleCommandsNeeded === StyleCommandsNeeded.ALL_OPAQUE) {
- commandList[i] = opaqueCommand;
- }
- if (styleCommandsNeeded === StyleCommandsNeeded.ALL_TRANSLUCENT) {
- commandList[i] = translucentCommand;
- }
- if (styleCommandsNeeded === StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT) {
- // PERFORMANCE_IDEA: if the tile has multiple commands, we do not know what features are in what
- // commands so this case may be overkill.
- commandList[i] = opaqueCommand;
- commandList.push(translucentCommand);
- }
- } else {
- // Command was originally translucent so no need to derive new commands;
- // as of now, a style can't change an originally translucent feature to
- // opaque since the style's alpha is modulated, not a replacement. When
- // this changes, we need to derive new opaque commands here.
- commandList[i] = originalCommand;
- }
- }
- };
-
- function getStyleCommandsNeeded(batchTable) {
- const translucentFeaturesLength =
- batchTable._batchTexture.translucentFeaturesLength;
-
- if (translucentFeaturesLength === 0) {
- return StyleCommandsNeeded.ALL_OPAQUE;
- } else if (translucentFeaturesLength === batchTable.featuresLength) {
- return StyleCommandsNeeded.ALL_TRANSLUCENT;
- }
-
- return StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT;
- }
-
- function deriveCommand(command) {
- const derivedCommand = DrawCommand.shallowClone(command);
-
- // Add a uniform to indicate if the original command was translucent so
- // the shader knows not to cull vertices that were originally transparent
- // even though their style is opaque.
- const translucentCommand = derivedCommand.pass === Pass.TRANSLUCENT;
-
- derivedCommand.uniformMap = defined(derivedCommand.uniformMap)
- ? derivedCommand.uniformMap
- : {};
- derivedCommand.uniformMap.tile_translucentCommand = function () {
- return translucentCommand;
- };
-
- return derivedCommand;
- }
-
- function deriveTranslucentCommand(command) {
- const derivedCommand = DrawCommand.shallowClone(command);
- derivedCommand.pass = Pass.TRANSLUCENT;
- derivedCommand.renderState = getTranslucentRenderState(command.renderState);
- return derivedCommand;
- }
-
- function deriveOpaqueCommand(command) {
- const derivedCommand = DrawCommand.shallowClone(command);
- derivedCommand.renderState = getOpaqueRenderState(command.renderState);
- return derivedCommand;
- }
-
- function getLogDepthPolygonOffsetFragmentShaderProgram(context, shaderProgram) {
- let shader = context.shaderCache.getDerivedShaderProgram(
- shaderProgram,
- "zBackfaceLogDepth",
- );
- if (!defined(shader)) {
- const fs = shaderProgram.fragmentShaderSource.clone();
- fs.defines = defined(fs.defines) ? fs.defines.slice(0) : [];
- fs.defines.push("POLYGON_OFFSET");
-
- shader = context.shaderCache.createDerivedShaderProgram(
- shaderProgram,
- "zBackfaceLogDepth",
- {
- vertexShaderSource: shaderProgram.vertexShaderSource,
- fragmentShaderSource: fs,
- attributeLocations: shaderProgram._attributeLocations,
- },
- );
- }
-
- return shader;
- }
-
- function deriveZBackfaceCommand(context, command) {
- // Write just backface depth of unresolved tiles so resolved stenciled tiles do not appear in front
- const derivedCommand = DrawCommand.shallowClone(command);
- const rs = clone(derivedCommand.renderState, true);
- rs.cull.enabled = true;
- rs.cull.face = CullFace.FRONT;
- // Back faces do not need to write color.
- rs.colorMask = {
- red: false,
- green: false,
- blue: false,
- alpha: false,
- };
- // Push back face depth away from the camera so it is less likely that back faces and front faces of the same tile
- // intersect and overlap. This helps avoid flickering for very thin double-sided walls.
- rs.polygonOffset = {
- enabled: true,
- factor: 5.0,
- units: 5.0,
- };
- // Set the 3D Tiles bit
- rs.stencilTest = StencilConstants.setCesium3DTileBit();
- rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
-
- derivedCommand.renderState = RenderState.fromCache(rs);
- derivedCommand.castShadows = false;
- derivedCommand.receiveShadows = false;
- derivedCommand.uniformMap = clone(command.uniformMap);
-
- const polygonOffset = new Cartesian2(5.0, 5.0);
- derivedCommand.uniformMap.u_polygonOffset = function () {
- return polygonOffset;
- };
-
- // Make the log depth depth fragment write account for the polygon offset, too.
- // Otherwise, the back face commands will cause the higher resolution
- // tiles to disappear.
- derivedCommand.shaderProgram = getLogDepthPolygonOffsetFragmentShaderProgram(
- context,
- command.shaderProgram,
- );
- return derivedCommand;
- }
-
- function deriveStencilCommand(command, reference) {
- // Tiles only draw if their selection depth is >= the tile drawn already. They write their
- // selection depth to the stencil buffer to prevent ancestor tiles from drawing on top
- const derivedCommand = DrawCommand.shallowClone(command);
- const rs = clone(derivedCommand.renderState, true);
- // Stencil test is masked to the most significant 3 bits so the reference is shifted. Writes 0 for the terrain bit
- rs.stencilTest.enabled = true;
- rs.stencilTest.mask = StencilConstants.SKIP_LOD_MASK;
- rs.stencilTest.reference =
- StencilConstants.CESIUM_3D_TILE_MASK |
- (reference << StencilConstants.SKIP_LOD_BIT_SHIFT);
- rs.stencilTest.frontFunction = StencilFunction.GREATER_OR_EQUAL;
- rs.stencilTest.frontOperation.zPass = StencilOperation.REPLACE;
- rs.stencilTest.backFunction = StencilFunction.GREATER_OR_EQUAL;
- rs.stencilTest.backOperation.zPass = StencilOperation.REPLACE;
- rs.stencilMask =
- StencilConstants.CESIUM_3D_TILE_MASK | StencilConstants.SKIP_LOD_MASK;
- derivedCommand.renderState = RenderState.fromCache(rs);
- return derivedCommand;
- }
-
- function getLastSelectionDepth(stencilCommand) {
- // Isolate the selection depth from the stencil reference.
- const reference = stencilCommand.renderState.stencilTest.reference;
- return (
- (reference & StencilConstants.SKIP_LOD_MASK) >>>
- StencilConstants.SKIP_LOD_BIT_SHIFT
- );
- }
-
- function getTranslucentRenderState(renderState) {
- const rs = clone(renderState, true);
- rs.cull.enabled = false;
- rs.depthTest.enabled = true;
- rs.depthMask = false;
- rs.blending = BlendingState.ALPHA_BLEND;
- rs.stencilTest = StencilConstants.setCesium3DTileBit();
- rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
-
- return RenderState.fromCache(rs);
- }
-
- function getOpaqueRenderState(renderState) {
- const rs = clone(renderState, true);
- rs.stencilTest = StencilConstants.setCesium3DTileBit();
- rs.stencilMask = StencilConstants.CESIUM_3D_TILE_MASK;
-
- return RenderState.fromCache(rs);
- }
-
- Cesium3DTileBatchTable.prototype.update = function (tileset, frameState) {
- this._batchTexture.update(tileset, frameState);
- };
-
- Cesium3DTileBatchTable.prototype.isDestroyed = function () {
- return false;
- };
-
- Cesium3DTileBatchTable.prototype.destroy = function () {
- this._batchTexture = this._batchTexture && this._batchTexture.destroy();
- return destroyObject(this);
- };
- export default Cesium3DTileBatchTable;
|