diff --git a/Apps/Sandcastle/gallery/3D Tiles Clipping Planes.html b/Apps/Sandcastle/gallery/3D Tiles Clipping Planes.html index a2d7a1c8a274..9f30816a1d26 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Clipping Planes.html +++ b/Apps/Sandcastle/gallery/3D Tiles Clipping Planes.html @@ -104,11 +104,10 @@ } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); -var scratchPlane = new Cesium.ClippingPlane(Cesium.Cartesian3.UNIT_X, 0.0); -function createPlaneUpdateFunction(plane, transform) { +function createPlaneUpdateFunction(plane) { return function () { plane.distance = targetY; - return Cesium.Plane.transform(plane, transform, scratchPlane); + return plane; }; } @@ -116,7 +115,7 @@ function loadTileset(url) { clippingPlanes = new Cesium.ClippingPlaneCollection({ planes : [ - new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), -100.0) + new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 0.0) ], edgeWidth : viewModel.edgeStylingEnabled ? 1.0 : 0.0 }); @@ -140,7 +139,7 @@ plane : { dimensions : new Cesium.Cartesian2(radius * 2.5, radius * 2.5), material : Cesium.Color.WHITE.withAlpha(0.1), - plane : new Cesium.CallbackProperty(createPlaneUpdateFunction(plane, tileset.modelMatrix), false), + plane : new Cesium.CallbackProperty(createPlaneUpdateFunction(plane), false), outline : true, outlineColor : Cesium.Color.WHITE } @@ -157,7 +156,7 @@ function loadModel(url) { clippingPlanes = new Cesium.ClippingPlaneCollection({ planes : [ - new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), -100.0) + new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 0.0) ], edgeWidth : viewModel.edgeStylingEnabled ? 1.0 : 0.0 }); @@ -189,7 +188,7 @@ plane : { dimensions : new Cesium.Cartesian2(300.0, 300.0), material : Cesium.Color.WHITE.withAlpha(0.1), - plane : new Cesium.CallbackProperty(createPlaneUpdateFunction(plane, Cesium.Matrix4.IDENTITY), false), + plane : new Cesium.CallbackProperty(createPlaneUpdateFunction(plane), false), outline : true, outlineColor : Cesium.Color.WHITE } @@ -218,13 +217,9 @@ if (newValue === clipObjects[0]) { loadTileset(bimUrl); } else if (newValue === clipObjects[1]) { - loadTileset(pointCloudUrl).then(function(tileset) { - tileset.clippingPlanes.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(tileset.boundingSphere.center); - }); + loadTileset(pointCloudUrl); } else if (newValue === clipObjects[2]) { - loadTileset(instancedUrl).then(function(tileset) { - tileset.clippingPlanes.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(tileset.boundingSphere.center); - }); + loadTileset(instancedUrl); } else { loadModel(modelUrl); } diff --git a/Apps/Sandcastle/gallery/Terrain Clipping Planes.html b/Apps/Sandcastle/gallery/Terrain Clipping Planes.html index d0347d0a1501..40ac90b42d79 100644 --- a/Apps/Sandcastle/gallery/Terrain Clipping Planes.html +++ b/Apps/Sandcastle/gallery/Terrain Clipping Planes.html @@ -52,7 +52,7 @@ }); var globe = viewer.scene.globe; -var exampleTypes = ['Cesium Man', 'St. Helens']; +var exampleTypes = ['Cesium Man', 'St. Helens', 'Grand Canyon Isolated']; var viewModel = { exampleTypes : exampleTypes, currentExampleType : exampleTypes[0], @@ -191,6 +191,27 @@ }); } +function loadGrandCanyon(){ + // Pick a position at the Grand Canyon + var position = Cesium.Cartographic.toCartesian(new Cesium.Cartographic.fromDegrees(-113.2665534, 36.0939345, 100)); + var distance = 3000.0; + var boundingSphere = new Cesium.BoundingSphere(position, distance); + + globe.clippingPlanes = new Cesium.ClippingPlaneCollection({ + modelMatrix : Cesium.Transforms.eastNorthUpToFixedFrame(position), + planes : [ + new Cesium.ClippingPlane(new Cesium.Cartesian3( 1.0, 0.0, 0.0), distance), + new Cesium.ClippingPlane(new Cesium.Cartesian3(-1.0, 0.0, 0.0), distance), + new Cesium.ClippingPlane(new Cesium.Cartesian3( 0.0, 1.0, 0.0), distance), + new Cesium.ClippingPlane(new Cesium.Cartesian3( 0.0, -1.0, 0.0), distance) + ], + unionClippingRegions : true + }); + + viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0.5, -0.5, boundingSphere.radius * 5.0)); + viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); +} + Cesium.knockout.getObservable(viewModel, 'clippingPlanesEnabled').subscribe(function(value) { globe.clippingPlanes.enabled = value; clippingPlanesEnabled = value; @@ -207,6 +228,8 @@ loadCesiumMan(); } else if (newValue === exampleTypes[1]) { loadStHelens(); + } else if (newValue === exampleTypes[2]) { + loadGrandCanyon(); } }); diff --git a/Apps/Sandcastle/gallery/development/Many Clipping Planes.html b/Apps/Sandcastle/gallery/development/Many Clipping Planes.html index 46cb523a11e2..47a6d19cf34d 100644 --- a/Apps/Sandcastle/gallery/development/Many Clipping Planes.html +++ b/Apps/Sandcastle/gallery/development/Many Clipping Planes.html @@ -226,9 +226,7 @@ }); } else if (newValue === clipObjects[3]) { // i3dm - loadTileset(instancedUrl, 100.0).then(function() { - tileset.clippingPlanes.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(tileset.boundingSphere.center); - }); + loadTileset(instancedUrl, 100.0); } else if (newValue === clipObjects[4]) { // Terrain var position = Cesium.Cartesian3.fromRadians(-2.0872979473351286, 0.6596620013036164, 2380.0); diff --git a/CHANGES.md b/CHANGES.md index bf2ba5572d65..466c0803d678 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,14 @@ Change Log ### 1.50 - 2018-10-01 +##### Breaking Changes :mega: +* Clipping planes on tilesets now use the root tile's transform, or the root tile's bounding sphere if a transform is not defined. [#7034](https://github.com/AnalyticalGraphicsInc/cesium/pull/7034) + * This is to make clipping planes' coordinates always relative to the object they're attached to. So if you were positioning the clipping planes as in the example below, this is no longer necessary: + ```javascript + clippingPlanes.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(tileset.boundingSphere.center); + ``` + * This also fixes several issues with clipping planes not using the correct transform for tilesets with children. + ##### Additions :tada: * Added `cartographicLimitRectangle` to `Globe`. Use this to limit terrain and imagery to a specific `Rectangle` area. [#6987](https://github.com/AnalyticalGraphicsInc/cesium/pull/6987) diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js index f3e291eaaa40..7566e3a67850 100644 --- a/Source/Scene/Batched3DModel3DTileContent.js +++ b/Source/Scene/Batched3DModel3DTileContent.js @@ -9,11 +9,11 @@ define([ '../Core/destroyObject', '../Core/DeveloperError', '../Core/FeatureDetection', - '../Core/getBaseUri', '../Core/getStringFromTypedArray', '../Core/Matrix4', '../Core/RequestType', '../Core/RuntimeError', + '../Core/Transforms', '../Renderer/Pass', './Axis', './Cesium3DTileBatchTable', @@ -33,11 +33,11 @@ define([ destroyObject, DeveloperError, FeatureDetection, - getBaseUri, getStringFromTypedArray, Matrix4, RequestType, RuntimeError, + Transforms, Pass, Axis, Cesium3DTileBatchTable, @@ -355,10 +355,10 @@ define([ primitive : tileset }; - content._rtcCenterTransform = Matrix4.clone(Matrix4.IDENTITY); + content._rtcCenterTransform = Matrix4.IDENTITY; var rtcCenter = featureTable.getGlobalProperty('RTC_CENTER', ComponentDatatype.FLOAT, 3); if (defined(rtcCenter)) { - content._rtcCenterTransform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter), content._rtcCenterTransform); + content._rtcCenterTransform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); } content._contentModelMatrix = Matrix4.multiply(tile.computedTransform, content._rtcCenterTransform, new Matrix4()); @@ -465,6 +465,7 @@ define([ // Update clipping planes var tilesetClippingPlanes = this._tileset.clippingPlanes; if (this._tile.clippingPlanesDirty && defined(tilesetClippingPlanes)) { + this._model.clippingPlaneOffsetMatrix = this._tileset.clippingPlaneOffsetMatrix; // Dereference the clipping planes from the model if they are irrelevant. // Link/Dereference directly to avoid ownership checks. // This will also trigger synchronous shader regeneration to remove or add the clipping plane and color blending code. diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 2e24ba16c617..113fc0d7acc2 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -24,6 +24,7 @@ define([ '../Core/RequestType', '../Core/Resource', '../Core/RuntimeError', + '../Core/Transforms', '../ThirdParty/when', './Cesium3DTileContentFactory', './Cesium3DTileContentState', @@ -60,6 +61,7 @@ define([ RequestType, Resource, RuntimeError, + Transforms, when, Cesium3DTileContentFactory, Cesium3DTileContentState, @@ -199,7 +201,6 @@ define([ Cesium3DTile._deprecationWarning('contentUrl', 'This tileset JSON uses the "content.url" property which has been deprecated. Use "content.uri" instead.'); contentHeaderUri = contentHeader.url; } - hasEmptyContent = false; contentState = Cesium3DTileContentState.UNLOADED; contentResource = baseResource.getDerivedResource({ @@ -333,7 +334,6 @@ define([ this._priority = 0.0; this._isClipped = true; this._clippingPlanesState = 0; // encapsulates (_isClipped, clippingPlanes.enabled) and number/function - this._debugBoundingVolume = undefined; this._debugContentBoundingVolume = undefined; this._debugViewerRequestVolume = undefined; @@ -702,7 +702,6 @@ define([ } var contentFailedFunction = getContentFailedFunction(this); - promise.then(function(arrayBuffer) { if (that.isDestroyed()) { // Tile is unloaded before the content finishes loading @@ -821,8 +820,7 @@ define([ var tileset = this._tileset; var clippingPlanes = tileset.clippingPlanes; if (defined(clippingPlanes) && clippingPlanes.enabled) { - var tileTransform = tileset.root.computedTransform; - var intersection = clippingPlanes.computeIntersectionWithBoundingVolume(boundingVolume, tileTransform); + var intersection = clippingPlanes.computeIntersectionWithBoundingVolume(boundingVolume, tileset.clippingPlaneOffsetMatrix); this._isClipped = intersection !== Intersect.INSIDE; if (intersection === Intersect.OUTSIDE) { return CullingVolume.MASK_OUTSIDE; @@ -856,8 +854,7 @@ define([ var tileset = this._tileset; var clippingPlanes = tileset.clippingPlanes; if (defined(clippingPlanes) && clippingPlanes.enabled) { - var tileTransform = tileset.root.computedTransform; - var intersection = clippingPlanes.computeIntersectionWithBoundingVolume(boundingVolume, tileTransform); + var intersection = clippingPlanes.computeIntersectionWithBoundingVolume(boundingVolume, tileset.clippingPlaneOffsetMatrix); this._isClipped = intersection !== Intersect.INSIDE; if (intersection === Intersect.OUTSIDE) { return Intersect.OUTSIDE; diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index 604742192531..eee397109f6b 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -18,6 +18,7 @@ define([ '../Core/Matrix4', '../Core/Resource', '../Core/RuntimeError', + '../Core/Transforms', '../Renderer/ClearCommand', '../Renderer/Pass', '../ThirdParty/when', @@ -60,6 +61,7 @@ define([ Matrix4, Resource, RuntimeError, + Transforms, ClearCommand, Pass, when, @@ -209,6 +211,9 @@ define([ this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); + this._useBoundingSphereForClipping = false; + this._clippingPlaneOffsetMatrix = undefined; + /** * Optimization option. Whether the tileset should refine based on a dynamic screen space error. Tiles that are further * away will be rendered with lower detail than closer tiles. This improves performance by rendering fewer @@ -697,6 +702,10 @@ define([ that._extensionsUsed = tilesetJson.extensionsUsed; that._gltfUpAxis = gltfUpAxis; that._extras = tilesetJson.extras; + if (!defined(tilesetJson.root.transform)) { + that._useBoundingSphereForClipping = true; + that._clippingPlaneOffsetMatrix = Transforms.eastNorthUpToFixedFrame(that.boundingSphere.center); + } that._readyPromise.resolve(that); }).otherwise(function(error) { that._readyPromise.reject(error); @@ -1107,6 +1116,18 @@ define([ } }, + /** + * @private + */ + clippingPlaneOffsetMatrix : { + get : function() { + if (this._useBoundingSphereForClipping) { + return this._clippingPlaneOffsetMatrix; + } + return this.root.computedTransform; + } + }, + /** * @private */ @@ -1478,7 +1499,6 @@ define([ filterProcessingQueue(tileset); var tiles = tileset._processingQueue; var length = tiles.length; - // Process tiles in the PROCESSING state so they will eventually move to the READY state. for (var i = 0; i < length; ++i) { tiles[i].process(tileset, frameState); @@ -1809,6 +1829,9 @@ define([ var clippingPlanes = this._clippingPlanes; if (defined(clippingPlanes) && clippingPlanes.enabled) { clippingPlanes.update(frameState); + if (this._useBoundingSphereForClipping) { + this._clippingPlaneOffsetMatrix = Transforms.eastNorthUpToFixedFrame(this.boundingSphere.center); + } } this._timeSinceLoad = Math.max(JulianDate.secondsDifference(frameState.time, this._loadTimestamp) * 1000, 0.0); diff --git a/Source/Scene/ClippingPlaneCollection.js b/Source/Scene/ClippingPlaneCollection.js index 01a3d88dac1e..31d9265dff91 100644 --- a/Source/Scene/ClippingPlaneCollection.js +++ b/Source/Scene/ClippingPlaneCollection.js @@ -55,6 +55,13 @@ define([ /** * Specifies a set of clipping planes. Clipping planes selectively disable rendering in a region on the * outside of the specified list of {@link ClippingPlane} objects for a single gltf model, 3D Tileset, or the globe. + *
+ * In general the clipping planes' coordinates are relative to the object they're attached to, so a plane with distance set to 0 will clip + * through the center of the object. + *
+ *+ * For 3D Tiles, the root tile's transform is used to position the clipping planes. If a transform is not defined, the root tile's {@link Cesium3DTile#boundingSphere} is used instead. + *
* * @alias ClippingPlaneCollection * @constructor @@ -63,9 +70,33 @@ define([ * @param {ClippingPlane[]} [options.planes=[]] An array of {@link ClippingPlane} objects used to selectively disable rendering on the outside of each plane. * @param {Boolean} [options.enabled=true] Determines whether the clipping planes are active. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix specifying an additional transform relative to the clipping planes original coordinate system. - * @param {Boolean} [options.unionClippingRegions=false] If true, a region will be clipped if included in any plane in the collection. Otherwise, the region to be clipped must intersect the regions defined by all planes in this collection. + * @param {Boolean} [options.unionClippingRegions=false] If true, a region will be clipped if it is on the outside of any plane in the collection. Otherwise, a region will only be clipped if it is on the outside of every plane. * @param {Color} [options.edgeColor=Color.WHITE] The color applied to highlight the edge along which an object is clipped. * @param {Number} [options.edgeWidth=0.0] The width, in pixels, of the highlight applied to the edge along which an object is clipped. + * + * @demo {@link https://cesiumjs.org/Cesium/Build/Apps/Sandcastle/?src=3D%20Tiles%20Clipping%20Planes.html|Clipping 3D Tiles and glTF models.} + * @demo {@link https://cesiumjs.org/Cesium/Build/Apps/Sandcastle/?src=Terrain%20Clipping%20Planes.html|Clipping the Globe.} + * + * @example + * // This clipping plane's distance is positive, which means its normal + * // is facing the origin. This will clip everything that is behind + * // the plane, which is anything with y coordinate < -5. + * var clippingPlanes = new Cesium.ClippingPlaneCollection({ + * planes : [ + * new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 1.0, 0.0), 5.0) + * ], + * }); + * // Create an entity and attach the ClippingPlaneCollection to the model. + * var entity = viewer.entities.add({ + * position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 10000), + * model : { + * uri : 'model.gltf', + * minimumPixelSize : 128, + * maximumScale : 20000, + * clippingPlanes : clippingPlanes + * } + * }); + * viewer.zoomTo(entity); */ function ClippingPlaneCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); @@ -168,8 +199,9 @@ define([ }, /** - * If true, a region will be clipped if included in any plane in the collection. Otherwise, the region - * to be clipped must intersect the regions defined by all planes in this collection. + * If true, a region will be clipped if it is on the outside of any plane in the + * collection. Otherwise, a region will only be clipped if it is on the + * outside of every plane. * * @memberof ClippingPlaneCollection.prototype * @type {Boolean} @@ -587,7 +619,7 @@ define([ var modelMatrix = this.modelMatrix; if (defined(transform)) { - modelMatrix = Matrix4.multiply(modelMatrix, transform, scratchMatrix); + modelMatrix = Matrix4.multiply(transform, modelMatrix, scratchMatrix); } // If the collection is not set to union the clipping regions, the volume must be outside of all planes to be diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js index 96a8711e1559..d10ef66f480e 100644 --- a/Source/Scene/Instanced3DModel3DTileContent.js +++ b/Source/Scene/Instanced3DModel3DTileContent.js @@ -472,6 +472,7 @@ define([ // Update for clipping planes var tilesetClippingPlanes = this._tileset.clippingPlanes; if (this._tile.clippingPlanesDirty && defined(tilesetClippingPlanes)) { + model.clippingPlaneOffsetMatrix = this._tileset.clippingPlaneOffsetMatrix; // Dereference the clipping planes from the model if they are irrelevant - saves on shading // Link/Dereference directly to avoid ownership checks. model._clippingPlanes = (tilesetClippingPlanes.enabled && this._tile._isClipped) ? tilesetClippingPlanes : undefined; diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 22b36ad03ab4..3d23693c3e1a 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -539,6 +539,10 @@ define([ this.clippingPlanes = options.clippingPlanes; // Used for checking if shaders need to be regenerated due to clipping plane changes. this._clippingPlanesState = 0; + // If defined, use this matrix to position the clipping planes instead of the modelMatrix. + // This is so that when models are part of a tileset they all get clipped relative + // to the root tile. + this.clippingPlaneOffsetMatrix = undefined; /** * This property is for debugging only; it is not for production use nor is it optimized. @@ -594,7 +598,7 @@ define([ this.opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE); this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale - this._modelViewMatrix = Matrix4.clone(Matrix4.IDENTITY); // Derived from modelMatrix, scale, and the current view matrix + this._clippingPlaneModelViewMatrix = Matrix4.clone(Matrix4.IDENTITY); // Derived from modelMatrix, scale, and the current view matrix this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins this._boundingSphere = undefined; this._scaledBoundingSphere = new BoundingSphere(); @@ -3009,7 +3013,7 @@ define([ if (!defined(clippingPlanes)) { return Matrix4.IDENTITY; } - return Matrix4.multiply(model._modelViewMatrix, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix); + return Matrix4.multiply(model._clippingPlaneModelViewMatrix, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix); }; } @@ -4426,7 +4430,8 @@ define([ var clippingPlanes = this._clippingPlanes; var currentClippingPlanesState = 0; if (defined(clippingPlanes) && clippingPlanes.enabled) { - Matrix4.multiply(context.uniformState.view3D, modelMatrix, this._modelViewMatrix); + var clippingPlaneOffsetMatrix = defaultValue(this.clippingPlaneOffsetMatrix, modelMatrix); + Matrix4.multiply(context.uniformState.view3D, clippingPlaneOffsetMatrix, this._clippingPlaneModelViewMatrix); currentClippingPlanesState = clippingPlanes.clippingPlanesState; } diff --git a/Source/Scene/PointCloud.js b/Source/Scene/PointCloud.js index 6623383bcf69..2edd8c05735d 100644 --- a/Source/Scene/PointCloud.js +++ b/Source/Scene/PointCloud.js @@ -185,6 +185,10 @@ define([ this.clippingPlanes = undefined; this.isClipped = false; this.clippingPlanesDirty = false; + // If defined, use this matrix to position the clipping planes instead of the modelMatrix. + // This is so that when point clouds are part of a tileset they all get clipped relative + // to the root tile. + this.clippingPlaneOffsetMatrix = undefined; this.attenuation = false; this._attenuation = false; @@ -819,8 +823,10 @@ define([ if (!defined(clippingPlanes)) { return Matrix4.IDENTITY; } - var modelViewMatrix = Matrix4.multiply(context.uniformState.view3D, pointCloud._modelMatrix, scratchClippingPlaneMatrix); - return Matrix4.multiply(modelViewMatrix, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix); + + var clippingPlaneOffsetMatrix = defaultValue(pointCloud.clippingPlaneOffsetMatrix, pointCloud._modelMatrix); + Matrix4.multiply(context.uniformState.view3D, clippingPlaneOffsetMatrix, scratchClippingPlaneMatrix); + return Matrix4.multiply(scratchClippingPlaneMatrix, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix); } }; diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index 83e929963023..6ef13b2dc634 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -297,6 +297,8 @@ define([ var styleDirty = this._styleDirty; this._styleDirty = false; + pointCloud.clippingPlaneOffsetMatrix = tileset.clippingPlaneOffsetMatrix; + pointCloud.style = defined(batchTable) ? undefined : tileset.style; pointCloud.styleDirty = styleDirty; pointCloud.modelMatrix = tile.computedTransform; diff --git a/Source/Scene/getClippingFunction.js b/Source/Scene/getClippingFunction.js index c694f7648b26..23bab2675487 100644 --- a/Source/Scene/getClippingFunction.js +++ b/Source/Scene/getClippingFunction.js @@ -44,7 +44,7 @@ define([ ' vec4 position = czm_windowToEyeCoordinates(fragCoord);\n' + ' vec3 clipNormal = vec3(0.0);\n' + ' vec3 clipPosition = vec3(0.0);\n' + - ' float clipAmount = 0.0;\n' + + ' float clipAmount;\n' + // For union planes, we want to get the min distance. So we set the initial value to the first plane distance in the loop below. ' float pixelWidth = czm_metersPerPixel(position);\n' + ' bool breakAndDiscard = false;\n' + @@ -56,7 +56,7 @@ define([ ' clipPosition = -clippingPlane.w * clipNormal;\n' + ' float amount = dot(clipNormal, (position.xyz - clipPosition)) / pixelWidth;\n' + - ' clipAmount = max(amount, clipAmount);\n' + + ' clipAmount = czm_branchFreeTernary(i == 0, amount, min(amount, clipAmount));\n' + ' if (amount <= 0.0)\n' + ' {\n' + diff --git a/Specs/Cesium3DTilesTester.js b/Specs/Cesium3DTilesTester.js index ab7b62618e7c..8a959d8cf1db 100644 --- a/Specs/Cesium3DTilesTester.js +++ b/Specs/Cesium3DTilesTester.js @@ -132,7 +132,8 @@ define([ var tileset = { _statistics : { batchTableByteLength : 0 - } + }, + root : {} }; var url = Resource.createIfNeeded(''); var content = Cesium3DTileContentFactory[type](tileset, mockTile, url, arrayBuffer, 0); diff --git a/Specs/Scene/Cesium3DTilesetSpec.js b/Specs/Scene/Cesium3DTilesetSpec.js index 204ae8911bc9..b572ad57003f 100644 --- a/Specs/Scene/Cesium3DTilesetSpec.js +++ b/Specs/Scene/Cesium3DTilesetSpec.js @@ -15,6 +15,7 @@ defineSuite([ 'Core/PrimitiveType', 'Core/RequestScheduler', 'Core/Resource', + 'Core/Transforms', 'Renderer/ClearCommand', 'Renderer/ContextLimits', 'Scene/Cesium3DTile', @@ -46,6 +47,7 @@ defineSuite([ PrimitiveType, RequestScheduler, Resource, + Transforms, ClearCommand, ContextLimits, Cesium3DTile, @@ -3122,7 +3124,9 @@ defineSuite([ tileset.update(scene.frameState); - var plane = new ClippingPlane(Cartesian3.UNIT_Z, 0.0); + var radius = 287.0736139905632; + + var plane = new ClippingPlane(Cartesian3.UNIT_X, radius); tileset.clippingPlanes = new ClippingPlaneCollection({ planes : [ plane @@ -3135,7 +3139,7 @@ defineSuite([ expect(statistics.numberOfCommands).toEqual(5); expect(root._isClipped).toBe(false); - plane.distance = -4081630.311150717; // center + plane.distance = -1; tileset.update(scene.frameState); scene.renderForSpecs(); @@ -3143,7 +3147,7 @@ defineSuite([ expect(statistics.numberOfCommands).toEqual(3); expect(root._isClipped).toBe(true); - plane.distance = -4081630.31115071 - 287.0736139905632; // center + radius + plane.distance = -radius; tileset.update(scene.frameState); scene.renderForSpecs(); @@ -3164,7 +3168,9 @@ defineSuite([ tileset.update(scene.frameState); - var plane = new ClippingPlane(Cartesian3.UNIT_Z, 0.0); + var radius = 142.19001637409772; + + var plane = new ClippingPlane(Cartesian3.UNIT_Z, radius); tileset.clippingPlanes = new ClippingPlaneCollection({ planes : [ plane @@ -3177,7 +3183,7 @@ defineSuite([ expect(statistics.numberOfCommands).toEqual(6); expect(root._isClipped).toBe(false); - plane.distance = -4081608.4377916814; // center + plane.distance = 0; tileset.update(scene.frameState); scene.renderForSpecs(); @@ -3185,7 +3191,7 @@ defineSuite([ expect(statistics.numberOfCommands).toEqual(6); expect(root._isClipped).toBe(true); - plane.distance = -4081608.4377916814 - 142.19001637409772; // center + radius + plane.distance = -radius; tileset.update(scene.frameState); scene.renderForSpecs(); @@ -3194,4 +3200,28 @@ defineSuite([ expect(root._isClipped).toBe(true); }); }); + + it('uses bounding sphere for clipping only if root has no transforms', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + expect(tileset._useBoundingSphereForClipping).toBe(true); + + return Cesium3DTilesTester.loadTileset(scene, withTransformBoxUrl).then(function(tileset) { + expect(tileset._useBoundingSphereForClipping).toBe(false); + }); + }); + }); + + it('correctly computes clippingPlaneOffsetMatrix', function() { + return Cesium3DTilesTester.loadTileset(scene, tilesetOfTilesetsUrl).then(function(tileset) { + var offsetMatrix = tileset.clippingPlaneOffsetMatrix; + var boundingSphereMatrix = Transforms.eastNorthUpToFixedFrame(tileset.root.boundingSphere.center); + expect(Matrix4.equals(offsetMatrix,boundingSphereMatrix)).toBe(true); + + return Cesium3DTilesTester.loadTileset(scene, withTransformBoxUrl).then(function(tileset) { + offsetMatrix = tileset.clippingPlaneOffsetMatrix; + expect(Matrix4.equals(offsetMatrix,tileset.root.computedTransform)).toBe(true); + }); + }); + }); + }, 'WebGL'); diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index 62d066ffbf7f..05bc03f51e5e 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -2848,7 +2848,7 @@ defineSuite([ scene.renderForSpecs(); var callsBeforeClipping = gl.texImage2D.calls.count(); - expect(model._modelViewMatrix).toEqual(Matrix4.IDENTITY); + expect(model._clippingPlaneModelViewMatrix).toEqual(Matrix4.IDENTITY); model.clippingPlanes = new ClippingPlaneCollection({ planes : [ @@ -2858,7 +2858,10 @@ defineSuite([ model.update(scene.frameState); scene.renderForSpecs(); - expect(gl.texImage2D.calls.count() - callsBeforeClipping * 2).toEqual(2); + // When clipping planes are created, we expect two calls to texImage2D + // (one for initial creation, and one for copying the data in) + // because clipping planes is stored inside a texture. + expect(gl.texImage2D.calls.count() - callsBeforeClipping).toEqual(2); primitives.remove(model); }); diff --git a/Specs/Scene/PointCloud3DTileContentSpec.js b/Specs/Scene/PointCloud3DTileContentSpec.js index 80d5100f715e..ec020fb43bd1 100644 --- a/Specs/Scene/PointCloud3DTileContentSpec.js +++ b/Specs/Scene/PointCloud3DTileContentSpec.js @@ -934,8 +934,7 @@ defineSuite([ tileset.clippingPlanes = new ClippingPlaneCollection({ planes : [ clipPlane - ], - modelMatrix : Transforms.eastNorthUpToFixedFrame(tileset.boundingSphere.center) + ] }); expect(scene).notToRender(color);