diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index dc5a93041057..8bbbfcd457c7 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3548,6 +3548,17 @@ define([ var scratchUp = new Cartesian3(); function pickFromRay(scene, ray, pickPosition, pickObject) { + //>>includeStart('debug', pragmas.debug); + Check.defined('ray', ray); + if (pickPosition && !this.pickPositionSupported) { + throw new DeveloperError('Picking from the depth buffer is not supported. Check pickPositionSupported.'); + } + if (scene._mode === SceneMode.SCENE2D) { + throw new DeveloperError('Pick from ray is not supported in 2D.'); + } + //>>includeEnd('debug'); + + var context = scene._context; var uniformState = context.uniformState; var frameState = scene._frameState; @@ -3635,7 +3646,7 @@ define([ /** * Returns an object with a `primitive` property that contains the first (top) primitive in the scene * hit by the ray or undefined if nothing is hit. Other properties may potentially be set depending on the type - * of primitive and may be used to further identify the picked object. + * of primitive and may be used to further identify the picked object. The ray must be given in world coordinates. *

* When a feature of a 3D Tiles tileset is picked, pick returns a {@link Cesium3DTileFeature} object. *

@@ -3648,9 +3659,6 @@ define([ * @see Scene#pick */ Scene.prototype.pickFromRay = function(ray) { - //>>includeStart('debug', pragmas.debug); - Check.defined('ray', ray); - //>>includeEnd('debug'); var pickResult = pickFromRay(this, ray, false, true); if (defined(pickResult)) { return pickResult.object; @@ -3658,7 +3666,8 @@ define([ }; /** - * Returns the cartesian position of the first intersection of the ray or undefined if nothing is hit. + * Returns the position, in world coordinates, of the first intersection of the ray or undefined if nothing is hit. + * The ray must be given in world coordinates. * * @private * @@ -3669,12 +3678,6 @@ define([ * @exception {DeveloperError} Picking from the depth buffer is not supported. Check pickPositionSupported. */ Scene.prototype.pickPositionFromRay = function(ray, result) { - //>>includeStart('debug', pragmas.debug); - Check.defined('ray', ray); - if (!this.pickPositionSupported) { - throw new DeveloperError('Picking from the depth buffer is not supported. Check pickPositionSupported.'); - } - //>>includeEnd('debug'); var pickResult = pickFromRay(this, ray, true, false); if (defined(pickResult)) { return Cartesian3.clone(pickResult.position, result); @@ -3684,7 +3687,7 @@ define([ /** * Returns a list of objects, each containing a `primitive` property, for all primitives hit * by the ray. Other properties may also be set depending on the type of primitive and may be - * used to further identify the picked object. + * used to further identify the picked object. The ray must be given in world coordinates. * The primitives in the list are ordered by their visual order in the scene (front to back). * * @private @@ -3696,9 +3699,6 @@ define([ * @see Scene#drillPick */ Scene.prototype.drillPickFromRay = function(ray, limit) { - //>>includeStart('debug', pragmas.debug); - Check.defined('ray', ray); - //>>includeEnd('debug'); var that = this; var pickCallback = function() { return pickFromRay(that, ray, false, true); @@ -3710,7 +3710,7 @@ define([ }; /** - * Returns a list of cartesian positions containing intersections of the ray in the scene. + * Returns a list of cartesian positions, in world coordinates, containing intersections of the ray in the scene. * * @private * @@ -3721,9 +3721,6 @@ define([ * @exception {DeveloperError} Picking from the depth buffer is not supported. Check pickPositionSupported. */ Scene.prototype.drillPickPositionFromRay = function(ray, limit) { - //>>includeStart('debug', pragmas.debug); - Check.defined('ray', ray); - //>>includeEnd('debug'); if (!this.pickPositionSupported) { throw new DeveloperError('Picking from the depth buffer is not supported. Check pickPositionSupported.'); } @@ -3758,9 +3755,6 @@ define([ * */ Scene.prototype.drillPick = function(windowPosition, limit, width, height) { - //>>includeStart('debug', pragmas.debug); - Check.defined('windowPosition', windowPosition); - //>>includeEnd('debug'); var that = this; var pickCallback = function() { var object = that.pick(windowPosition, width, height); @@ -3804,9 +3798,6 @@ define([ Scene.prototype.sampleHeight = function(position, objectsToExclude) { //>>includeStart('debug', pragmas.debug); Check.defined('position', position); - if (!this.pickPositionSupported) { - throw new DeveloperError('Picking from the depth buffer is not supported. Check pickPositionSupported.'); - } //>>includeEnd('debug'); var globe = this.globe; var ellipsoid = defined(globe) ? globe.ellipsoid : this.mapProjection.ellipsoid; @@ -3869,6 +3860,9 @@ define([ * @exception {DeveloperError} Picking from the depth buffer is not supported. Check pickPositionSupported. */ Scene.prototype.clampToHeight = function(cartesian, objectsToExclude, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('cartesian', cartesian); + //>>includeEnd('debug'); var globe = this.globe; var ellipsoid = defined(globe) ? globe.ellipsoid : this.mapProjection.ellipsoid; var cartographic = Cartographic.fromCartesian(cartesian, ellipsoid, scratchPickCartographic); diff --git a/Specs/Scene/PickSpec.js b/Specs/Scene/PickSpec.js index 03d2ae3964e7..5343dce4c007 100644 --- a/Specs/Scene/PickSpec.js +++ b/Specs/Scene/PickSpec.js @@ -1,29 +1,41 @@ defineSuite([ + 'Core/Cartographic', 'Core/FeatureDetection', 'Core/GeometryInstance', 'Core/Math', + 'Core/Matrix4', 'Core/OrthographicFrustum', 'Core/PerspectiveFrustum', + 'Core/Ray', 'Core/Rectangle', 'Core/RectangleGeometry', 'Core/ShowGeometryInstanceAttribute', + 'Core/Transforms', + 'Scene/Cesium3DTileset', 'Scene/EllipsoidSurfaceAppearance', 'Scene/Primitive', 'Scene/SceneMode', + 'Specs/Cesium3DTilesTester', 'Specs/createCanvas', 'Specs/createScene' ], 'Scene/Pick', function( + Cartographic, FeatureDetection, GeometryInstance, CesiumMath, + Matrix4, OrthographicFrustum, PerspectiveFrustum, + Ray, Rectangle, RectangleGeometry, ShowGeometryInstanceAttribute, + Transforms, + Cesium3DTileset, EllipsoidSurfaceAppearance, Primitive, SceneMode, + Cesium3DTilesTester, createCanvas, createScene) { 'use strict'; @@ -32,6 +44,9 @@ defineSuite([ var primitives; var camera; var primitiveRectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); + var otherRectangle = Rectangle.fromDegrees(-45.0, -1.0, -43.0, 1.0); + var primitiveRay; + var otherRay; beforeAll(function() { scene = createScene({ @@ -39,6 +54,16 @@ defineSuite([ }); primitives = scene.primitives; camera = scene.camera; + + camera.setView({ + destination : primitiveRectangle + }); + primitiveRay = new Ray(camera.positionWC, camera.directionWC); + + camera.setView({ + destination : otherRectangle + }); + otherRay = new Ray(camera.positionWC, camera.directionWC); }); afterAll(function() { @@ -82,6 +107,20 @@ defineSuite([ return e; } + function createTileset() { + var url = 'Data/Cesium3DTiles/Batched/BatchedWithTransformBox/tileset.json'; + var options = { + maximumScreenSpaceError : 0 + }; + return Cesium3DTilesTester.loadTileset(scene, url, options).then(function(tileset) { + var cartographic = Rectangle.center(primitiveRectangle); + var cartesian = Cartographic.toCartesian(cartographic); + tileset.root.transform = Matrix4.IDENTITY; + tileset.modelMatrix = Transforms.eastNorthUpToFixedFrame(cartesian); + return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); + }); + } + describe('pick', function() { it('does not pick undefined window positions', function() { expect(function() { @@ -364,4 +403,82 @@ defineSuite([ }); }); }); + + describe('pickFromRay', function() { + it('picks a tileset', function() { + return createTileset().then(function(tileset) { + scene.renderForSpecs(); + var picked = scene.pickFromRay(primitiveRay); + expect(picked.primitive).toBe(tileset); + }); + }); + + it('picks a primitive', function() { + var rectangle = createRectangle(); + var picked = scene.pickFromRay(primitiveRay); + expect(picked.primitive).toBe(rectangle); + }); + + it('does not pick primitive', function() { + createRectangle(); + var picked = scene.pickFromRay(otherRay); + expect(picked).toBeUndefined(); + }); + + it('does not pick primitives when show is false', function() { + var rectangle = createRectangle(); + rectangle.show = false; + var picked = scene.pickFromRay(primitiveRay); + expect(picked).toBeUndefined(); + }); + + it('does not pick primitives when alpha is zero', function() { + var rectangle = createRectangle(); + rectangle.appearance.material.uniforms.color.alpha = 0.0; + var picked = scene.pickFromRay(primitiveRay); + expect(picked).toBeUndefined(); + }); + + it('picks the top primitive', function() { + createRectangle(); + var rectangle2 = createRectangle(); + rectangle2.height = 0.01; + + var picked = scene.pickFromRay(primitiveRay); + expect(picked.primitive).toBe(rectangle2); + }); + + it('picks when main camera is in 3D with orthographic projection', function() { + var frustum = new OrthographicFrustum(); + frustum.aspectRatio = 1.0; + frustum.width = 20.0; + camera.frustum = frustum; + + // force off center update + expect(frustum.projectionMatrix).toBeDefined(); + + camera.setView({ destination : primitiveRectangle }); + var rectangle = createRectangle(); + scene.initializeFrame(); + var picked = scene.pickFromRay(primitiveRay); + expect(picked.primitive).toBe(rectangle); + }); + + it('picks when main camera is in CV', function() { + // TODO + }); + + it('throws if ray is undefined', function() { + expect(function() { + scene.pickFromRay(undefined); + }).toThrowDeveloperError(); + }); + + // it('throws if main camera is in 2D', function() { + // scene.morphTo2D(0.0); + // expect(function() { + // scene.pickFromRay(primitiveRay); + // }).toThrowDeveloperError(); + // }); + }); }, 'WebGL');