From f6dc8ee68430f2bb3a2fb297eac74fd551325b78 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 26 Jun 2018 20:22:44 -0400 Subject: [PATCH] Initial work for computing bounding sphere --- Source/Scene/PointCloud.js | 72 ++++++++++++++++++++----- Source/Scene/PointCloud3DTileContent.js | 9 ++-- Source/Scene/TimeDynamicPointCloud.js | 23 +++++++- 3 files changed, 86 insertions(+), 18 deletions(-) diff --git a/Source/Scene/PointCloud.js b/Source/Scene/PointCloud.js index ffc18a28e3cc..a2c5cf809d96 100644 --- a/Source/Scene/PointCloud.js +++ b/Source/Scene/PointCloud.js @@ -1,7 +1,9 @@ define([ '../Core/arraySlice', + '../Core/BoundingSphere', '../Core/Cartesian3', '../Core/Cartesian4', + '../Core/Math', '../Core/Check', '../Core/Color', '../Core/combine', @@ -39,8 +41,10 @@ define([ './ShadowMode' ], function( arraySlice, + BoundingSphere, Cartesian3, Cartesian4, + CesiumMath, Check, Color, combine, @@ -161,6 +165,7 @@ define([ this._batchTableLoaded = options.batchTableLoaded; this._pickIdLoaded = options.pickIdLoaded; this._opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE); + this._cull = defaultValue(options.cull, true); this.style = undefined; this._style = undefined; @@ -171,7 +176,7 @@ define([ this.time = 0.0; // For styling this.shadows = ShadowMode.ENABLED; - this.boundingVolume = undefined; + this.boundingSphere = undefined; this.clippingPlanes = undefined; this.isClipped = false; @@ -452,6 +457,31 @@ define([ pointCloud._hasBatchIds = hasBatchIds; } + var scratchMin = new Cartesian3(); + var scratchMax = new Cartesian3(); + var scratchPosition = new Cartesian3(); + + function computeApproximateBoundingSphereFromPositions(positions) { + var pointsLength = positions.length / 3; + var samplesLength = Math.min(pointsLength, 20); + var maxValue = Number.MAX_VALUE; + var minValue = -Number.MAX_VALUE; + var min = Cartesian3.fromElements(maxValue, maxValue, maxValue, scratchMin); + var max = Cartesian3.fromElements(minValue, minValue, minValue, scratchMax); + for (var i = 0; i < samplesLength; ++i) { + var index = Math.floor(i * pointsLength / samplesLength); + var position = Cartesian3.unpack(positions, index * 3, scratchPosition); + Cartesian3.minimumByComponent(min, position, min); + Cartesian3.maximumByComponent(max, position, max); + } + + var boundingSphere = BoundingSphere.fromCornerPoints(min, max); + if (pointsLength === 1) { + boundingSphere.radius = CesiumMath.EPSILON2; // To avoid radius of zero + } + return boundingSphere; + } + function prepareVertexAttribute(typedArray) { // WebGL does not support UNSIGNED_INT, INT, or DOUBLE vertex attributes. Convert these to FLOAT. var componentDatatype = ComponentDatatype.fromTypedArray(typedArray); @@ -473,6 +503,7 @@ define([ var numberOfAttributes = 4; var scratchClippingPlaneMatrix = new Matrix4(); + function createResources(pointCloud, frameState) { var context = frameState.context; var parsedContent = pointCloud._parsedContent; @@ -602,6 +633,18 @@ define([ strideInBytes : 0 }); + if (pointCloud._cull) { + if (isQuantized || isQuantizedDraco) { + // Quantized volume offset is applied to the model matrix, not the bounding sphere + var scale = pointCloud._quantizedVolumeScale; + var center = Cartesian3.multiplyByScalar(scale, 0.5, new Cartesian3()); + var radius = Cartesian3.maximumComponent(scale) * 0.5; + pointCloud.boundingSphere = new BoundingSphere(center, radius); + } else { + pointCloud.boundingSphere = computeApproximateBoundingSphereFromPositions(positions); + } + } + if (hasColors) { if (isRGB565) { attributes.push({ @@ -685,8 +728,8 @@ define([ }); pointCloud._drawCommand = new DrawCommand({ - boundingVolume : undefined, // Updated in update - cull : false, // Already culled by 3D Tiles + boundingVolume : new BoundingSphere(), + cull : pointCloud._cull, modelMatrix : new Matrix4(), primitiveType : PrimitiveType.POINTS, vertexArray : vertexArray, @@ -1148,10 +1191,6 @@ define([ } } - var scratchComputedTranslation = new Cartesian4(); - var scratchComputedMatrixIn2D = new Matrix4(); - var scratchModelMatrix = new Matrix4(); - function decodeDraco(pointCloud, context) { if (pointCloud._decodingState === DecodingState.READY) { return false; @@ -1207,6 +1246,10 @@ define([ return true; } + var scratchComputedTranslation = new Cartesian4(); + var scratchModelMatrix = new Matrix4(); + var scratchScale = new Cartesian3(); + PointCloud.prototype.update = function(frameState) { var context = frameState.context; var decoding = decodeDraco(this, context); @@ -1229,6 +1272,7 @@ define([ this._ready = true; this._readyPromise.resolve(this); this._parsedContent = undefined; // Unload + this._drawCommand.boundingVolume = this.boundingSphere.clone(); } if (modelMatrixDirty) { @@ -1247,16 +1291,20 @@ define([ var translation = Matrix4.getColumn(modelMatrix, 3, scratchComputedTranslation); if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) { Transforms.basisTo2D(projection, modelMatrix, modelMatrix); - } else { - var center = this.boundingVolume.center; - var to2D = Transforms.wgs84To2DModelMatrix(projection, center, scratchComputedMatrixIn2D); - Matrix4.multiply(to2D, modelMatrix, modelMatrix); } } Matrix4.clone(modelMatrix, this._drawCommand.modelMatrix); - this._drawCommand.boundingVolume = this.boundingVolume; + var boundingSphere = this._drawCommand.boundingVolume; + BoundingSphere.clone(this.boundingSphere, boundingSphere); + + if (this._cull) { + var center = boundingSphere.center; + Matrix4.multiplyByPoint(modelMatrix, center, center); + var scale = Matrix4.getScale(modelMatrix, scratchScale); + boundingSphere.radius *= Cartesian3.maximumComponent(scale); + } } if (this.clippingPlanesDirty) { diff --git a/Source/Scene/PointCloud3DTileContent.js b/Source/Scene/PointCloud3DTileContent.js index 92c171c8f00e..61b6196c62ab 100644 --- a/Source/Scene/PointCloud3DTileContent.js +++ b/Source/Scene/PointCloud3DTileContent.js @@ -62,6 +62,7 @@ define([ this._pointCloud = new PointCloud({ arrayBuffer : arrayBuffer, byteOffset : byteOffset, + cull : false, opaquePass : Pass.CESIUM_3D_TILE, vertexShaderLoaded : getVertexShaderLoaded(this), fragmentShaderLoaded : getFragmentShaderLoaded(this), @@ -286,11 +287,11 @@ define([ batchTable.update(tileset, frameState); } - var boundingVolume; + var boundingSphere; if (defined(tile._contentBoundingVolume)) { - boundingVolume = mode === SceneMode.SCENE3D ? tile._contentBoundingVolume.boundingSphere : tile._contentBoundingVolume2D.boundingSphere; + boundingSphere = mode === SceneMode.SCENE3D ? tile._contentBoundingVolume.boundingSphere : tile._contentBoundingVolume2D.boundingSphere; } else { - boundingVolume = mode === SceneMode.SCENE3D ? tile._boundingVolume.boundingSphere : tile._boundingVolume2D.boundingSphere; + boundingSphere = mode === SceneMode.SCENE3D ? tile._boundingVolume.boundingSphere : tile._boundingVolume2D.boundingSphere; } var styleDirty = this._styleDirty; @@ -301,7 +302,7 @@ define([ pointCloud.modelMatrix = tile.computedTransform; pointCloud.time = tileset.timeSinceLoad; pointCloud.shadows = tileset.shadows; - pointCloud.boundingVolume = boundingVolume; + pointCloud.boundingSphere = boundingSphere; pointCloud.clippingPlanes = clippingPlanes; pointCloud.isClipped = defined(clippingPlanes) && clippingPlanes.enabled && tile._isClipped; pointCloud.clippingPlanesDirty = tile.clippingPlanesDirty; diff --git a/Source/Scene/TimeDynamicPointCloud.js b/Source/Scene/TimeDynamicPointCloud.js index 0cb944e43f91..113afca4f878 100644 --- a/Source/Scene/TimeDynamicPointCloud.js +++ b/Source/Scene/TimeDynamicPointCloud.js @@ -320,6 +320,7 @@ define([ }).then(function(arrayBuffer) { frame.pointCloud = new PointCloud({ arrayBuffer : arrayBuffer, + cull : true, fragmentShaderLoaded : getFragmentShaderLoaded, uniformMapLoaded : getUniformMapLoaded(that), pickIdLoaded : getPickIdLoaded @@ -373,6 +374,24 @@ define([ var scratchModelMatrix = new Matrix4(); + function getGeometricError(that, pointCloud) { + var pointCloudShading = that.pointCloudShading; + if (defined(pointCloudShading) && defined(pointCloudShading.baseResolution)) { + return pointCloudShading.baseResolution; + } + return CesiumMath.cbrt(pointCloud.boundingSphere.volume() / pointCloud.pointsLength); + } + + function getMaximumAttenuation(that) { + var pointCloudShading = that.pointCloudShading; + if (defined(pointCloudShading) && defined(pointCloudShading.maximumAttenuation)) { + return pointCloudShading.maximumAttenuation; + } + + // Return a hardcoded maximum attenuation. For a tileset this would instead be the maximum screen space error. + return 10.0; + } + function renderFrame(that, frame, timeSinceLoad, isClipped, clippingPlanesDirty, frameState) { var pointCloud = frame.pointCloud; var transform = defaultValue(frame.transform, Matrix4.IDENTITY); @@ -388,9 +407,9 @@ define([ var pointCloudShading = that.pointCloudShading; if (defined(pointCloudShading)) { pointCloud.attenuation = pointCloudShading.attenuation; - pointCloud.geometricError = 10.0; // TODO : If we had a bounding volume we could derive it + pointCloud.geometricError = getGeometricError(that, pointCloud); pointCloud.geometricErrorScale = pointCloudShading.geometricErrorScale; - pointCloud.maximumAttenuation = defined(pointCloudShading.maximumAttenuation) ? pointCloudShading.maximumAttenuation : 10; + pointCloud.maximumAttenuation = getMaximumAttenuation(that); } pointCloud.update(frameState); frame.touchedFrameNumber = frameState.frameNumber;