123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143 |
- 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 defaultValue from "../Core/defaultValue.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";
- 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 = defaultValue(extensions, {});
- const properties = initializeProperties(batchTableJson);
- this._properties = properties;
- this._batchTableHierarchy = initializeHierarchy(
- this,
- batchTableJson,
- batchTableBinary
- );
- this._batchTableBinaryProperties = getBinaryProperties(
- featuresLength,
- properties,
- batchTableBinary
- );
- 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, {
- memorySizeInBytes: {
- get: function () {
- return this._batchTexture.memorySizeInBytes;
- },
- },
- });
- 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;
- }
- 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)
- ? defaultValue(
- style.color.evaluateColor(feature, scratchColor),
- DEFAULT_COLOR_VALUE
- )
- : DEFAULT_COLOR_VALUE;
- const show = defined(style.show)
- ? defaultValue(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 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.getPropertyNames = function (
- batchId,
- results
- ) {
- //>>includeStart('debug', pragmas.debug);
- checkBatchId(batchId, this.featuresLength);
- //>>includeEnd('debug');
- results = defined(results) ? results : [];
- results.length = 0;
- const scratchPropertyNames = Object.keys(this._properties);
- results.push.apply(results, scratchPropertyNames);
- if (defined(this._batchTableHierarchy)) {
- results.push.apply(
- results,
- this._batchTableHierarchy.getPropertyIds(batchId, scratchPropertyNames)
- );
- }
- 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" +
- "varying vec4 tile_featureColor; \n" +
- "varying vec2 tile_featureSt; \n" +
- "void main() \n" +
- "{ \n" +
- " vec2 st = computeSt("
- }${batchIdAttributeName}); \n` +
- ` vec4 featureProperties = texture2D(tile_batchTexture, st); \n` +
- ` tile_color(featureProperties); \n` +
- ` float show = ceil(featureProperties.a); \n` + // 0 - false, non-zeo - 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 =
- `${
- "varying 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.
- // gl_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` +
- ` gl_FragColor.a *= tile_featureColor.a; \n` +
- ` float highlight = ceil(tile_colorBlend); \n` +
- ` gl_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n` +
- `} \n`
- );
- }
- function replaceDiffuseTextureCalls(source, diffuseAttributeOrUniformName) {
- const functionCall = `texture2D(${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.
- // gl_FragColor is multiplied by the tile color only when tile_colorBlend is 0.0 (highlight)
- const highlight =
- " tile_featureColor = czm_gammaCorrect(tile_featureColor); \n" +
- " gl_FragColor.a *= tile_featureColor.a; \n" +
- " float highlight = ceil(tile_colorBlend); \n" +
- " gl_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. texture2D(u_diffuse, uv)
- // E.g. texture2D(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" +
- "varying vec2 tile_featureSt; \n" +
- "varying vec4 tile_featureColor; \n" +
- "void main() \n" +
- "{ \n" +
- " tile_color(tile_featureColor); \n";
- if (hasPremultipliedAlpha) {
- source += " gl_FragColor.rgb *= gl_FragColor.a; \n";
- }
- source += "}";
- } else {
- if (handleTranslucent) {
- source += "uniform bool tile_translucentCommand; \n";
- }
- source +=
- "uniform sampler2D tile_pickTexture; \n" +
- "uniform sampler2D tile_batchTexture; \n" +
- "varying vec2 tile_featureSt; \n" +
- "void main() \n" +
- "{ \n" +
- " vec4 featureProperties = texture2D(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 += " gl_FragColor.rgb *= gl_FragColor.a; \n";
- }
- source += "} \n";
- }
- return source;
- };
- };
- Cesium3DTileBatchTable.prototype.getClassificationFragmentShaderCallback = function () {
- if (this.featuresLength === 0) {
- return;
- }
- return function (source) {
- source = ShaderSource.replaceMain(source, "tile_main");
- if (ContextLimits.maximumVertexTextureImageUnits > 0) {
- // When VTF is supported, per-feature show/hide already happened in the fragment shader
- source +=
- "uniform sampler2D tile_pickTexture;\n" +
- "varying vec2 tile_featureSt; \n" +
- "varying vec4 tile_featureColor; \n" +
- "void main() \n" +
- "{ \n" +
- " tile_main(); \n" +
- " gl_FragColor = tile_featureColor; \n" +
- " gl_FragColor.rgb *= gl_FragColor.a; \n" +
- "}";
- } else {
- source +=
- "uniform sampler2D tile_batchTexture; \n" +
- "uniform sampler2D tile_pickTexture;\n" +
- "varying vec2 tile_featureSt; \n" +
- "void main() \n" +
- "{ \n" +
- " tile_main(); \n" +
- " vec4 featureProperties = texture2D(tile_batchTexture, tile_featureSt); \n" +
- " if (featureProperties.a == 0.0) { \n" + // show: alpha == 0 - false, non-zeo - true
- " discard; \n" +
- " } \n" +
- " gl_FragColor = featureProperties; \n" +
- " gl_FragColor.rgb *= gl_FragColor.a; \n" +
- "} \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 defaultValue(
- 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 "texture2D(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._skipLevelOfDetail &&
- 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");
- fs.sources.unshift(
- "#ifdef GL_OES_standard_derivatives\n#extension GL_OES_standard_derivatives : enable\n#endif\n"
- );
- 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;
|