diff --git a/CHANGES.md b/CHANGES.md index 8a1b8a61a900..cfed0404a55e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,7 @@ Change Log * Fixed incorrect 3D Tiles statistics when a tile fails during processing. [#6558](https://github.com/AnalyticalGraphicsInc/cesium/pull/6558) * Fixed race condition causing intermittent crash when changing geometry show value [#3061](https://github.com/AnalyticalGraphicsInc/cesium/issues/3061) * `ProviderViewModel`s with no category are displayed in an untitled group in `BaseLayerPicker` instead of being labeled as `'Other'` [#6574](https://github.com/AnalyticalGraphicsInc/cesium/pull/6574) +* Fixed a bug causing intermittent crashes with clipping planes due to uninitialized textures. [#6576](https://github.com/AnalyticalGraphicsInc/cesium/pull/6576) * Added a workaround for clipping planes causing a picking shader compilation failure for gltf models and 3D Tilesets in Internet Explorer [#6575](https://github.com/AnalyticalGraphicsInc/cesium/issues/6575) ### 1.45 - 2018-05-01 diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js index 2f6f2a350dd0..9fb502c1e2d1 100644 --- a/Source/Scene/Batched3DModel3DTileContent.js +++ b/Source/Scene/Batched3DModel3DTileContent.js @@ -513,6 +513,12 @@ define([ this._model._clippingPlanes = (tilesetClippingPlanes.enabled && this._tile._isClipped) ? tilesetClippingPlanes : undefined; } + // If the model references a different ClippingPlaneCollection due to the tileset's collection being replaced with a + // ClippingPlaneCollection that gives this tile the same clipping status, update the model to use the new ClippingPlaneCollection. + if (defined(tilesetClippingPlanes) && defined(this._model._clippingPlanes) && this._model._clippingPlanes !== tilesetClippingPlanes) { + this._model._clippingPlanes = tilesetClippingPlanes; + } + this._model.update(frameState); // If any commands were pushed, add derived commands diff --git a/Source/Scene/ClippingPlaneCollection.js b/Source/Scene/ClippingPlaneCollection.js index f4ef53be5f8b..bcb7e0e44da3 100644 --- a/Source/Scene/ClippingPlaneCollection.js +++ b/Source/Scene/ClippingPlaneCollection.js @@ -459,7 +459,8 @@ define([ if (!defined(clippingPlanesTexture)) { // Allocate twice as much space as needed to avoid frequent texture reallocation. - requiredResolution.x *= 2; + // Allocate in the Y direction, since texture may be as wide as context texture support. + requiredResolution.y *= 2; var sampler = new Sampler({ wrapS : TextureWrap.CLAMP_TO_EDGE, @@ -502,9 +503,12 @@ define([ } if (!this._multipleDirtyPlanes) { // partial updates possible - var offsetY = Math.floor(dirtyIndex / clippingPlanesTexture.width); - var offsetX = Math.floor(dirtyIndex - offsetY * clippingPlanesTexture.width); + var offsetX = 0; + var offsetY = 0; if (useFloatTexture) { + offsetY = Math.floor(dirtyIndex / clippingPlanesTexture.width); + offsetX = Math.floor(dirtyIndex - offsetY * clippingPlanesTexture.width); + packPlanesAsFloats(this, dirtyIndex, dirtyIndex + 1); clippingPlanesTexture.copyFrom({ width : 1, @@ -512,6 +516,9 @@ define([ arrayBufferView : this._float32View }, offsetX, offsetY); } else { + offsetY = Math.floor((dirtyIndex * 2) / clippingPlanesTexture.width); + offsetX = Math.floor((dirtyIndex * 2) - offsetY * clippingPlanesTexture.width); + packPlanesAsUint8(this, dirtyIndex, dirtyIndex + 1); clippingPlanesTexture.copyFrom({ width : 2, @@ -665,6 +672,33 @@ define([ return context.floatingPointTexture; }; + /** + * Function for getting the clipping plane collection's texture resolution. + * If the ClippingPlaneCollection hasn't been updated, returns the resolution that will be + * allocated based on the current plane count. + * + * @param {ClippingPlaneCollection} clippingPlaneCollection The clipping plane collection + * @param {Context} context The rendering context + * @param {Cartesian2} result A Cartesian2 for the result. + * @returns {Cartesian2} The required resolution. + * @private + */ + ClippingPlaneCollection.getTextureResolution = function(clippingPlaneCollection, context, result) { + var texture = clippingPlaneCollection.texture; + if (defined(texture)) { + result.x = texture.width; + result.y = texture.height; + return result; + } + + var pixelsNeeded = ClippingPlaneCollection.useFloatTexture(context) ? clippingPlaneCollection.length : clippingPlaneCollection.length * 2; + var requiredResolution = computeTextureResolution(pixelsNeeded, result); + + // Allocate twice as much space as needed to avoid frequent texture reallocation. + requiredResolution.y *= 2; + return requiredResolution; + }; + /** * Returns true if this object was destroyed; otherwise, false. *

diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 49ed43895ddc..5a7a7e819bb7 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -123,7 +123,7 @@ define([ var fs = this.baseFragmentShaderSource.clone(); if (currentClippingShaderState !== 0) { - fs.sources.unshift(getClippingFunction(clippingPlanes)); // Need to go before GlobeFS + fs.sources.unshift(getClippingFunction(clippingPlanes, frameState.context)); // Need to go before GlobeFS } vs.defines.push(quantizationDefine); diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js index 01f65513809f..5354eca55985 100644 --- a/Source/Scene/Instanced3DModel3DTileContent.js +++ b/Source/Scene/Instanced3DModel3DTileContent.js @@ -520,6 +520,12 @@ define([ // Link/Dereference directly to avoid ownership checks. model._clippingPlanes = (tilesetClippingPlanes.enabled && this._tile._isClipped) ? tilesetClippingPlanes : undefined; } + + // If the model references a different ClippingPlaneCollection due to the tileset's collection being replaced with a + // ClippingPlaneCollection that gives this tile the same clipping status, update the model to use the new ClippingPlaneCollection. + if (defined(tilesetClippingPlanes) && defined(model._clippingPlanes) && model._clippingPlanes !== tilesetClippingPlanes) { + model._clippingPlanes = tilesetClippingPlanes; + } } this._modelInstanceCollection.update(frameState); diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 64132b2e194e..542c15d3acec 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -2043,7 +2043,7 @@ define([ finalFS = Model._modifyShaderForColor(finalFS, model._hasPremultipliedAlpha); } if (addClippingPlaneCode) { - finalFS = modifyShaderForClippingPlanes(finalFS, clippingPlaneCollection); + finalFS = modifyShaderForClippingPlanes(finalFS, clippingPlaneCollection, context); } var drawVS = modifyShader(vs, id, model._vertexShaderLoaded); @@ -2066,7 +2066,7 @@ define([ } if (addClippingPlaneCode) { - pickFS = modifyShaderForClippingPlanes(pickFS, clippingPlaneCollection); + pickFS = modifyShaderForClippingPlanes(pickFS, clippingPlaneCollection, context); } if (!FeatureDetection.isInternetExplorer()) { @@ -3960,9 +3960,9 @@ define([ } } - function modifyShaderForClippingPlanes(shader, clippingPlaneCollection) { + function modifyShaderForClippingPlanes(shader, clippingPlaneCollection, context) { shader = ShaderSource.replaceMain(shader, 'gltf_clip_main'); - shader += Model._getClippingFunction(clippingPlaneCollection) + '\n'; + shader += Model._getClippingFunction(clippingPlaneCollection, context) + '\n'; shader += 'uniform sampler2D gltf_clippingPlanes; \n' + 'uniform mat4 gltf_clippingPlanesMatrix; \n' + diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index 78638b9d62d3..60bf9564429b 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -1126,7 +1126,7 @@ define([ 'uniform mat4 u_clippingPlanesMatrix; \n' + 'uniform vec4 u_clippingPlanesEdgeStyle; \n'; fs += '\n'; - fs += getClippingFunction(clippingPlanes); + fs += getClippingFunction(clippingPlanes, context); fs += '\n'; } diff --git a/Source/Scene/getClippingFunction.js b/Source/Scene/getClippingFunction.js index 902eb9b87b7e..c694f7648b26 100644 --- a/Source/Scene/getClippingFunction.js +++ b/Source/Scene/getClippingFunction.js @@ -1,36 +1,43 @@ define([ + '../Core/Cartesian2', '../Core/Check', - '../Renderer/PixelDatatype' + '../Renderer/PixelDatatype', + './ClippingPlaneCollection' ], function( + Cartesian2, Check, - PixelDatatype) { + PixelDatatype, + ClippingPlaneCollection) { 'use strict'; + var textureResolutionScratch = new Cartesian2(); /** * Gets the GLSL functions needed to retrieve clipping planes from a ClippingPlaneCollection's texture. * * @param {ClippingPlaneCollection} clippingPlaneCollection ClippingPlaneCollection with a defined texture. + * @param {Context} context The current rendering context. * @returns {String} A string containing GLSL functions for retrieving clipping planes. * @private */ - function getClippingFunction(clippingPlaneCollection) { + function getClippingFunction(clippingPlaneCollection, context) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object('clippingPlaneCollection', clippingPlaneCollection); + Check.typeOf.object('context', context); //>>includeEnd('debug'); var unionClippingRegions = clippingPlaneCollection.unionClippingRegions; var clippingPlanesLength = clippingPlaneCollection.length; - var texture = clippingPlaneCollection.texture; - var usingFloatTexture = texture.pixelDatatype === PixelDatatype.FLOAT; - var width = texture.width; - var height = texture.height; + var usingFloatTexture = ClippingPlaneCollection.useFloatTexture(context); + var textureResolution = ClippingPlaneCollection.getTextureResolution(clippingPlaneCollection, context, textureResolutionScratch); + var width = textureResolution.x; + var height = textureResolution.y; var functions = usingFloatTexture ? getClippingPlaneFloat(width, height) : getClippingPlaneUint8(width, height); functions += '\n'; - functions += unionClippingRegions ? clippingFunctionUnion(usingFloatTexture, clippingPlanesLength) : clippingFunctionIntersect(usingFloatTexture, clippingPlanesLength); + functions += unionClippingRegions ? clippingFunctionUnion(clippingPlanesLength) : clippingFunctionIntersect(clippingPlanesLength); return functions; } - function clippingFunctionUnion(usingFloatTexture, clippingPlanesLength) { + function clippingFunctionUnion(clippingPlanesLength) { var functionString = 'float clip(vec4 fragCoord, sampler2D clippingPlanes, mat4 clippingPlanesMatrix)\n' + '{\n' + @@ -66,7 +73,7 @@ define([ return functionString; } - function clippingFunctionIntersect(usingFloatTexture, clippingPlanesLength) { + function clippingFunctionIntersect(clippingPlanesLength) { var functionString = 'float clip(vec4 fragCoord, sampler2D clippingPlanes, mat4 clippingPlanesMatrix)\n' + '{\n' + diff --git a/Specs/Scene/Batched3DModel3DTileContentSpec.js b/Specs/Scene/Batched3DModel3DTileContentSpec.js index b1ac299a4efb..cb9ebbf4a892 100644 --- a/Specs/Scene/Batched3DModel3DTileContentSpec.js +++ b/Specs/Scene/Batched3DModel3DTileContentSpec.js @@ -319,6 +319,39 @@ defineSuite([ }); }); + it('Links model to tileset clipping planes if tileset clipping planes are reassigned', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + var tile = tileset._root; + var model = tile.content._model; + + expect(model.clippingPlanes).toBeUndefined(); + + var clippingPlaneCollection = new ClippingPlaneCollection({ + planes : [ + new ClippingPlane(Cartesian3.UNIT_X, 0.0) + ] + }); + tileset.clippingPlanes = clippingPlaneCollection; + clippingPlaneCollection.update(scene.frameState); + tile.update(tileset, scene.frameState); + + expect(model.clippingPlanes).toBeDefined(); + expect(model.clippingPlanes).toBe(tileset.clippingPlanes); + + var newClippingPlaneCollection = new ClippingPlaneCollection({ + planes : [ + new ClippingPlane(Cartesian3.UNIT_X, 0.0) + ] + }); + tileset.clippingPlanes = newClippingPlaneCollection; + newClippingPlaneCollection.update(scene.frameState); + expect(model.clippingPlanes).not.toBe(tileset.clippingPlanes); + + tile.update(tileset, scene.frameState); + expect(model.clippingPlanes).toBe(tileset.clippingPlanes); + }); + }); + it('rebuilds Model shaders when clipping planes change', function() { spyOn(Model, '_getClippingFunction').and.callThrough(); diff --git a/Specs/Scene/ClippingPlaneCollectionSpec.js b/Specs/Scene/ClippingPlaneCollectionSpec.js index 0ebd498eaa3d..bd6717110818 100644 --- a/Specs/Scene/ClippingPlaneCollectionSpec.js +++ b/Specs/Scene/ClippingPlaneCollectionSpec.js @@ -158,8 +158,8 @@ defineSuite([ expect(packedTexture).toBeDefined(); // Two RGBA uint8 clipping planes consume 4 pixels of texture, allocation to be double that - expect(packedTexture.width).toEqual(8); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.width).toEqual(4); + expect(packedTexture.height).toEqual(2); expect(packedTexture.pixelFormat).toEqual(PixelFormat.RGBA); expect(packedTexture.pixelDatatype).toEqual(PixelDatatype.UNSIGNED_BYTE); @@ -230,21 +230,26 @@ defineSuite([ var packedTexture = clippingPlanes.texture; // Two RGBA uint8 clipping planes consume 4 pixels of texture, allocation to be double that - expect(packedTexture.width).toEqual(8); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.width).toEqual(4); + expect(packedTexture.height).toEqual(2); + // Reach capacity clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); - clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); + clippingPlanes.update(scene.frameState); + expect(packedTexture.isDestroyed()).toBe(false); + + // Exceed capacity + clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); clippingPlanes.update(scene.frameState); expect(packedTexture.isDestroyed()).toBe(true); packedTexture = clippingPlanes.texture; // Five RGBA uint8 clipping planes consume 10 pixels of texture, allocation to be double that - expect(packedTexture.width).toEqual(20); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.width).toEqual(10); + expect(packedTexture.height).toEqual(2); clippingPlanes.removeAll(); clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); @@ -255,8 +260,58 @@ defineSuite([ packedTexture = clippingPlanes.texture; // One RGBA uint8 clipping plane consume 2 pixels of texture, allocation to be double that + expect(packedTexture.width).toEqual(2); + expect(packedTexture.height).toEqual(2); + + clippingPlanes.destroy(); + scene.destroyForSpecs(); + }); + + it('performs partial updates when only a single plane has changed and full texture updates otherwise', function() { + var scene = createScene(); + var gl = scene.frameState.context._gl; + var copyWidth; + var copyHeight; + spyOn(gl, 'texSubImage2D').and.callFake(function(target, level, xoffset, yoffset, width, height, format, type, arrayBufferView) { + copyWidth = width; + copyHeight = height; + }); + + clippingPlanes = new ClippingPlaneCollection({ + planes : planes, + enabled : false, + edgeColor : Color.RED, + modelMatrix : transform + }); + + clippingPlanes.update(scene.frameState); + + // Two RGBA uint8 clipping planes consume 4 pixels of texture, allocation to be double that + var packedTexture = clippingPlanes.texture; expect(packedTexture.width).toEqual(4); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.height).toEqual(2); + + var targetPlane = new ClippingPlane(Cartesian3.UNIT_X, 1.0); + clippingPlanes.add(targetPlane); + clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); + clippingPlanes.update(scene.frameState); + + // Haven't hit limit yet + expect(packedTexture.isDestroyed()).toBe(false); + + // Addition of two planes, expect a full texture update + expect(gl.texSubImage2D.calls.count()).toEqual(1); + expect(copyWidth).toEqual(packedTexture.width); + expect(copyHeight).toEqual(packedTexture.height); + + // Move target plane for partial update + targetPlane.distance += 1.0; + clippingPlanes.update(scene.frameState); + + expect(packedTexture.isDestroyed()).toBe(false); + expect(gl.texSubImage2D.calls.count()).toEqual(2); + expect(copyWidth).toEqual(2); + expect(copyHeight).toEqual(1); clippingPlanes.destroy(); scene.destroyForSpecs(); @@ -284,8 +339,8 @@ defineSuite([ var packedTexture = clippingPlanes.texture; expect(packedTexture).toBeDefined(); - expect(packedTexture.width).toEqual(4); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.width).toEqual(2); + expect(packedTexture.height).toEqual(2); expect(packedTexture.pixelFormat).toEqual(PixelFormat.RGBA); expect(packedTexture.pixelDatatype).toEqual(PixelDatatype.FLOAT); @@ -362,21 +417,26 @@ defineSuite([ var packedTexture = clippingPlanes.texture; // Two RGBA float clipping planes consume 2 pixels of texture, allocation to be double that - expect(packedTexture.width).toEqual(4); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.width).toEqual(2); + expect(packedTexture.height).toEqual(2); + // Reach capacity clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); - clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); + clippingPlanes.update(scene.frameState); + + expect(packedTexture.isDestroyed()).toBe(false); + // Exceed capacity + clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); clippingPlanes.update(scene.frameState); expect(packedTexture.isDestroyed()).toBe(true); packedTexture = clippingPlanes.texture; // Five RGBA float clipping planes consume 5 pixels of texture, allocation to be double that - expect(packedTexture.width).toEqual(10); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.width).toEqual(5); + expect(packedTexture.height).toEqual(2); clippingPlanes.removeAll(); clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); @@ -387,8 +447,65 @@ defineSuite([ packedTexture = clippingPlanes.texture; // One RGBA float clipping plane consume 1 pixels of texture, allocation to be double that + expect(packedTexture.width).toEqual(1); + expect(packedTexture.height).toEqual(2); + + clippingPlanes.destroy(); + scene.destroyForSpecs(); + }); + + it('performs partial updates when only a single plane has changed and full texture updates otherwise', function() { + var scene = createScene(); + + if (!ClippingPlaneCollection.useFloatTexture(scene._context)) { + // Don't fail just because float textures aren't supported + scene.destroyForSpecs(); + return; + } + + var gl = scene.frameState.context._gl; + var copyWidth; + var copyHeight; + spyOn(gl, 'texSubImage2D').and.callFake(function(target, level, xoffset, yoffset, width, height, format, type, arrayBufferView) { + copyWidth = width; + copyHeight = height; + }); + + clippingPlanes = new ClippingPlaneCollection({ + planes : planes, + enabled : false, + edgeColor : Color.RED, + modelMatrix : transform + }); + + clippingPlanes.update(scene.frameState); + + // Two RGBA Float clipping planes consume 2 pixels of texture, allocation to be double that + var packedTexture = clippingPlanes.texture; expect(packedTexture.width).toEqual(2); - expect(packedTexture.height).toEqual(1); + expect(packedTexture.height).toEqual(2); + + var targetPlane = new ClippingPlane(Cartesian3.UNIT_X, 1.0); + clippingPlanes.add(targetPlane); + clippingPlanes.add(new ClippingPlane(Cartesian3.UNIT_X, 1.0)); + clippingPlanes.update(scene.frameState); + + // Haven't hit limit yet + expect(packedTexture.isDestroyed()).toBe(false); + + // Addition of two planes, expect a full texture update + expect(gl.texSubImage2D.calls.count()).toEqual(1); + expect(copyWidth).toEqual(packedTexture.width); + expect(copyHeight).toEqual(packedTexture.height); + + // Move target plane for partial update + targetPlane.distance += 1.0; + clippingPlanes.update(scene.frameState); + + expect(packedTexture.isDestroyed()).toBe(false); + expect(gl.texSubImage2D.calls.count()).toEqual(2); + expect(copyWidth).toEqual(1); + expect(copyHeight).toEqual(1); clippingPlanes.destroy(); scene.destroyForSpecs(); @@ -596,4 +713,31 @@ defineSuite([ clippingPlanes.remove(holdThisPlane); expect(clippingPlanes.clippingPlanesState).toEqual(1); }); + + it('provides a function for checking the texture resolution', function() { + spyOn(ClippingPlaneCollection, 'useFloatTexture').and.returnValue(false); + + var scene = createScene(); + clippingPlanes = new ClippingPlaneCollection({ + planes : planes, + enabled : false, + edgeColor : Color.RED, + modelMatrix : transform + }); + + // Predicted resolution before texture has been allocated + var predictedResolution = ClippingPlaneCollection.getTextureResolution(clippingPlanes, scene.frameState.context, new Cartesian2()); + + expect(predictedResolution.x).toEqual(4); + expect(predictedResolution.y).toEqual(2); + + clippingPlanes.update(scene.frameState); + var actualResolution = ClippingPlaneCollection.getTextureResolution(clippingPlanes, scene.frameState.context, new Cartesian2()); + + expect(predictedResolution.x).toEqual(actualResolution.x); + expect(predictedResolution.y).toEqual(actualResolution.y); + + clippingPlanes.destroy(); + scene.destroyForSpecs(); + }); }); diff --git a/Specs/Scene/Instanced3DModel3DTileContentSpec.js b/Specs/Scene/Instanced3DModel3DTileContentSpec.js index c3626c773e57..dbc68181d84c 100644 --- a/Specs/Scene/Instanced3DModel3DTileContentSpec.js +++ b/Specs/Scene/Instanced3DModel3DTileContentSpec.js @@ -334,6 +334,39 @@ defineSuite([ }); }); + it('Links model to tileset clipping planes if tileset clipping planes are reassigned', function() { + return Cesium3DTilesTester.loadTileset(scene, withBatchTableUrl).then(function(tileset) { + var tile = tileset._root; + var model = tile.content._modelInstanceCollection._model; + + expect(model.clippingPlanes).toBeUndefined(); + + var clippingPlaneCollection = new ClippingPlaneCollection({ + planes : [ + new ClippingPlane(Cartesian3.UNIT_X, 0.0) + ] + }); + tileset.clippingPlanes = clippingPlaneCollection; + clippingPlaneCollection.update(scene.frameState); + tile.update(tileset, scene.frameState); + + expect(model.clippingPlanes).toBeDefined(); + expect(model.clippingPlanes).toBe(tileset.clippingPlanes); + + var newClippingPlaneCollection = new ClippingPlaneCollection({ + planes : [ + new ClippingPlane(Cartesian3.UNIT_X, 0.0) + ] + }); + tileset.clippingPlanes = newClippingPlaneCollection; + newClippingPlaneCollection.update(scene.frameState); + expect(model.clippingPlanes).not.toBe(tileset.clippingPlanes); + + tile.update(tileset, scene.frameState); + expect(model.clippingPlanes).toBe(tileset.clippingPlanes); + }); + }); + it('rebuilds Model shaders when clipping planes change', function() { spyOn(Model, '_getClippingFunction').and.callThrough();