From c70cb42464bd7096c0da3b10711cf518d25aeb1d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 17 Apr 2014 17:36:26 -0400 Subject: [PATCH 01/74] Do not release terrain mesh data after creating vertex array. --- Source/Scene/Tile.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index c2627e2d60b5..b5d254c87fcc 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -356,7 +356,8 @@ define([ var isRenderable = defined(this.vertexArray); // But it's not done loading until our two state machines are terminated. - var isDoneLoading = !defined(this.loadedTerrain) && !defined(this.upsampledTerrain); + var isDoneLoading = defined(this.loadedTerrain) && this.loadedTerrain.state === TerrainState.READY; + isDoneLoading = isDoneLoading || (defined(this.upsampledTerrain) && this.upsampledTerrain.state === TerrainState.READY); // Transition imagery states var tileImageryCollection = this.imagery; @@ -466,7 +467,7 @@ define([ var upsampled = tile.upsampledTerrain; var suspendUpsampling = false; - if (defined(loaded)) { + if (defined(loaded) && loaded.state !== TerrainState.READY) { loaded.processLoadStateMachine(context, terrainProvider, tile.x, tile.y, tile.level); // Publish the terrain data on the tile as soon as it is available. @@ -499,10 +500,6 @@ define([ if (loaded.state === TerrainState.READY) { loaded.publishToTile(tile); - - // No further loading or upsampling is necessary. - tile.loadedTerrain = undefined; - tile.upsampledTerrain = undefined; } else if (loaded.state === TerrainState.FAILED) { // Loading failed for some reason, or data is simply not available, // so no need to continue trying to load. Any retrying will happen before we @@ -511,7 +508,7 @@ define([ } } - if (!suspendUpsampling && defined(upsampled)) { + if (!suspendUpsampling && defined(upsampled) && upsampled.state !== TerrainState.READY) { upsampled.processUpsampleStateMachine(context, terrainProvider, tile.x, tile.y, tile.level); // Publish the terrain data on the tile as soon as it is available. @@ -534,9 +531,6 @@ define([ if (upsampled.state === TerrainState.READY) { upsampled.publishToTile(tile); - - // No further upsampling is necessary. We need to continue loading, though. - tile.upsampledTerrain = undefined; } else if (upsampled.state === TerrainState.FAILED) { // Upsampling failed for some reason. This is pretty much a catastrophic failure, // but maybe we'll be saved by loading. From c84f71a04a150a873b4e577051d5d139f1c5cba3 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 17 Apr 2014 17:37:05 -0400 Subject: [PATCH 02/74] Add ray-triangle intersection test. --- Source/Core/IntersectionTests.js | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 05a1840f1a88..513a1bdc27b7 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -1,5 +1,6 @@ /*global define*/ define([ + './defaultValue', './defined', './DeveloperError', './Math', @@ -10,6 +11,7 @@ define([ './QuarticRealPolynomial' ], function( + defaultValue, defined, DeveloperError, CesiumMath, @@ -65,6 +67,103 @@ define([ return Cartesian3.add(origin, result, result); }; + var scratchEdge0 = new Cartesian3(); + var scratchEdge1 = new Cartesian3(); + var scratchPVec = new Cartesian3(); + var scratchTVec = new Cartesian3(); + var scratchQVec = new Cartesian3(); + + /** + * Computes the intersection of a ray and a triangle. + * @memberof IntersectionTests + * + * @param {Ray} ray The ray. + * @param {Cartesian3} p0 The first vertex of the triangle. + * @param {Cartesian3} p1 The second vertex of the triangle. + * @param {Cartesian3} p2 The third vertex of the triangle. + * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle + * and return undefined for intersections with the back face. + * @returns {Cartesian3} The intersection point or undefined if there is no intersections. + */ + IntersectionTests.rayTriangle = function(ray, p0, p1, p2, cullBackFacing, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(ray)) { + throw new DeveloperError('ray is required.'); + } + if (!defined(p0)) { + throw new DeveloperError('p0 is required.'); + } + if (!defined(p1)) { + throw new DeveloperError('p1 is required.'); + } + if (!defined(p2)) { + throw new DeveloperError('p2 is required.'); + } + //>>includeEnd('debug'); + + cullBackFacing = defaultValue(cullBackFacing, false); + + var origin = ray.origin; + var direction = ray.direction; + + var edge0 = Cartesian3.subtract(p1, p0, scratchEdge0); + var edge1 = Cartesian3.subtract(p2, p0, scratchEdge1); + + var p = Cartesian3.cross(direction, edge1, scratchPVec); + var det = Cartesian3.dot(edge0, p); + + var tvec; + var q; + + var u; + var v; + var t; + + if (cullBackFacing) { + if (det < CesiumMath.EPSILON6) { + return undefined; + } + + tvec = Cartesian3.subtract(origin, p0, scratchTVec); + u = Cartesian3.dot(tvec, p); + if (u < 0.0 || u > det) { + return undefined; + } + + q = Cartesian3.cross(tvec, edge0, scratchQVec); + + v = Cartesian3.dot(direction, q); + if (v < 0.0 || u + v > det) { + return undefined; + } + + t = Cartesian3.dot(edge1, q) / det; + } else { + if (Math.abs(det) < CesiumMath.EPSILON6) { + return undefined; + } + var invDet = 1.0 / det; + + tvec = Cartesian3.subtract(origin, p0, scratchTVec); + u = Cartesian3.dot(tvec, p) * invDet; + if (u < 0.0 || u > 1.0) { + return undefined; + } + + q = Cartesian3.cross(tvec, edge0, scratchQVec); + + v = Cartesian3.dot(direction, q) * invDet; + if (v < 0.0 || u + v > 1.0) { + return undefined; + } + + t = Cartesian3.dot(edge1, q) * invDet; + } + + result = Cartesian3.multiplyByScalar(direction, t, result); + return Cartesian3.add(origin, result, result); + }; + var scratchQ = new Cartesian3(); var scratchW = new Cartesian3(); From 9105d249ef1f189c2ce8fc215dd76f3b95e2e0dd Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 17 Apr 2014 20:06:03 -0400 Subject: [PATCH 03/74] Add ray-triangle intersection tests. --- Specs/Core/IntersectionTestsSpec.js | 101 ++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/Specs/Core/IntersectionTestsSpec.js b/Specs/Core/IntersectionTestsSpec.js index e0f779e0c94c..d0367f920a3c 100644 --- a/Specs/Core/IntersectionTestsSpec.js +++ b/Specs/Core/IntersectionTestsSpec.js @@ -57,6 +57,107 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('rayTriangle throws without ray', function() { + expect(function() { + IntersectionTests.rayTriangle(); + }).toThrow(); + }); + + it('rayTriangle throws without p0', function() { + expect(function() { + IntersectionTests.rayTriangle(new Ray()); + }).toThrow(); + }); + + it('rayTriangle throws without p1', function() { + expect(function() { + IntersectionTests.rayTriangle(new Ray(), new Cartesian3()); + }).toThrow(); + }); + + it('rayTriangle throws without p2', function() { + expect(function() { + IntersectionTests.rayTriangle(new Ray(), new Cartesian3(), new Cartesian3()); + }).toThrow(); + }); + + it('rayTriangle intersects front face', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(Cartesian3.UNIT_Z, Cartesian3.negate(Cartesian3.UNIT_Z)); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); + expect(intersection).toEqual(Cartesian3.ZERO); + }); + + it('rayTriangle intersects back face without culling', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(Cartesian3.negate(Cartesian3.UNIT_Z), Cartesian3.UNIT_Z); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); + expect(intersection).toEqual(Cartesian3.ZERO); + }); + + it('rayTriangle does not intersect back face with culling', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(Cartesian3.negate(Cartesian3.UNIT_Z), Cartesian3.UNIT_Z); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2, true); + expect(intersection).not.toBeDefined(); + }); + + it('rayTriangle does not intersect outside the 0-1 edge', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(new Cartesian3(0.0, -1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z)); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('rayTriangle does not intersect outside the 1-2 edge', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(new Cartesian3(1.0, 1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z)); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('rayTriangle does not intersect outside the 2-0 edge', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(new Cartesian3(-1.0, 1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z)); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('rayTriangle does not intersect parallel ray and triangle', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(new Cartesian3(-1.0, 0.0, 1.0), Cartesian3.UNIT_X); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + it('rayEllipsoid throws without ray', function() { expect(function() { IntersectionTests.rayEllipsoid(); From 9602ee4ed6e6bb18d39dbaa05e83976cdc359c7a Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 18 Apr 2014 15:35:16 -0400 Subject: [PATCH 04/74] Add ray-sphere intersection test. --- Source/Core/IntersectionTests.js | 71 +++++++++++ Specs/Core/IntersectionTestsSpec.js | 186 ++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 513a1bdc27b7..e0f3a13e4da4 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -164,6 +164,77 @@ define([ return Cartesian3.add(origin, result, result); }; + /** + * Computes the intersection points of a ray with a sphere. + * @memberof IntersectionTests + * + * @param {Ray} ray The ray. + * @param {BoundingSphere} sphere The sphere. + * @returns {Object} An object with the first (start) and the second (stop) intersection scalars for points along the ray or undefined if there are no intersections. + */ + IntersectionTests.raySphere = function(ray, sphere) { + //>>includeStart('debug', pragmas.debug); + if (!defined(ray)) { + throw new DeveloperError('ray is required.'); + } + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + //>>includeEnd('debug'); + + var origin = ray.origin; + var direction = ray.direction; + + var center = sphere.center; + var radiusSquared = sphere.radius * sphere.radius; + + var diff = Cartesian3.subtract(origin, center, scratchPVec); + + var a = Cartesian3.dot(direction, direction); + var b = 2.0 * Cartesian3.dot(direction, diff); + var c = Cartesian3.magnitudeSquared(diff) - radiusSquared; + + var det = b * b - 4.0 * a * c; + if (det < 0.0) { + return undefined; + } else if (det > 0.0) { + var denom = 1.0 / (2.0 * a); + var disc = Math.sqrt(det); + var root0 = (-b + disc) * denom; + var root1 = (-b - disc) * denom; + + if (root0 <= 0.0 && root1 <= 0.0) { + return undefined; + } else if (root0 < 0.0) { + root0 = 0.0; + } else if (root1 < 0.0) { + root1 = 0.0; + } + + if (root0 < root1) { + return { + start : root0, + stop : root1 + }; + } + + return { + start : root1, + stop : root0 + }; + } + + var root = -b / (2.0 * a); + if (root === 0.0) { + return undefined; + } + + return { + start : root, + stop : root + }; + }; + var scratchQ = new Cartesian3(); var scratchW = new Cartesian3(); diff --git a/Specs/Core/IntersectionTestsSpec.js b/Specs/Core/IntersectionTestsSpec.js index d0367f920a3c..f820ded52396 100644 --- a/Specs/Core/IntersectionTestsSpec.js +++ b/Specs/Core/IntersectionTestsSpec.js @@ -1,6 +1,7 @@ /*global defineSuite*/ defineSuite([ 'Core/IntersectionTests', + 'Core/BoundingSphere', 'Core/Cartesian3', 'Core/defined', 'Core/Ellipsoid', @@ -9,6 +10,7 @@ defineSuite([ 'Core/Ray' ], function( IntersectionTests, + BoundingSphere, Cartesian3, defined, Ellipsoid, @@ -158,6 +160,190 @@ defineSuite([ expect(intersection).not.toBeDefined(); }); + it('raySphere throws without ray', function() { + expect(function() { + IntersectionTests.raySphere(); + }).toThrowDeveloperError(); + }); + + it('raySphere throws without ellipsoid', function() { + expect(function() { + IntersectionTests.raySphere(new Ray()); + }).toThrowDeveloperError(); + }); + + it('raySphere outside intersections', function() { + var unitSphere = new BoundingSphere(Cartesian3.ZERO, 1.0); + + var ray = new Ray(new Cartesian3(2.0, 0.0, 0.0), new Cartesian3(-1.0, 0.0, 0.0)); + var intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(0.0, 2.0, 0.0), new Cartesian3(0.0, -1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(0.0, 0.0, 2.0), new Cartesian3(0.0, 0.0, -1.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(1.0, 1.0, 0.0), new Cartesian3(-1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(-2.0, 0.0, 0.0), new Cartesian3(1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(0.0, -2.0, 0.0), new Cartesian3(0.0, 1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(0.0, 0.0, -2.0), new Cartesian3(0.0, 0.0, 1.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(-1.0, -1.0, 0.0), new Cartesian3(1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(-2.0, 0.0, 0.0), new Cartesian3(-1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).toBeUndefined(); + + ray = new Ray(new Cartesian3(0.0, -2.0, 0.0), new Cartesian3(0.0, -1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).toBeUndefined(); + + ray = new Ray(new Cartesian3(0.0, 0.0, -2.0), new Cartesian3(0.0, 0.0, -1.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).toBeUndefined(); + }); + + it('raySphere ray inside pointing in intersection', function() { + var sphere = new BoundingSphere(Cartesian3.ZERO, 5000.0); + + var origin = new Cartesian3(200.0, 0.0, 0.0); + var direction = Cartesian3.negate(Cartesian3.normalize(origin)); + var ray = new Ray(origin, direction); + + var expected = { + start : 0.0, + stop : sphere.radius + origin.x + }; + var actual = IntersectionTests.raySphere(ray, sphere); + + expect(actual).toBeDefined(); + expect(actual.start).toEqual(expected.start); + expect(actual.stop).toEqual(expected.stop); + }); + + it('raySphere ray inside pointing out intersection', function() { + var sphere = new BoundingSphere(Cartesian3.ZERO, 5000.0); + + var origin = new Cartesian3(200.0, 0.0, 0.0); + var direction = Cartesian3.normalize(origin); + var ray = new Ray(origin, direction); + + var expected = { + start : 0.0, + stop : sphere.radius - origin.x + }; + var actual = IntersectionTests.raySphere(ray, sphere); + + expect(actual).toBeDefined(); + expect(actual.start).toEqual(expected.start); + expect(actual.stop).toEqual(expected.stop); + }); + + it('raySphere tangent intersections', function() { + var unitSphere = new BoundingSphere(Cartesian3.ZERO, 1.0); + + var ray = new Ray(Cartesian3.UNIT_X, Cartesian3.UNIT_Z); + var intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).not.toBeDefined(); + }); + + it('raySphere no intersections', function() { + var unitSphere = new BoundingSphere(Cartesian3.ZERO, 1.0); + + var ray = new Ray(new Cartesian3(2.0, 0.0, 0.0), new Cartesian3(0.0, 0.0, 1.0)); + var intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).not.toBeDefined(); + + ray = new Ray(new Cartesian3(2.0, 0.0, 0.0), new Cartesian3(0.0, 0.0, -1.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).not.toBeDefined(); + + ray = new Ray(new Cartesian3(2.0, 0.0, 0.0), new Cartesian3(0.0, 1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).not.toBeDefined(); + + ray = new Ray(new Cartesian3(2.0, 0.0, 0.0), new Cartesian3(0.0, -1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).not.toBeDefined(); + }); + + it('raySphere intersection with sphere center not the origin', function() { + var unitSphere = new BoundingSphere(new Cartesian3(200.0, 0.0, 0.0), 1.0); + + var ray = new Ray(new Cartesian3(202.0, 0.0, 0.0), new Cartesian3(-1.0, 0.0, 0.0)); + var intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(200.0, 2.0, 0.0), new Cartesian3(0.0, -1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(200.0, 0.0, 2.0), new Cartesian3(0.0, 0.0, -1.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(201.0, 1.0, 0.0), new Cartesian3(-1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(198.0, 0.0, 0.0), new Cartesian3(1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(200.0, -2.0, 0.0), new Cartesian3(0.0, 1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(200.0, 0.0, -2.0), new Cartesian3(0.0, 0.0, 1.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + expect(intersections.stop).toEqualEpsilon(3.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(199.0, -1.0, 0.0), new Cartesian3(1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections.start).toEqualEpsilon(1.0, CesiumMath.EPSILON14); + + ray = new Ray(new Cartesian3(198.0, 0.0, 0.0), new Cartesian3(-1.0, 0.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).toBeUndefined(); + + ray = new Ray(new Cartesian3(200.0, -2.0, 0.0), new Cartesian3(0.0, -1.0, 0.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).toBeUndefined(); + + ray = new Ray(new Cartesian3(200.0, 0.0, -2.0), new Cartesian3(0.0, 0.0, -1.0)); + intersections = IntersectionTests.raySphere(ray, unitSphere); + expect(intersections).toBeUndefined(); + }); + it('rayEllipsoid throws without ray', function() { expect(function() { IntersectionTests.rayEllipsoid(); From 2fbcfcada7c5dcadbe51866469df37d257d91b34 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 18 Apr 2014 19:04:11 -0400 Subject: [PATCH 05/74] Pick rendered tiles bounding spheres. --- Apps/Sandcastle/gallery/Picking.html | 25 ++++++++++++++++++ Source/Scene/CentralBody.js | 4 +++ Source/Scene/CentralBodySurface.js | 38 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/Apps/Sandcastle/gallery/Picking.html b/Apps/Sandcastle/gallery/Picking.html index 43522358c3ac..dae654c886e4 100644 --- a/Apps/Sandcastle/gallery/Picking.html +++ b/Apps/Sandcastle/gallery/Picking.html @@ -94,6 +94,7 @@ function cleanup() { widget.scene.primitives.removeAll(); + widget.scene.primitives.centralBody._surface._debug.boundingSphereTile = undefined; handler = handler && handler.destroy(); } @@ -118,6 +119,24 @@ } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); } + + function pickTerrainTile(scene, ellipsoid) { + Sandcastle.declare(pickTerrainTile); // For highlighting in Sandcastle. + + var centralBody = scene.primitives.centralBody; + var camera = scene.camera; + + handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); + handler.setInputAction(function(movement) { + var ray = camera.getPickRay(movement.position); + var tile = centralBody.pick(ray); + if (tile) { + centralBody._surface._debug.boundingSphereTile = tile; + } else { + centralBody._surface._debug.boundingSphereTile = undefined; + } + }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); + } function pickBillboard(scene, ellipsoid) { Sandcastle.declare(pickBillboard); // For highlighting in Sandcastle. @@ -489,6 +508,12 @@ drawRectangle(scene, ellipsoid); Sandcastle.highlight(drawRectangle); }); + + Sandcastle.addToolbarButton('Pick Terrain', function() { + cleanup(); + pickTerrainTile(scene, ellipsoid); + Sandcastle.highlight(pickTerrainTile); + }); Sandcastle.finishedLoading(); }); diff --git a/Source/Scene/CentralBody.js b/Source/Scene/CentralBody.js index cb7afd87ebb9..77ad8ac6b92c 100644 --- a/Source/Scene/CentralBody.js +++ b/Source/Scene/CentralBody.js @@ -292,6 +292,10 @@ define([ } }); + CentralBody.prototype.pick = function(ray) { + return this._surface.pick(ray); + }; + var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; var scratchCartesian1 = new Cartesian3(); var scratchCartesian2 = new Cartesian3(); diff --git a/Source/Scene/CentralBodySurface.js b/Source/Scene/CentralBodySurface.js index 54895848fe40..fb09bfd229ea 100644 --- a/Source/Scene/CentralBodySurface.js +++ b/Source/Scene/CentralBodySurface.js @@ -14,6 +14,7 @@ define([ '../Core/FeatureDetection', '../Core/getTimestamp', '../Core/Intersect', + '../Core/IntersectionTests', '../Core/Matrix4', '../Core/PrimitiveType', '../Core/Queue', @@ -42,6 +43,7 @@ define([ FeatureDetection, getTimestamp, Intersect, + IntersectionTests, Matrix4, PrimitiveType, Queue, @@ -255,6 +257,42 @@ define([ } }; + function createComparePickTileFunction(rayOrigin) { + return function(a, b) { + var aDist = BoundingSphere.distanceSquaredTo(a.boundingSphere3D, rayOrigin); + var bDist = BoundingSphere.distanceSquaredTo(b.boundingSphere3D, rayOrigin); + + return aDist - bDist; + }; + } + + var scratchSphereIntersections = []; + + CentralBodySurface.prototype.pick = function(ray) { + var sphereIntersections = scratchSphereIntersections; + sphereIntersections.length = 0; + + var tilesToRenderByTextureCount = this._tilesToRenderByTextureCount; + var tile; + for (var i = 0; i < tilesToRenderByTextureCount.length; ++i) { + var tileSet = tilesToRenderByTextureCount[i]; + if (!defined(tileSet)) { + continue; + } + for (var j = 0; j < tileSet.length; ++j) { + tile = tileSet[j]; + var boundingSphereIntersection = IntersectionTests.raySphere(ray, tile.boundingSphere3D); + if (defined(boundingSphereIntersection)) { + sphereIntersections.push(tile); + } + } + } + + sphereIntersections.sort(createComparePickTileFunction(ray.origin)); + + return sphereIntersections[0]; + }; + /** * Returns true if this object was destroyed; otherwise, false. *

From 1ebae09280742c63d4b25df07b964f8caea6bd8d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 22 Apr 2014 15:27:44 -0400 Subject: [PATCH 06/74] Pick terrain mesh. --- Apps/Sandcastle/gallery/Picking.html | 27 +---------------- Apps/Sandcastle/gallery/Terrain.html | 18 ++++++++++++ Source/Scene/CentralBody.js | 4 +-- Source/Scene/CentralBodySurface.js | 20 +++++++++++-- Source/Scene/Tile.js | 43 ++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 31 deletions(-) diff --git a/Apps/Sandcastle/gallery/Picking.html b/Apps/Sandcastle/gallery/Picking.html index dae654c886e4..755044de7427 100644 --- a/Apps/Sandcastle/gallery/Picking.html +++ b/Apps/Sandcastle/gallery/Picking.html @@ -94,7 +94,6 @@ function cleanup() { widget.scene.primitives.removeAll(); - widget.scene.primitives.centralBody._surface._debug.boundingSphereTile = undefined; handler = handler && handler.destroy(); } @@ -119,24 +118,6 @@ } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); } - - function pickTerrainTile(scene, ellipsoid) { - Sandcastle.declare(pickTerrainTile); // For highlighting in Sandcastle. - - var centralBody = scene.primitives.centralBody; - var camera = scene.camera; - - handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); - handler.setInputAction(function(movement) { - var ray = camera.getPickRay(movement.position); - var tile = centralBody.pick(ray); - if (tile) { - centralBody._surface._debug.boundingSphereTile = tile; - } else { - centralBody._surface._debug.boundingSphereTile = undefined; - } - }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); - } function pickBillboard(scene, ellipsoid) { Sandcastle.declare(pickBillboard); // For highlighting in Sandcastle. @@ -508,15 +489,9 @@ drawRectangle(scene, ellipsoid); Sandcastle.highlight(drawRectangle); }); - - Sandcastle.addToolbarButton('Pick Terrain', function() { - cleanup(); - pickTerrainTile(scene, ellipsoid); - Sandcastle.highlight(pickTerrainTile); - }); Sandcastle.finishedLoading(); }); - + \ No newline at end of file diff --git a/Apps/Sandcastle/gallery/Terrain.html b/Apps/Sandcastle/gallery/Terrain.html index 646a0edcff3a..c9abd6b4ac5f 100644 --- a/Apps/Sandcastle/gallery/Terrain.html +++ b/Apps/Sandcastle/gallery/Terrain.html @@ -199,6 +199,24 @@ centralBody.terrainProvider = cesiumTerrainProviderMeshes; createTerrainMenu(terrainProviders); + + var labels = new Cesium.LabelCollection(); + var label = labels.add(); + scene.primitives.add(labels); + + var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); + handler.setInputAction(function(movement) { + var ray = scene.camera.getPickRay(movement.position); + var cartesian = centralBody.pick(ray); + if (cartesian) { + var cartographic = centralBody.ellipsoid.cartesianToCartographic(cartesian); + label.show = true; + label.text = '(' + Cesium.Math.toDegrees(cartographic.longitude).toFixed(2) + ', ' + Cesium.Math.toDegrees(cartographic.latitude).toFixed(2) + ', ' + cartographic.height.toFixed(2) + ')'; + label.position = cartesian; + } else { + label.text = ''; + } + }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); Sandcastle.finishedLoading(); }); diff --git a/Source/Scene/CentralBody.js b/Source/Scene/CentralBody.js index 77ad8ac6b92c..6bbf1f75472a 100644 --- a/Source/Scene/CentralBody.js +++ b/Source/Scene/CentralBody.js @@ -292,8 +292,8 @@ define([ } }); - CentralBody.prototype.pick = function(ray) { - return this._surface.pick(ray); + CentralBody.prototype.pick = function(ray, result) { + return this._surface.pick(ray, result); }; var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; diff --git a/Source/Scene/CentralBodySurface.js b/Source/Scene/CentralBodySurface.js index fb09bfd229ea..c280f06eaefd 100644 --- a/Source/Scene/CentralBodySurface.js +++ b/Source/Scene/CentralBodySurface.js @@ -268,13 +268,17 @@ define([ var scratchSphereIntersections = []; - CentralBodySurface.prototype.pick = function(ray) { + CentralBodySurface.prototype.pick = function(ray, result) { var sphereIntersections = scratchSphereIntersections; sphereIntersections.length = 0; var tilesToRenderByTextureCount = this._tilesToRenderByTextureCount; + var length = tilesToRenderByTextureCount.length; + var tile; - for (var i = 0; i < tilesToRenderByTextureCount.length; ++i) { + var i; + + for (i = 0; i < length; ++i) { var tileSet = tilesToRenderByTextureCount[i]; if (!defined(tileSet)) { continue; @@ -290,7 +294,17 @@ define([ sphereIntersections.sort(createComparePickTileFunction(ray.origin)); - return sphereIntersections[0]; + var intersection; + length = sphereIntersections.length; + for (i = 0; i < length; ++i) { + tile = sphereIntersections[i]; + intersection = tile.pick(ray, result); + if (defined(intersection)) { + break; + } + } + + return intersection; }; /** diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index b5d254c87fcc..2adb0d4a0f01 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -4,9 +4,11 @@ define([ '../Core/Cartesian3', '../Core/Cartesian4', '../Core/Cartographic', + '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', '../Core/DeveloperError', + '../Core/IntersectionTests', '../Core/Rectangle', './ImageryState', './TerrainState', @@ -22,9 +24,11 @@ define([ Cartesian3, Cartesian4, Cartographic, + defaultValue, defined, defineProperties, DeveloperError, + IntersectionTests, Rectangle, ImageryState, TerrainState, @@ -269,6 +273,45 @@ define([ } }); + var scratchV0 = new Cartesian3(); + var scratchV1 = new Cartesian3(); + var scratchV2 = new Cartesian3(); + + Tile.prototype.pick = function(ray, result) { + var terrain = defaultValue(this.loadedTerrain, this.upsampledTerrain); + if (!defined(terrain)) { + return undefined; + } + + var mesh = terrain.mesh; + var vertices = mesh.vertices; + var indices = mesh.indices; + + var center = this.center; + + var length = indices.length; + for (var i = 0; i < length; i += 3) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + + var v0 = Cartesian3.unpack(vertices, i0 * 6, scratchV0); + var v1 = Cartesian3.unpack(vertices, i1 * 6, scratchV1); + var v2 = Cartesian3.unpack(vertices, i2 * 6, scratchV2); + + Cartesian3.add(center, v0, v0); + Cartesian3.add(center, v1, v1); + Cartesian3.add(center, v2, v2); + + var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, result); + if (defined(intersection)) { + return intersection; + } + } + + return undefined; + }; + Tile.prototype.freeResources = function() { if (defined(this.waterMaskTexture)) { --this.waterMaskTexture.referenceCount; From 1342a26118eaf599cf5be4e0d318e7a1f9c267ec Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 25 Apr 2014 15:06:50 -0400 Subject: [PATCH 07/74] Updates after merge. --- Apps/Sandcastle/gallery/Terrain.html | 4 ++-- Source/Scene/Globe.js | 2 +- Source/Scene/GlobeSurface.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Apps/Sandcastle/gallery/Terrain.html b/Apps/Sandcastle/gallery/Terrain.html index 4f5ab9c5a153..1c83c39fd69d 100644 --- a/Apps/Sandcastle/gallery/Terrain.html +++ b/Apps/Sandcastle/gallery/Terrain.html @@ -206,9 +206,9 @@ var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); handler.setInputAction(function(movement) { var ray = scene.camera.getPickRay(movement.position); - var cartesian = centralBody.pick(ray); + var cartesian = globe.pick(ray); if (cartesian) { - var cartographic = centralBody.ellipsoid.cartesianToCartographic(cartesian); + var cartographic = globe.ellipsoid.cartesianToCartographic(cartesian); label.show = true; label.text = '(' + Cesium.Math.toDegrees(cartographic.longitude).toFixed(2) + ', ' + Cesium.Math.toDegrees(cartographic.latitude).toFixed(2) + ', ' + cartographic.height.toFixed(2) + ')'; label.position = cartesian; diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 25f7f7b06b48..0cf3e2ecf502 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -292,7 +292,7 @@ define([ } }); - CentralBody.prototype.pick = function(ray, result) { + Globe.prototype.pick = function(ray, result) { return this._surface.pick(ray, result); }; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 536b449c6293..f36b86cd4d6b 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -268,7 +268,7 @@ define([ var scratchSphereIntersections = []; - CentralBodySurface.prototype.pick = function(ray, result) { + GlobeSurface.prototype.pick = function(ray, result) { var sphereIntersections = scratchSphereIntersections; sphereIntersections.length = 0; From 5a029ebaeda7165fa63ab2c01e7584aefa25c5d2 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 6 May 2014 15:23:29 -0400 Subject: [PATCH 08/74] Pan terrain in 3D. --- Source/Scene/Scene.js | 2 +- Source/Scene/ScreenSpaceCameraController.js | 46 ++++++++++++++------- Source/Scene/Tile.js | 4 ++ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 2a0c6d53f808..63b6b5478b55 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -141,7 +141,7 @@ define([ this._primitives = new CompositePrimitive(); this._pickFramebuffer = undefined; this._camera = new Camera(this); - this._screenSpaceCameraController = new ScreenSpaceCameraController(canvas, this._camera); + this._screenSpaceCameraController = new ScreenSpaceCameraController(this); this._animations = new AnimationCollection(); diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index c52e8fae45ad..9f726d9ac51e 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -52,16 +52,12 @@ define([ * @alias ScreenSpaceCameraController * @constructor * - * @param {HTMLCanvasElement} canvas The canvas to listen for events. - * @param {Camera} camera The camera. + * @param {Scene} scene The scene. */ - var ScreenSpaceCameraController = function(canvas, camera) { + var ScreenSpaceCameraController = function(scene) { //>>includeStart('debug', pragmas.debug); - if (!defined(canvas)) { - throw new DeveloperError('canvas is required.'); - } - if (!defined(camera)) { - throw new DeveloperError('camera is required.'); + if (!defined(scene)) { + throw new DeveloperError('scene is required.'); } //>>includeEnd('debug'); @@ -233,11 +229,12 @@ define([ modifier : KeyboardEventModifier.SHIFT }; - this._canvas = canvas; - this._camera = camera; + this._scene = scene; + this._camera = scene.camera; + this._canvas = scene.canvas; this._ellipsoid = Ellipsoid.WGS84; - this._aggregator = new CameraEventAggregator(canvas); + this._aggregator = new CameraEventAggregator(this._canvas); this._lastInertiaSpinMovement = undefined; this._lastInertiaZoomMovement = undefined; @@ -653,9 +650,27 @@ define([ } var spin3DPick = new Cartesian3(); + var scratchStartRay = new Ray(); + function spin3D(controller, movement) { if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { - pan3D(controller, movement); + var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC).height; + if (height < 150000.0) { + var scene = controller._scene; + var globe = scene.globe; + + var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); + var mousePos = globe.pick(startRay); + if (!defined(mousePos)) { + pan3D(controller, movement, controller._ellipsoid); + } else { + var magnitude = Cartesian3.magnitude(mousePos); + var ellipsoid = new Ellipsoid(magnitude, magnitude, magnitude); + pan3D(controller, movement, ellipsoid); + } + } else { + pan3D(controller, movement, controller._ellipsoid); + } } else { rotate3D(controller, movement); } @@ -752,10 +767,11 @@ define([ var pan3DTemp1 = new Cartesian3(); var pan3DTemp2 = new Cartesian3(); var pan3DTemp3 = new Cartesian3(); - function pan3D(controller, movement) { + + function pan3D(controller, movement, ellipsoid) { var camera = controller._camera; - var p0 = camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, pan3DP0); - var p1 = camera.pickEllipsoid(movement.endPosition, controller._ellipsoid, pan3DP1); + var p0 = camera.pickEllipsoid(movement.startPosition, ellipsoid, pan3DP0); + var p1 = camera.pickEllipsoid(movement.endPosition, ellipsoid, pan3DP1); if (!defined(p0) || !defined(p1)) { return; diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 57c9d66d4cd2..62c884fd3ffa 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -284,6 +284,10 @@ define([ } var mesh = terrain.mesh; + if (!defined(mesh)) { + return undefined; + } + var vertices = mesh.vertices; var indices = mesh.indices; From ffd4b44bbf35812bad074279232168a7cc7805fa Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 6 May 2014 15:43:36 -0400 Subject: [PATCH 09/74] Pick terrain when tilting in 3D. --- Source/Scene/ScreenSpaceCameraController.js | 32 ++++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 9f726d9ac51e..89c033dae8c2 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -228,6 +228,7 @@ define([ eventType : CameraEventType.LEFT_DRAG, modifier : KeyboardEventModifier.SHIFT }; + this.minimumTerrainHeight = 150000.0; this._scene = scene; this._camera = scene.camera; @@ -655,7 +656,7 @@ define([ function spin3D(controller, movement) { if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC).height; - if (height < 150000.0) { + if (height < controller.minimumTerrainHeight) { var scene = controller._scene; var globe = scene.globe; @@ -895,17 +896,26 @@ define([ var ray = camera.getPickRay(windowPosition, tilt3DRay); var center; - var intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); - if (defined(intersection)) { - center = Ray.getPoint(ray, intersection.start, tilt3DCenter); - } else { - var grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, ellipsoid); - if (!defined(grazingAltitudeLocation)) { - return; + var intersection; + + if (height < controller.minimumTerrainHeight) { + var globe = controller._scene.globe; + center = globe.pick(ray); + } + + if (!defined(center)) { + intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); + if (defined(intersection)) { + center = Ray.getPoint(ray, intersection.start, tilt3DCenter); + } else { + var grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, ellipsoid); + if (!defined(grazingAltitudeLocation)) { + return; + } + var grazingAltitudeCart = ellipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); + grazingAltitudeCart.height = 0.0; + center = ellipsoid.cartographicToCartesian(grazingAltitudeCart, tilt3DCenter); } - var grazingAltitudeCart = ellipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); - grazingAltitudeCart.height = 0.0; - center = ellipsoid.cartographicToCartesian(grazingAltitudeCart, tilt3DCenter); } var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); From 00c923ff53b110c93d60d6eb9fdad444cceb9f98 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 7 May 2014 12:03:31 -0400 Subject: [PATCH 10/74] Add picking terrain and use it when tilting in Columbus view. --- Apps/Sandcastle/gallery/Terrain.html | 12 +++++-- Source/Scene/Globe.js | 4 +-- Source/Scene/GlobeSurface.js | 25 +++++++++---- Source/Scene/ScreenSpaceCameraController.js | 30 +++++++++++----- Source/Scene/Tile.js | 39 +++++++++++++++------ 5 files changed, 81 insertions(+), 29 deletions(-) diff --git a/Apps/Sandcastle/gallery/Terrain.html b/Apps/Sandcastle/gallery/Terrain.html index 1c83c39fd69d..e97a736196b9 100644 --- a/Apps/Sandcastle/gallery/Terrain.html +++ b/Apps/Sandcastle/gallery/Terrain.html @@ -206,9 +206,17 @@ var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); handler.setInputAction(function(movement) { var ray = scene.camera.getPickRay(movement.position); - var cartesian = globe.pick(ray); + var cartesian = globe.pick(ray, scene.frameState); if (cartesian) { - var cartographic = globe.ellipsoid.cartesianToCartographic(cartesian); + var cartographic; + if (scene.frameState.mode === Cesium.SceneMode.SCENE3D) { + cartographic = globe.ellipsoid.cartesianToCartographic(cartesian); + } else { + cartesian = Cesium.Cartesian3.fromElements(cartesian.y, cartesian.z, cartesian.x); + cartographic = scene.scene2D.projection.unproject(cartesian); + cartesian = globe.ellipsoid.cartographicToCartesian(cartographic); + } + label.show = true; label.text = '(' + Cesium.Math.toDegrees(cartographic.longitude).toFixed(2) + ', ' + Cesium.Math.toDegrees(cartographic.latitude).toFixed(2) + ', ' + cartographic.height.toFixed(2) + ')'; label.position = cartesian; diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 0cf3e2ecf502..bf20e645fb4f 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -292,8 +292,8 @@ define([ } }); - Globe.prototype.pick = function(ray, result) { - return this._surface.pick(ray, result); + Globe.prototype.pick = function(ray, frameState, result) { + return this._surface.pick(ray, frameState, result); }; var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 0139da06228e..7e0eb12bd498 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -259,8 +259,8 @@ define([ function createComparePickTileFunction(rayOrigin) { return function(a, b) { - var aDist = BoundingSphere.distanceSquaredTo(a.boundingSphere3D, rayOrigin); - var bDist = BoundingSphere.distanceSquaredTo(b.boundingSphere3D, rayOrigin); + var aDist = BoundingSphere.distanceSquaredTo(a.boundingSphere, rayOrigin); + var bDist = BoundingSphere.distanceSquaredTo(b.boundingSphere, rayOrigin); return aDist - bDist; }; @@ -268,7 +268,7 @@ define([ var scratchSphereIntersections = []; - GlobeSurface.prototype.pick = function(ray, result) { + GlobeSurface.prototype.pick = function(ray, frameState, result) { var sphereIntersections = scratchSphereIntersections; sphereIntersections.length = 0; @@ -285,9 +285,21 @@ define([ } for (var j = 0; j < tileSet.length; ++j) { tile = tileSet[j]; - var boundingSphereIntersection = IntersectionTests.raySphere(ray, tile.boundingSphere3D); + + var boundingVolume = new BoundingSphere(); + if (frameState.mode !== SceneMode.SCENE3D) { + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.scene2D.projection, tile.minimumHeight, tile.maximumHeight, boundingVolume); + Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); + } else { + BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); + } + + var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume); if (defined(boundingSphereIntersection)) { - sphereIntersections.push(tile); + sphereIntersections.push({ + tile : tile, + boundingSphere : boundingVolume + }); } } } @@ -297,8 +309,7 @@ define([ var intersection; length = sphereIntersections.length; for (i = 0; i < length; ++i) { - tile = sphereIntersections[i]; - intersection = tile.pick(ray, result); + intersection = sphereIntersections[i].tile.pick(ray, frameState, result); if (defined(intersection)) { break; } diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 89c033dae8c2..dd95ae1fe118 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -15,6 +15,7 @@ define([ '../Core/isArray', '../Core/Math', '../Core/Matrix4', + '../Core/Plane', '../Core/Ray', '../Core/Transforms', './AnimationCollection', @@ -38,6 +39,7 @@ define([ isArray, CesiumMath, Matrix4, + Plane, Ray, Transforms, AnimationCollection, @@ -588,11 +590,22 @@ define([ var ray = controller._camera.getPickRay(windowPosition, rotateCVWindowRay); var normal = Cartesian3.UNIT_X; - var position = ray.origin; - var direction = ray.direction; - var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); - var center = Cartesian3.multiplyByScalar(direction, scalar, rotateCVCenter); - Cartesian3.add(position, center, center); + var center; + + if (controller._camera.position.z < controller.minimumTerrainHeight) { + var scene = controller._scene; + var globe = scene.globe; + center = globe.pick(ray, scene.frameState); + } + + if (!defined(center)) { + var position = ray.origin; + var direction = ray.direction; + var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); + center = Cartesian3.multiplyByScalar(direction, scalar, rotateCVCenter); + Cartesian3.add(position, center, center); + } + var transform = Matrix4.fromTranslation(center, rotateTransform); var oldEllipsoid = controller._ellipsoid; @@ -661,7 +674,7 @@ define([ var globe = scene.globe; var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); - var mousePos = globe.pick(startRay); + var mousePos = globe.pick(startRay, scene.frameState); if (!defined(mousePos)) { pan3D(controller, movement, controller._ellipsoid); } else { @@ -899,8 +912,9 @@ define([ var intersection; if (height < controller.minimumTerrainHeight) { - var globe = controller._scene.globe; - center = globe.pick(ray); + var scene = controller._scene; + var globe = scene.globe; + center = globe.pick(ray, scene.frameState); } if (!defined(center)) { diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index f0381ca60912..a44e2e708b5f 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -1,6 +1,7 @@ /*global define*/ define([ '../Core/BoundingSphere', + '../Core/Cartesian2', '../Core/Cartesian3', '../Core/Cartesian4', '../Core/Cartographic', @@ -8,9 +9,13 @@ define([ '../Core/defined', '../Core/defineProperties', '../Core/DeveloperError', + '../Core/GeographicProjection', '../Core/IntersectionTests', + '../Core/Math', '../Core/Rectangle', + '../Core/WebMercatorProjection', './ImageryState', + './SceneMode', './TerrainState', './TileState', './TileTerrain', @@ -21,6 +26,7 @@ define([ '../Renderer/TextureWrap' ], function( BoundingSphere, + Cartesian2, Cartesian3, Cartesian4, Cartographic, @@ -28,9 +34,13 @@ define([ defined, defineProperties, DeveloperError, + GeographicProjection, IntersectionTests, + CesiumMath, Rectangle, + WebMercatorProjection, ImageryState, + SceneMode, TerrainState, TileState, TileTerrain, @@ -273,11 +283,26 @@ define([ } }); + function getPosition(tile, frameState, vertices, index, result) { + Cartesian3.unpack(vertices, index * 6, result); + Cartesian3.add(tile.center, result, result); + + if (frameState.mode !== SceneMode.SCENE3D) { + var projection = frameState.scene2D.projection; + var ellipsoid = projection.ellipsoid; + var positionCart = ellipsoid.cartesianToCartographic(result); + projection.project(positionCart, result); + Cartesian3.fromElements(result.z, result.x, result.y, result); + } + + return result; + } + var scratchV0 = new Cartesian3(); var scratchV1 = new Cartesian3(); var scratchV2 = new Cartesian3(); - Tile.prototype.pick = function(ray, result) { + Tile.prototype.pick = function(ray, frameState, result) { var terrain = defaultValue(this.loadedTerrain, this.upsampledTerrain); if (!defined(terrain)) { return undefined; @@ -291,21 +316,15 @@ define([ var vertices = mesh.vertices; var indices = mesh.indices; - var center = this.center; - var length = indices.length; for (var i = 0; i < length; i += 3) { var i0 = indices[i]; var i1 = indices[i + 1]; var i2 = indices[i + 2]; - var v0 = Cartesian3.unpack(vertices, i0 * 6, scratchV0); - var v1 = Cartesian3.unpack(vertices, i1 * 6, scratchV1); - var v2 = Cartesian3.unpack(vertices, i2 * 6, scratchV2); - - Cartesian3.add(center, v0, v0); - Cartesian3.add(center, v1, v1); - Cartesian3.add(center, v2, v2); + var v0 = getPosition(this, frameState, vertices, i0, scratchV0); + var v1 = getPosition(this, frameState, vertices, i1, scratchV1); + var v2 = getPosition(this, frameState, vertices, i2, scratchV2); var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, result); if (defined(intersection)) { From 24e7da088d1e636a91042a720b80b55d448e6d96 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 7 May 2014 13:04:07 -0400 Subject: [PATCH 11/74] Pick terrain in Columbus view when panning. --- Source/Scene/ScreenSpaceCameraController.js | 40 +++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index dd95ae1fe118..48c29256f076 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -549,19 +549,37 @@ define([ var camera = controller._camera; var startRay = camera.getPickRay(movement.startPosition, translateCVStartRay); var endRay = camera.getPickRay(movement.endPosition, translateCVEndRay); + + var origin = Cartesian3.clone(Cartesian3.ZERO); var normal = Cartesian3.UNIT_X; - var position = startRay.origin; - var direction = startRay.direction; - var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); - var startPlanePos = Cartesian3.multiplyByScalar(direction, scalar, translateCVStartPos); - Cartesian3.add(position, startPlanePos, startPlanePos); - - position = endRay.origin; - direction = endRay.direction; - scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); - var endPlanePos = Cartesian3.multiplyByScalar(direction, scalar, translateCVEndPos); - Cartesian3.add(position, endPlanePos, endPlanePos); + var startPlanePos; + + if (controller._camera.position.z < controller.minimumTerrainHeight) { + var scene = controller._scene; + var globe = scene.globe; + startPlanePos = globe.pick(startRay, scene.frameState); + + if (defined(startPlanePos)) { + origin.x = startPlanePos.x; + } + } + + if (!defined(startPlanePos)) { + var position = startRay.origin; + var direction = startRay.direction; + + var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); + startPlanePos = Cartesian3.multiplyByScalar(direction, scalar, translateCVStartPos); + Cartesian3.add(position, startPlanePos, startPlanePos); + } + + var plane = Plane.fromPointNormal(origin, normal); + var endPlanePos = IntersectionTests.rayPlane(endRay, plane); + + if (!defined(startPlanePos) || !defined(endPlanePos)) { + return; + } var diff = Cartesian3.subtract(startPlanePos, endPlanePos, translatCVDifference); var temp = diff.x; From 3f8892192c9a2671c27ce4f2b08854e47d2ec4b7 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 7 May 2014 13:58:03 -0400 Subject: [PATCH 12/74] Some clean up. --- Apps/Sandcastle/gallery/Terrain.html | 26 ------- Source/Core/Ellipsoid.js | 84 +++++++++++++-------- Source/Core/IntersectionTests.js | 27 +++---- Source/Scene/Globe.js | 14 ++++ Source/Scene/GlobeSurface.js | 33 +++++--- Source/Scene/ScreenSpaceCameraController.js | 32 +++++--- Source/Scene/Tile.js | 2 + 7 files changed, 128 insertions(+), 90 deletions(-) diff --git a/Apps/Sandcastle/gallery/Terrain.html b/Apps/Sandcastle/gallery/Terrain.html index e97a736196b9..356e7670dc71 100644 --- a/Apps/Sandcastle/gallery/Terrain.html +++ b/Apps/Sandcastle/gallery/Terrain.html @@ -198,32 +198,6 @@ scene.terrainProvider = cesiumTerrainProviderMeshes; createTerrainMenu(terrainProviders); - - var labels = new Cesium.LabelCollection(); - var label = labels.add(); - scene.primitives.add(labels); - - var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); - handler.setInputAction(function(movement) { - var ray = scene.camera.getPickRay(movement.position); - var cartesian = globe.pick(ray, scene.frameState); - if (cartesian) { - var cartographic; - if (scene.frameState.mode === Cesium.SceneMode.SCENE3D) { - cartographic = globe.ellipsoid.cartesianToCartographic(cartesian); - } else { - cartesian = Cesium.Cartesian3.fromElements(cartesian.y, cartesian.z, cartesian.x); - cartographic = scene.scene2D.projection.unproject(cartesian); - cartesian = globe.ellipsoid.cartographicToCartesian(cartographic); - } - - label.show = true; - label.text = '(' + Cesium.Math.toDegrees(cartographic.longitude).toFixed(2) + ', ' + Cesium.Math.toDegrees(cartographic.latitude).toFixed(2) + ', ' + cartographic.height.toFixed(2) + ')'; - label.position = cartesian; - } else { - label.text = ''; - } - }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); Sandcastle.finishedLoading(); }); diff --git a/Source/Core/Ellipsoid.js b/Source/Core/Ellipsoid.js index a9f3d9a3de36..1ec67fdde137 100644 --- a/Source/Core/Ellipsoid.js +++ b/Source/Core/Ellipsoid.js @@ -19,28 +19,7 @@ define([ Cartographic) { "use strict"; - /** - * A quadratic surface defined in Cartesian coordinates by the equation - * (x / a)^2 + (y / b)^2 + (z / c)^2 = 1. Primarily used - * by Cesium to represent the shape of planetary bodies. - * - * Rather than constructing this object directly, one of the provided - * constants is normally used. - * @alias Ellipsoid - * @constructor - * @immutable - * - * @param {Number} [x=0] The radius in the x direction. - * @param {Number} [y=0] The radius in the y direction. - * @param {Number} [z=0] The radius in the z direction. - * - * @exception {DeveloperError} All radii components must be greater than or equal to zero. - * - * @see Ellipsoid.fromCartesian3 - * @see Ellipsoid.WGS84 - * @see Ellipsoid.UNIT_SPHERE - */ - var Ellipsoid = function(x, y, z) { + function initialize(ellipsoid, x, y, z) { x = defaultValue(x, 0.0); y = defaultValue(y, 0.0); z = defaultValue(z, 0.0); @@ -51,29 +30,62 @@ define([ } //>>includeEnd('debug'); - this._radii = new Cartesian3(x, y, z); + ellipsoid._radii = new Cartesian3(x, y, z); - this._radiiSquared = new Cartesian3(x * x, + ellipsoid._radiiSquared = new Cartesian3(x * x, y * y, z * z); - this._radiiToTheFourth = new Cartesian3(x * x * x * x, + ellipsoid._radiiToTheFourth = new Cartesian3(x * x * x * x, y * y * y * y, z * z * z * z); - this._oneOverRadii = new Cartesian3(x === 0.0 ? 0.0 : 1.0 / x, + ellipsoid._oneOverRadii = new Cartesian3(x === 0.0 ? 0.0 : 1.0 / x, y === 0.0 ? 0.0 : 1.0 / y, z === 0.0 ? 0.0 : 1.0 / z); - this._oneOverRadiiSquared = new Cartesian3(x === 0.0 ? 0.0 : 1.0 / (x * x), + ellipsoid._oneOverRadiiSquared = new Cartesian3(x === 0.0 ? 0.0 : 1.0 / (x * x), y === 0.0 ? 0.0 : 1.0 / (y * y), z === 0.0 ? 0.0 : 1.0 / (z * z)); - this._minimumRadius = Math.min(x, y, z); + ellipsoid._minimumRadius = Math.min(x, y, z); - this._maximumRadius = Math.max(x, y, z); + ellipsoid._maximumRadius = Math.max(x, y, z); - this._centerToleranceSquared = CesiumMath.EPSILON1; + ellipsoid._centerToleranceSquared = CesiumMath.EPSILON1; + } + + /** + * A quadratic surface defined in Cartesian coordinates by the equation + * (x / a)^2 + (y / b)^2 + (z / c)^2 = 1. Primarily used + * by Cesium to represent the shape of planetary bodies. + * + * Rather than constructing this object directly, one of the provided + * constants is normally used. + * @alias Ellipsoid + * @constructor + * + * @param {Number} [x=0] The radius in the x direction. + * @param {Number} [y=0] The radius in the y direction. + * @param {Number} [z=0] The radius in the z direction. + * + * @exception {DeveloperError} All radii components must be greater than or equal to zero. + * + * @see Ellipsoid.fromCartesian3 + * @see Ellipsoid.WGS84 + * @see Ellipsoid.UNIT_SPHERE + */ + var Ellipsoid = function(x, y, z) { + this._radii = undefined; + this._radiiSquared = undefined; + this._radiiToTheFourth = undefined; + this._oneOverRadii = undefined; + this._oneOverRadiiSquared = undefined; + this._minimumRadius = undefined; + this._maximumRadius = undefined; + this._centerToleranceSquared = undefined; + + initialize(this, x, y, z); }; defineProperties(Ellipsoid.prototype, { @@ -192,11 +204,17 @@ define([ * @see Ellipsoid.WGS84 * @see Ellipsoid.UNIT_SPHERE */ - Ellipsoid.fromCartesian3 = function(cartesian) { + Ellipsoid.fromCartesian3 = function(cartesian, result) { + if (!defined(result)) { + result = new Ellipsoid(); + } + if (!defined(cartesian)) { - return new Ellipsoid(); + return result; } - return new Ellipsoid(cartesian.x, cartesian.y, cartesian.z); + + initialize(result, cartesian.x, cartesian.y, cartesian.z); + return result; }; /** diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index e0f3a13e4da4..f63a2e0f4679 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -170,9 +170,10 @@ define([ * * @param {Ray} ray The ray. * @param {BoundingSphere} sphere The sphere. + * @param {Object} [result] The result onto which to store the result. * @returns {Object} An object with the first (start) and the second (stop) intersection scalars for points along the ray or undefined if there are no intersections. */ - IntersectionTests.raySphere = function(ray, sphere) { + IntersectionTests.raySphere = function(ray, sphere, result) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError('ray is required.'); @@ -182,6 +183,10 @@ define([ } //>>includeEnd('debug'); + if (!defined(result)) { + result = {}; + } + var origin = ray.origin; var direction = ray.direction; @@ -212,16 +217,14 @@ define([ } if (root0 < root1) { - return { - start : root0, - stop : root1 - }; + result.start = root0; + result.stop = root1; + } else { + result.start = root1; + result.stop = root0; } - return { - start : root1, - stop : root0 - }; + return result; } var root = -b / (2.0 * a); @@ -229,10 +232,8 @@ define([ return undefined; } - return { - start : root, - stop : root - }; + result.start = result.stop = root; + return result; }; var scratchQ = new Cartesian3(); diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index bf20e645fb4f..4c6b0776db57 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -292,6 +292,20 @@ define([ } }); + /** + * Find an intersection between a ray and the globe. + * @memberof Globe + * + * @param {Ray} ray The ray to test for intersection. + * @param {FrameState} frameState The current frame state. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3|undefined} The intersection of undefined if none was found. + * + * @example + * // find intersection of ray through a pixel and the globe + * var ray = scene.camera.getPickRay(windowCoordinates); + * var intersection = globe.pick(ray, scene.frameState); + */ Globe.prototype.pick = function(ray, frameState, result) { return this._surface.pick(ray, frameState, result); }; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 7e0eb12bd498..fad0c8612284 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -259,15 +259,33 @@ define([ function createComparePickTileFunction(rayOrigin) { return function(a, b) { - var aDist = BoundingSphere.distanceSquaredTo(a.boundingSphere, rayOrigin); - var bDist = BoundingSphere.distanceSquaredTo(b.boundingSphere, rayOrigin); + var aDist = BoundingSphere.distanceSquaredTo(a.pickBoundingSphere, rayOrigin); + var bDist = BoundingSphere.distanceSquaredTo(b.pickBoundingSphere, rayOrigin); return aDist - bDist; }; } var scratchSphereIntersections = []; + var scratchSphereIntersectionResult = { + start : 0.0, + stop : 0.0 + }; + /** + * Find an intersection between a ray and the globe surface. + * @memberof GlobeSurface + * + * @param {Ray} ray The ray to test for intersection. + * @param {FrameState} frameState The current frame state. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3|undefined} The intersection of undefined if none was found. + * + * @example + * // find intersection of ray through a pixel and the globe + * var ray = scene.camera.getPickRay(windowCoordinates); + * var intersection = surface.pick(ray, scene.frameState); + */ GlobeSurface.prototype.pick = function(ray, frameState, result) { var sphereIntersections = scratchSphereIntersections; sphereIntersections.length = 0; @@ -286,7 +304,7 @@ define([ for (var j = 0; j < tileSet.length; ++j) { tile = tileSet[j]; - var boundingVolume = new BoundingSphere(); + var boundingVolume = tile.pickBoundingSphere; if (frameState.mode !== SceneMode.SCENE3D) { BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.scene2D.projection, tile.minimumHeight, tile.maximumHeight, boundingVolume); Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); @@ -294,12 +312,9 @@ define([ BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); } - var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume); + var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); if (defined(boundingSphereIntersection)) { - sphereIntersections.push({ - tile : tile, - boundingSphere : boundingVolume - }); + sphereIntersections.push(tile); } } } @@ -309,7 +324,7 @@ define([ var intersection; length = sphereIntersections.length; for (i = 0; i < length; ++i) { - intersection = sphereIntersections[i].tile.pick(ray, frameState, result); + intersection = sphereIntersections[i].pick(ray, frameState, result); if (defined(intersection)) { break; } diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 48c29256f076..52cc033e14ef 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -230,6 +230,11 @@ define([ eventType : CameraEventType.LEFT_DRAG, modifier : KeyboardEventModifier.SHIFT }; + /** + * The minimum height the camera must be before testing for intersection with the terrain instead of the ellipsoid. + * @type {Number} + * @default 150000.0 + */ this.minimumTerrainHeight = 150000.0; this._scene = scene; @@ -545,12 +550,15 @@ define([ var translateCVStartPos = new Cartesian3(); var translateCVEndPos = new Cartesian3(); var translatCVDifference = new Cartesian3(); + var translateCVOrigin = new Cartesian3(); + var translateCVPlane = new Plane(Cartesian3.ZERO, 0.0); + function translateCV(controller, movement) { var camera = controller._camera; var startRay = camera.getPickRay(movement.startPosition, translateCVStartRay); var endRay = camera.getPickRay(movement.endPosition, translateCVEndRay); - var origin = Cartesian3.clone(Cartesian3.ZERO); + var origin = Cartesian3.clone(Cartesian3.ZERO, translateCVOrigin); var normal = Cartesian3.UNIT_X; var startPlanePos; @@ -558,7 +566,7 @@ define([ if (controller._camera.position.z < controller.minimumTerrainHeight) { var scene = controller._scene; var globe = scene.globe; - startPlanePos = globe.pick(startRay, scene.frameState); + startPlanePos = globe.pick(startRay, scene.frameState, translateCVStartPos); if (defined(startPlanePos)) { origin.x = startPlanePos.x; @@ -574,8 +582,8 @@ define([ Cartesian3.add(position, startPlanePos, startPlanePos); } - var plane = Plane.fromPointNormal(origin, normal); - var endPlanePos = IntersectionTests.rayPlane(endRay, plane); + var plane = Plane.fromPointNormal(origin, normal, translateCVPlane); + var endPlanePos = IntersectionTests.rayPlane(endRay, plane, translateCVEndPos); if (!defined(startPlanePos) || !defined(endPlanePos)) { return; @@ -613,7 +621,7 @@ define([ if (controller._camera.position.z < controller.minimumTerrainHeight) { var scene = controller._scene; var globe = scene.globe; - center = globe.pick(ray, scene.frameState); + center = globe.pick(ray, scene.frameState, rotateCVCenter); } if (!defined(center)) { @@ -683,21 +691,27 @@ define([ var spin3DPick = new Cartesian3(); var scratchStartRay = new Ray(); + var scratchCartographic = new Cartographic(); + var scratchMousePos = new Cartesian3(); + var scratchRadii = new Cartesian3(); + var scratchEllipsoid = new Ellipsoid(); function spin3D(controller, movement) { if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { - var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC).height; + var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; if (height < controller.minimumTerrainHeight) { var scene = controller._scene; var globe = scene.globe; var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); - var mousePos = globe.pick(startRay, scene.frameState); + var mousePos = globe.pick(startRay, scene.frameState, scratchMousePos); if (!defined(mousePos)) { pan3D(controller, movement, controller._ellipsoid); } else { var magnitude = Cartesian3.magnitude(mousePos); - var ellipsoid = new Ellipsoid(magnitude, magnitude, magnitude); + var radii = scratchRadii; + radii.x = radii.y = radii.z = magnitude; + var ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); pan3D(controller, movement, ellipsoid); } } else { @@ -932,7 +946,7 @@ define([ if (height < controller.minimumTerrainHeight) { var scene = controller._scene; var globe = scene.globe; - center = globe.pick(ray, scene.frameState); + center = globe.pick(ray, scene.frameState, tilt3DCenter); } if (!defined(center)) { diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index a44e2e708b5f..7a64b60cf103 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -236,6 +236,8 @@ define([ this.loadedTerrain = undefined; this.upsampledTerrain = undefined; + + this.pickBoundingSphere = new BoundingSphere(); }; defineProperties(Tile.prototype, { From 76f2bf6edbd877e64073358e1a42ae41f1b89dd6 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 7 May 2014 17:40:50 -0400 Subject: [PATCH 13/74] Some more clean up. --- Source/Scene/Scene.js | 4 +- Source/Scene/ScreenSpaceCameraController.js | 160 +++++++++++--------- Source/Widgets/CesiumWidget/CesiumWidget.js | 1 + 3 files changed, 94 insertions(+), 71 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 81fb557d1484..d331c9b7afcc 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -141,7 +141,7 @@ define([ this._primitives = new CompositePrimitive(); this._pickFramebuffer = undefined; this._camera = new Camera(this); - this._screenSpaceCameraController = new ScreenSpaceCameraController(this); + this._screenSpaceCameraController = new ScreenSpaceCameraController(canvas, this._camera); this._animations = new AnimationCollection(); @@ -1187,7 +1187,7 @@ define([ this._animations.update(); this._camera.update(this.mode, this.scene2D); - this._screenSpaceCameraController.update(this.mode); + this._screenSpaceCameraController.update(this._frameState); }; function render(scene, time) { diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 52cc033e14ef..cf7addbc146e 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', '../Core/destroyObject', @@ -24,6 +25,7 @@ define([ './CameraColumbusViewMode', './SceneMode' ], function( + defaultValue, defined, defineProperties, destroyObject, @@ -54,12 +56,17 @@ define([ * @alias ScreenSpaceCameraController * @constructor * - * @param {Scene} scene The scene. + * @param {HTMLCanvas} canvas The canvas. + * @param {Camera} camera The camera. + * @param {Globe|Ellipsoid} [globeOrEllipsoid=Ellipsoid.WGS84] The globe or ellipsoid to use to determine camera movement direction and speed. */ - var ScreenSpaceCameraController = function(scene) { + var ScreenSpaceCameraController = function(canvas, camera, globeOrEllipsoid) { //>>includeStart('debug', pragmas.debug); - if (!defined(scene)) { - throw new DeveloperError('scene is required.'); + if (!defined(canvas)) { + throw new DeveloperError('canvas is required.'); + } + if (!defined(camera)) { + throw new DeveloperError('camera is required.'); } //>>includeEnd('debug'); @@ -237,10 +244,22 @@ define([ */ this.minimumTerrainHeight = 150000.0; - this._scene = scene; - this._camera = scene.camera; - this._canvas = scene.canvas; - this._ellipsoid = Ellipsoid.WGS84; + this._camera = camera; + this._canvas = canvas; + + var globe; + var ellipsoid; + + globeOrEllipsoid = defaultValue(globeOrEllipsoid, Ellipsoid.WGS84); + if (defined(globeOrEllipsoid.ellipsoid)) { + globe = globeOrEllipsoid; + ellipsoid = globe.ellipsoid; + } else { + ellipsoid = globeOrEllipsoid; + } + + this._globe = globe; + this._ellipsoid = ellipsoid; this._aggregator = new CameraEventAggregator(this._canvas); @@ -269,23 +288,36 @@ define([ defineProperties(ScreenSpaceCameraController.prototype, { /** - * Gets and sets the ellipsoid. The ellipsoid is used to determine the size of the map in 2D and Columbus view + * Gets and sets the globe. The globe is used to determine the size of the map in 2D and Columbus view * as well as how fast to rotate the camera based on the distance to its surface. * @memberof ScreenSpaceCameraController.prototype - * @type {Ellipsoid} + * @type {Globe|Ellipsoid} */ - ellipsoid : { + globe : { get : function() { + if (defined(this._globe)) { + return this._globe; + } + return this._ellipsoid; }, - set : function(ellipsoid) { + set : function(globe) { //>>includeStart('debug', pragmas.debug); - if (!defined(ellipsoid)) { - throw new DeveloperError('ellipsoid is required'); + if (!defined(globe)) { + throw new DeveloperError('globe is required'); } //>>includeEnd('debug'); - var radius = ellipsoid.maximumRadius; + + var ellipsoid = globe.ellipsoid; + if (!defined(ellipsoid)) { + ellipsoid = globe; + globe = undefined; + } + + this._globe = globe; this._ellipsoid = ellipsoid; + + var radius = ellipsoid.maximumRadius; this._rotateFactor = 1.0 / radius; this._rotateRateRangeAdjustment = radius; } @@ -311,7 +343,7 @@ define([ // hardware. Should be investigated further. var inertiaMaxClickTimeThreshold = 0.4; - function maintainInertia(aggregator, type, modifier, decayCoef, action, object, lastMovementName) { + function maintainInertia(aggregator, frameState, type, modifier, decayCoef, action, object, lastMovementName) { var movementState = object[lastMovementName]; if (!defined(movementState)) { movementState = object[lastMovementName] = { @@ -364,7 +396,7 @@ define([ } if (!aggregator.isButtonDown(type, modifier)) { - action(object, movementState); + action(object, movementState, frameState); } } else { movementState.active = false; @@ -373,7 +405,7 @@ define([ var scratchEventTypeArray = []; - function reactToInput(controller, enabled, eventTypes, action, inertiaConstant, inertiaStateName) { + function reactToInput(controller, frameState, enabled, eventTypes, action, inertiaConstant, inertiaStateName) { if (!defined(eventTypes)) { return; } @@ -395,9 +427,9 @@ define([ if (controller.enableInputs && enabled) { if (movement) { - action(controller, movement); + action(controller, movement, frameState); } else if (inertiaConstant < 1.0) { - maintainInertia(aggregator, type, modifier, inertiaConstant, action, controller, inertiaStateName); + maintainInertia(aggregator, frameState, type, modifier, inertiaConstant, action, controller, inertiaStateName); } } } @@ -523,14 +555,14 @@ define([ controller._camera.twistRight(deltaPhi); } - function update2D(controller) { + function update2D(controller, frameState) { if (controller._aggregator.anyButtonDown()) { controller._animationCollection.removeAll(); } - reactToInput(controller, controller.enableTranslate, controller.translateEventTypes, translate2D, controller.inertiaTranslate, '_lastInertiaTranslateMovement'); - reactToInput(controller, controller.enableZoom, controller.zoomEventTypes, zoom2D, controller.inertiaZoom, '_lastInertiaZoomMovement'); - reactToInput(controller, controller.enableRotate, controller.tiltEventTypes, twist2D, controller.inertiaSpin, '_lastInertiaTiltMovement'); + reactToInput(controller, frameState, controller.enableTranslate, controller.translateEventTypes, translate2D, controller.inertiaTranslate, '_lastInertiaTranslateMovement'); + reactToInput(controller, frameState, controller.enableZoom, controller.zoomEventTypes, zoom2D, controller.inertiaZoom, '_lastInertiaZoomMovement'); + reactToInput(controller, frameState, controller.enableRotate, controller.tiltEventTypes, twist2D, controller.inertiaSpin, '_lastInertiaTiltMovement'); if (!controller._aggregator.anyButtonDown() && (!defined(controller._lastInertiaZoomMovement) || !controller._lastInertiaZoomMovement.active) && @@ -553,7 +585,7 @@ define([ var translateCVOrigin = new Cartesian3(); var translateCVPlane = new Plane(Cartesian3.ZERO, 0.0); - function translateCV(controller, movement) { + function translateCV(controller, movement, frameState) { var camera = controller._camera; var startRay = camera.getPickRay(movement.startPosition, translateCVStartRay); var endRay = camera.getPickRay(movement.endPosition, translateCVEndRay); @@ -563,11 +595,8 @@ define([ var startPlanePos; - if (controller._camera.position.z < controller.minimumTerrainHeight) { - var scene = controller._scene; - var globe = scene.globe; - startPlanePos = globe.pick(startRay, scene.frameState, translateCVStartPos); - + if (defined(controller._globe) && controller._camera.position.z < controller.minimumTerrainHeight) { + startPlanePos = controller._globe.pick(startRay, frameState, translateCVStartPos); if (defined(startPlanePos)) { origin.x = startPlanePos.x; } @@ -605,7 +634,7 @@ define([ var rotateCVWindowRay = new Ray(); var rotateCVCenter = new Cartesian3(); var rotateTransform = new Matrix4(); - function rotateCV(controller, movement) { + function rotateCV(controller, movement, frameState) { if (defined(movement.angleAndHeight)) { movement = movement.angleAndHeight; } @@ -618,10 +647,8 @@ define([ var center; - if (controller._camera.position.z < controller.minimumTerrainHeight) { - var scene = controller._scene; - var globe = scene.globe; - center = globe.pick(ray, scene.frameState, rotateCVCenter); + if (defined(controller._globe) && controller._camera.position.z < controller.minimumTerrainHeight) { + center = controller._globe.pick(ray, frameState, rotateCVCenter); } if (!defined(center)) { @@ -634,12 +661,12 @@ define([ var transform = Matrix4.fromTranslation(center, rotateTransform); - var oldEllipsoid = controller._ellipsoid; - controller.ellipsoid = Ellipsoid.UNIT_SPHERE; + var oldGlobe = controller.globe; + controller.globe = Ellipsoid.UNIT_SPHERE; rotate3D(controller, movement, transform, Cartesian3.UNIT_Z); - controller.ellipsoid = oldEllipsoid; + controller.globe = oldGlobe; } var zoomCVWindowPos = new Cartesian2(); @@ -662,19 +689,19 @@ define([ handleZoom(controller, movement, controller._zoomFactor, scalar); } - function updateCV(controller) { + function updateCV(controller, frameState) { if (controller.columbusViewMode === CameraColumbusViewMode.LOCKED) { - reactToInput(controller, controller.enableRotate, controller.rotateEventTypes, rotate3D, controller.inertiaSpin, '_lastInertiaSpinMovement'); - reactToInput(controller, controller.enableZoom, controller.zoomEventTypes, zoom3D, controller.inertiaZoom, '_lastInertiaZoomMovement'); + reactToInput(controller, frameState, controller.enableRotate, controller.rotateEventTypes, rotate3D, controller.inertiaSpin, '_lastInertiaSpinMovement'); + reactToInput(controller, frameState, controller.enableZoom, controller.zoomEventTypes, zoom3D, controller.inertiaZoom, '_lastInertiaZoomMovement'); } else { if (controller._aggregator.anyButtonDown()) { controller._animationCollection.removeAll(); } - reactToInput(controller, controller.enableTilt, controller.tiltEventTypes, rotateCV, controller.inertiaSpin, '_lastInertiaTiltMovement'); - reactToInput(controller, controller.enableTranslate, controller.translateEventTypes, translateCV, controller.inertiaTranslate, '_lastInertiaTranslateMovement'); - reactToInput(controller, controller.enableZoom, controller.zoomEventTypes, zoomCV, controller.inertiaZoom, '_lastInertiaZoomMovement'); - reactToInput(controller, controller.enableLook, controller.lookEventTypes, look3D); + reactToInput(controller, frameState, controller.enableTilt, controller.tiltEventTypes, rotateCV, controller.inertiaSpin, '_lastInertiaTiltMovement'); + reactToInput(controller, frameState, controller.enableTranslate, controller.translateEventTypes, translateCV, controller.inertiaTranslate, '_lastInertiaTranslateMovement'); + reactToInput(controller, frameState, controller.enableZoom, controller.zoomEventTypes, zoomCV, controller.inertiaZoom, '_lastInertiaZoomMovement'); + reactToInput(controller, frameState, controller.enableLook, controller.lookEventTypes, look3D); if (!controller._aggregator.anyButtonDown() && (!defined(controller._lastInertiaZoomMovement) || !controller._lastInertiaZoomMovement.active) && (!defined(controller._lastInertiaTranslateMovement) || !controller._lastInertiaTranslateMovement.active) && @@ -696,15 +723,12 @@ define([ var scratchRadii = new Cartesian3(); var scratchEllipsoid = new Ellipsoid(); - function spin3D(controller, movement) { + function spin3D(controller, movement, frameState) { if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; - if (height < controller.minimumTerrainHeight) { - var scene = controller._scene; - var globe = scene.globe; - + if (defined(controller._globe) && height < controller.minimumTerrainHeight) { var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); - var mousePos = globe.pick(startRay, scene.frameState, scratchMousePos); + var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); if (!defined(mousePos)) { pan3D(controller, movement, controller._ellipsoid); } else { @@ -823,7 +847,6 @@ define([ return; } - // CAMERA TODO: remove access to camera p0 = camera.worldToCameraCoordinates(p0, p0); p1 = camera.worldToCameraCoordinates(p1, p1); @@ -920,7 +943,7 @@ define([ var tilt3DCenter = Cartesian4.clone(Cartesian4.UNIT_W); var tilt3DTransform = new Matrix4(); - function tilt3D(controller, movement) { + function tilt3D(controller, movement, frameState) { if (defined(movement.angleAndHeight)) { movement = movement.angleAndHeight; } @@ -943,10 +966,8 @@ define([ var center; var intersection; - if (height < controller.minimumTerrainHeight) { - var scene = controller._scene; - var globe = scene.globe; - center = globe.pick(ray, scene.frameState, tilt3DCenter); + if (defined(controller._globe) && height < controller.minimumTerrainHeight) { + center = controller._globe.pick(ray, frameState, tilt3DCenter); } if (!defined(center)) { @@ -966,13 +987,13 @@ define([ var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); - var oldEllipsoid = controller._ellipsoid; - controller.ellipsoid = Ellipsoid.UNIT_SPHERE; + var oldGlobe = controller.globe; + controller.globe = Ellipsoid.UNIT_SPHERE; var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); rotate3D(controller, movement, transform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle); - controller.ellipsoid = oldEllipsoid; + controller.globe = oldGlobe; } var look3DStartPos = new Cartesian2(); @@ -1020,25 +1041,26 @@ define([ camera.lookUp(angle); } - function update3D(controller) { - reactToInput(controller, controller.enableRotate, controller.rotateEventTypes, spin3D, controller.inertiaSpin, '_lastInertiaSpinMovement'); - reactToInput(controller, controller.enableZoom, controller.zoomEventTypes, zoom3D, controller.inertiaZoom, '_lastInertiaZoomMovement'); - reactToInput(controller, controller.enableTilt, controller.tiltEventTypes, tilt3D, controller.inertiaSpin, '_lastInertiaTiltMovement'); - reactToInput(controller, controller.enableLook, controller.lookEventTypes, look3D); + function update3D(controller, frameState) { + reactToInput(controller, frameState, controller.enableRotate, controller.rotateEventTypes, spin3D, controller.inertiaSpin, '_lastInertiaSpinMovement'); + reactToInput(controller, frameState, controller.enableZoom, controller.zoomEventTypes, zoom3D, controller.inertiaZoom, '_lastInertiaZoomMovement'); + reactToInput(controller, frameState, controller.enableTilt, controller.tiltEventTypes, tilt3D, controller.inertiaSpin, '_lastInertiaTiltMovement'); + reactToInput(controller, frameState, controller.enableLook, controller.lookEventTypes, look3D); } /** * @private */ - ScreenSpaceCameraController.prototype.update = function(mode) { + ScreenSpaceCameraController.prototype.update = function(frameState) { + var mode = frameState.mode; if (mode === SceneMode.SCENE2D) { - update2D(this); + update2D(this, frameState); } else if (mode === SceneMode.COLUMBUS_VIEW) { this._horizontalRotationAxis = Cartesian3.UNIT_Z; - updateCV(this); + updateCV(this, frameState); } else if (mode === SceneMode.SCENE3D) { this._horizontalRotationAxis = undefined; - update3D(this); + update3D(this, frameState); } this._aggregator.reset(); diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.js b/Source/Widgets/CesiumWidget/CesiumWidget.js index d03b2eeb2e6a..6ea7e233ab42 100644 --- a/Source/Widgets/CesiumWidget/CesiumWidget.js +++ b/Source/Widgets/CesiumWidget/CesiumWidget.js @@ -191,6 +191,7 @@ define([ var globe = new Globe(ellipsoid); scene.globe = globe; + scene.screenSpaceCameraController.globe = globe; var skyBox = options.skyBox; if (!defined(skyBox)) { From 04c946422c5ce0f44c54e4ff9777927e7a8a83f0 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 7 May 2014 18:54:19 -0400 Subject: [PATCH 14/74] Fix Sandcastle examples, dynamic object view, and widgets. --- Apps/Sandcastle/gallery/3D Models.html | 2 +- Apps/Sandcastle/gallery/Camera.html | 4 +-- Source/DynamicScene/DynamicObjectView.js | 4 +-- Source/Widgets/Geocoder/GeocoderViewModel.js | 18 +------------ .../Widgets/HomeButton/HomeButtonViewModel.js | 27 ++++--------------- 5 files changed, 11 insertions(+), 44 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index d356f054ebd7..1c0c00199e96 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -57,7 +57,7 @@ camera.transform = transform; camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z; var controller = scene.screenSpaceCameraController; - controller.ellipsoid = Cesium.Ellipsoid.UNIT_SPHERE; + controller.globe = Cesium.Ellipsoid.UNIT_SPHERE; controller.enableTilt = false; var r = 1.25 * Math.max(model.boundingSphere.radius, camera.frustum.near); controller.minimumZoomDistance = r * 0.25; diff --git a/Apps/Sandcastle/gallery/Camera.html b/Apps/Sandcastle/gallery/Camera.html index 699de7fa7a4f..b7242d113fbc 100644 --- a/Apps/Sandcastle/gallery/Camera.html +++ b/Apps/Sandcastle/gallery/Camera.html @@ -119,7 +119,7 @@ camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z; var controller = scene.screenSpaceCameraController; - controller.ellipsoid = Cesium.Ellipsoid.UNIT_SPHERE; + controller.globe = Cesium.Ellipsoid.UNIT_SPHERE; controller.enableTilt = false; // Zoom in @@ -180,7 +180,7 @@ Cesium.Cartesian3.UNIT_Z); var controller = scene.screenSpaceCameraController; - controller.ellipsoid = ellipsoid; + controller.globe = scene.globe; controller.enableTilt = true; } diff --git a/Source/DynamicScene/DynamicObjectView.js b/Source/DynamicScene/DynamicObjectView.js index 35fba5f41fb8..e4f950d83c1f 100644 --- a/Source/DynamicScene/DynamicObjectView.js +++ b/Source/DynamicScene/DynamicObjectView.js @@ -157,7 +157,7 @@ define([ camera.transform = Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, update3DTransform); } - that._screenSpaceCameraController.ellipsoid = Ellipsoid.UNIT_SPHERE; + that._screenSpaceCameraController.globe = Ellipsoid.UNIT_SPHERE; var position = camera.position; Cartesian3.clone(position, that._lastOffset); @@ -184,7 +184,7 @@ define([ var controller = that._screenSpaceCameraController; controller.enableTranslate = false; - controller.ellipsoid = Ellipsoid.UNIT_SPHERE; + controller.globe = Ellipsoid.UNIT_SPHERE; controller.columbusViewMode = CameraColumbusViewMode.LOCKED; var position = camera.position; diff --git a/Source/Widgets/Geocoder/GeocoderViewModel.js b/Source/Widgets/Geocoder/GeocoderViewModel.js index bd0ea1693c18..5ef413f20947 100644 --- a/Source/Widgets/Geocoder/GeocoderViewModel.js +++ b/Source/Widgets/Geocoder/GeocoderViewModel.js @@ -5,7 +5,6 @@ define([ '../../Core/defined', '../../Core/defineProperties', '../../Core/DeveloperError', - '../../Core/Ellipsoid', '../../Core/Rectangle', '../../Core/jsonp', '../../Core/Matrix4', @@ -21,7 +20,6 @@ define([ defined, defineProperties, DeveloperError, - Ellipsoid, Rectangle, jsonp, Matrix4, @@ -47,7 +45,6 @@ define([ * written to the console reminding you that you must create and supply a Bing Maps * key as soon as possible. Please do not deploy an application that uses * this widget without creating a separate key for your application. - * @param {Ellipsoid} [description.ellipsoid=Ellipsoid.WGS84] The Scene's primary ellipsoid. * @param {Number} [description.flightDuration=1500] The duration of the camera flight to an entered location, in milliseconds. */ var GeocoderViewModel = function(description) { @@ -64,7 +61,6 @@ define([ this._key = BingMapsApi.getKey(description.key); this._scene = description.scene; - this._ellipsoid = defaultValue(description.ellipsoid, Ellipsoid.WGS84); this._flightDuration = defaultValue(description.flightDuration, 1500); this._searchText = ''; this._isSearchInProgress = false; @@ -178,18 +174,6 @@ define([ } }, - /** - * Gets the ellipsoid to be viewed. - * @memberof GeocoderViewModel.prototype - * - * @type {Ellipsoid} - */ - ellipsoid : { - get : function() { - return this._ellipsoid; - } - }, - /** * Gets the Command that is executed when the button is clicked. * @memberof GeocoderViewModel.prototype @@ -266,7 +250,7 @@ define([ duration : viewModel._flightDuration, onComplete : function() { var screenSpaceCameraController = viewModel._scene.screenSpaceCameraController; - screenSpaceCameraController.ellipsoid = viewModel._ellipsoid; + screenSpaceCameraController.globe = viewModel._scene.globe; screenSpaceCameraController.columbusViewMode = CameraColumbusViewMode.FREE; }, endReferenceFrame : (viewModel._scene.mode !== SceneMode.SCENE3D) ? transform2D : Matrix4.IDENTITY diff --git a/Source/Widgets/HomeButton/HomeButtonViewModel.js b/Source/Widgets/HomeButton/HomeButtonViewModel.js index b9e9bee58f0c..45de15c500fc 100644 --- a/Source/Widgets/HomeButton/HomeButtonViewModel.js +++ b/Source/Widgets/HomeButton/HomeButtonViewModel.js @@ -5,7 +5,6 @@ define([ '../../Core/defined', '../../Core/defineProperties', '../../Core/DeveloperError', - '../../Core/Ellipsoid', '../../Core/Rectangle', '../../Core/Matrix4', '../../Scene/Camera', @@ -20,7 +19,6 @@ define([ defined, defineProperties, DeveloperError, - Ellipsoid, Rectangle, Matrix4, Camera, @@ -31,11 +29,11 @@ define([ knockout) { "use strict"; - function viewHome(scene, ellipsoid, duration) { + function viewHome(scene, duration) { var mode = scene.mode; var controller = scene.screenSpaceCameraController; - controller.ellipsoid = ellipsoid; + controller.globe = scene.globe; controller.columbusViewMode = CameraColumbusViewMode.FREE; if (defined(scene) && mode === SceneMode.MORPHING) { @@ -67,7 +65,7 @@ define([ flight = CameraFlightPath.createAnimation(scene, description); scene.animations.add(flight); } else if (mode === SceneMode.COLUMBUS_VIEW) { - var maxRadii = ellipsoid.maximumRadius; + var maxRadii = scene.globe.ellipsoid.maximumRadius; var position = Cartesian3.multiplyByScalar(Cartesian3.normalize(new Cartesian3(0.0, -1.0, 1.0)), 5.0 * maxRadii); var direction = Cartesian3.normalize(Cartesian3.subtract(Cartesian3.ZERO, position)); var right = Cartesian3.cross(direction, Cartesian3.UNIT_Z); @@ -95,26 +93,23 @@ define([ * @constructor * * @param {Scene} scene The scene instance to use. - * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to be viewed when in home position. * @param {Number} [duration=1500] The duration of the camera flight in milliseconds */ - var HomeButtonViewModel = function(scene, ellipsoid, duration) { + var HomeButtonViewModel = function(scene, duration) { //>>includeStart('debug', pragmas.debug); if (!defined(scene)) { throw new DeveloperError('scene is required.'); } //>>includeEnd('debug'); - ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); duration = defaultValue(duration, 1500); this._scene = scene; - this._ellipsoid = ellipsoid; this._duration = duration; var that = this; this._command = createCommand(function() { - viewHome(that._scene, that._ellipsoid, that._duration); + viewHome(that._scene, that._duration); }); /** @@ -140,18 +135,6 @@ define([ } }, - /** - * Gets the ellipsoid to be viewed when in home position. - * @memberof HomeButtonViewModel.prototype - * - * @type {Ellipsoid} - */ - ellipsoid : { - get : function() { - return this._ellipsoid; - } - }, - /** * Gets the Command that is executed when the button is clicked. * @memberof HomeButtonViewModel.prototype From b7271ca5a8bdbe0029053022444a1f86fefc0af4 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 7 May 2014 18:59:59 -0400 Subject: [PATCH 15/74] Fix Geocoder and HomeButton after last change. --- Source/Widgets/Geocoder/Geocoder.js | 1 - Source/Widgets/HomeButton/HomeButton.js | 5 ++--- Source/Widgets/Viewer/Viewer.js | 5 ++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/Widgets/Geocoder/Geocoder.js b/Source/Widgets/Geocoder/Geocoder.js index 48762c595379..7204f682c063 100644 --- a/Source/Widgets/Geocoder/Geocoder.js +++ b/Source/Widgets/Geocoder/Geocoder.js @@ -37,7 +37,6 @@ define([ * written to the console reminding you that you must create and supply a Bing Maps * key as soon as possible. Please do not deploy an application that uses * this widget without creating a separate key for your application. - * @param {Ellipsoid} [description.ellipsoid=Ellipsoid.WGS84] The Scene's primary ellipsoid. * @param {Number} [description.flightDuration=1500] The duration of the camera flight to an entered location, in milliseconds. */ var Geocoder = function(description) { diff --git a/Source/Widgets/HomeButton/HomeButton.js b/Source/Widgets/HomeButton/HomeButton.js index 4b767cf0839b..47d6eed16ddb 100644 --- a/Source/Widgets/HomeButton/HomeButton.js +++ b/Source/Widgets/HomeButton/HomeButton.js @@ -25,10 +25,9 @@ define([ * * @param {Element|String} container The DOM element or ID that will contain the widget. * @param {Scene} scene The Scene instance to use. - * @param {Ellipsoid} [ellipsoid] The Scene's primary ellipsoid. * @param {Number} [duration] The time, in milliseconds, it takes to complete the camera flight home. */ - var HomeButton = function(container, scene, ellipsoid, duration) { + var HomeButton = function(container, scene, duration) { //>>includeStart('debug', pragmas.debug); if (!defined(container)) { throw new DeveloperError('container is required.'); @@ -37,7 +36,7 @@ define([ container = getElement(container); - var viewModel = new HomeButtonViewModel(scene, ellipsoid, duration); + var viewModel = new HomeButtonViewModel(scene, duration); viewModel._svgPath = 'M14,4l-10,8.75h20l-4.25-3.7188v-4.6562h-2.812v2.1875l-2.938-2.5625zm-7.0938,9.906v10.094h14.094v-10.094h-14.094zm2.1876,2.313h3.3122v4.25h-3.3122v-4.25zm5.8442,1.281h3.406v6.438h-3.406v-6.438z'; diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 39014400301c..a1b43ea86ab5 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -262,15 +262,14 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to toolbar.appendChild(geocoderContainer); geocoder = new Geocoder({ container : geocoderContainer, - scene : cesiumWidget.scene, - ellipsoid : cesiumWidget.scene.globe.ellipsoid + scene : cesiumWidget.scene }); } //HomeButton var homeButton; if (!defined(options.homeButton) || options.homeButton !== false) { - homeButton = new HomeButton(toolbar, cesiumWidget.scene, cesiumWidget.scene.globe.ellipsoid); + homeButton = new HomeButton(toolbar, cesiumWidget.scene); if (defined(geocoder)) { eventHelper.add(homeButton.viewModel.command.afterExecute, function() { var viewModel = geocoder.viewModel; From cbf3369a895dbd85ba9688c8eec5e9874581892a Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 8 May 2014 14:04:11 -0400 Subject: [PATCH 16/74] Fix ScreenSpaceCameraController, Geocoder, and HomeButton specs. --- Source/Scene/ScreenSpaceCameraController.js | 16 ++++++++-------- Specs/Scene/ScreenSpaceCameraControllerSpec.js | 10 +++++----- Specs/Widgets/Geocoder/GeocoderSpec.js | 5 ----- Specs/Widgets/Geocoder/GeocoderViewModelSpec.js | 5 ----- Specs/Widgets/HomeButton/HomeButtonSpec.js | 7 +------ .../HomeButton/HomeButtonViewModelSpec.js | 8 -------- 6 files changed, 14 insertions(+), 37 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index cf7addbc146e..be82ab3c0ec9 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -664,7 +664,7 @@ define([ var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; - rotate3D(controller, movement, transform, Cartesian3.UNIT_Z); + rotate3D(controller, movement, frameState, transform, Cartesian3.UNIT_Z); controller.globe = oldGlobe; } @@ -730,19 +730,19 @@ define([ var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); if (!defined(mousePos)) { - pan3D(controller, movement, controller._ellipsoid); + pan3D(controller, movement, frameState, controller._ellipsoid); } else { var magnitude = Cartesian3.magnitude(mousePos); var radii = scratchRadii; radii.x = radii.y = radii.z = magnitude; var ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); - pan3D(controller, movement, ellipsoid); + pan3D(controller, movement, frameState, ellipsoid); } } else { - pan3D(controller, movement, controller._ellipsoid); + pan3D(controller, movement, frameState, controller._ellipsoid); } } else { - rotate3D(controller, movement); + rotate3D(controller, movement, frameState); } } @@ -751,7 +751,7 @@ define([ var rotate3DNegateScratch = new Cartesian3(); var rotate3DInverseMatrixScratch = new Matrix4(); - function rotate3D(controller, movement, transform, constrainedAxis, restrictedAngle) { + function rotate3D(controller, movement, frameState, transform, constrainedAxis, restrictedAngle) { var camera = controller._camera; var oldAxis = camera.constrainedAxis; if (defined(constrainedAxis)) { @@ -838,7 +838,7 @@ define([ var pan3DTemp2 = new Cartesian3(); var pan3DTemp3 = new Cartesian3(); - function pan3D(controller, movement, ellipsoid) { + function pan3D(controller, movement, frameState, ellipsoid) { var camera = controller._camera; var p0 = camera.pickEllipsoid(movement.startPosition, ellipsoid, pan3DP0); var p1 = camera.pickEllipsoid(movement.endPosition, ellipsoid, pan3DP1); @@ -991,7 +991,7 @@ define([ controller.globe = Ellipsoid.UNIT_SPHERE; var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); - rotate3D(controller, movement, transform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle); + rotate3D(controller, movement, frameState, transform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle); controller.globe = oldGlobe; } diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 0fe5f54d5146..4c8157f1405d 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -72,14 +72,14 @@ defineSuite([ }); it('get/set ellipsoid', function() { - expect(controller.ellipsoid).toEqual(Ellipsoid.WGS84); - controller.ellipsoid = Ellipsoid.UNIT_SPHERE; - expect(controller.ellipsoid).toEqual(Ellipsoid.UNIT_SPHERE); + expect(controller.globe).toEqual(Ellipsoid.WGS84); + controller.globe = Ellipsoid.UNIT_SPHERE; + expect(controller.globe).toEqual(Ellipsoid.UNIT_SPHERE); }); function updateController(frameState) { camera.update(frameState.mode, frameState.scene2D); - controller.update(frameState.mode); + controller.update(frameState); } function setUp2D() { @@ -548,7 +548,7 @@ defineSuite([ expect(Cartesian3.dot(camera.right, Cartesian3.UNIT_Z)).toBeLessThan(CesiumMath.EPSILON16); }); - it('rotates in Columus view locked mode', function() { + it('rotates in Columbus view locked mode', function() { var frameState = setUpCV(); controller.columbusViewMode = CameraColumbusViewMode.LOCKED; diff --git a/Specs/Widgets/Geocoder/GeocoderSpec.js b/Specs/Widgets/Geocoder/GeocoderSpec.js index 9850a39565a4..5d6edda2ad66 100644 --- a/Specs/Widgets/Geocoder/GeocoderSpec.js +++ b/Specs/Widgets/Geocoder/GeocoderSpec.js @@ -1,13 +1,11 @@ /*global defineSuite*/ defineSuite([ 'Widgets/Geocoder/Geocoder', - 'Core/Ellipsoid', 'Specs/EventHelper', 'Specs/createScene', 'Specs/destroyScene' ], function( Geocoder, - Ellipsoid, EventHelper, createScene, destroyScene) { @@ -24,7 +22,6 @@ defineSuite([ }); it('constructor sets expected properties', function() { - var ellipsoid = new Ellipsoid(); var flightDuration = 1234; var url = 'bing.invalid/'; var key = 'testKey'; @@ -32,7 +29,6 @@ defineSuite([ var geocoder = new Geocoder({ container : document.body, scene : scene, - ellipsoid : ellipsoid, flightDuration : flightDuration, url : url, key : key @@ -40,7 +36,6 @@ defineSuite([ var viewModel = geocoder.viewModel; expect(viewModel.scene).toBe(scene); - expect(viewModel.ellipsoid).toBe(ellipsoid); expect(viewModel.flightDuration).toBe(flightDuration); expect(viewModel.url).toBe(url); expect(viewModel.key).toBe(key); diff --git a/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js b/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js index 6d0d11871007..b8465a8b437b 100644 --- a/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js +++ b/Specs/Widgets/Geocoder/GeocoderViewModelSpec.js @@ -2,13 +2,11 @@ defineSuite([ 'Widgets/Geocoder/GeocoderViewModel', 'Core/Cartesian3', - 'Core/Ellipsoid', 'Specs/createScene', 'Specs/destroyScene' ], function( GeocoderViewModel, Cartesian3, - Ellipsoid, createScene, destroyScene) { "use strict"; @@ -24,21 +22,18 @@ defineSuite([ }); it('constructor sets expected properties', function() { - var ellipsoid = new Ellipsoid(); var flightDuration = 1234; var url = 'bing.invalid/'; var key = 'testKey'; var viewModel = new GeocoderViewModel({ scene : scene, - ellipsoid : ellipsoid, flightDuration : flightDuration, url : url, key : key }); expect(viewModel.scene).toBe(scene); - expect(viewModel.ellipsoid).toBe(ellipsoid); expect(viewModel.flightDuration).toBe(flightDuration); expect(viewModel.url).toBe(url); expect(viewModel.key).toBe(key); diff --git a/Specs/Widgets/HomeButton/HomeButtonSpec.js b/Specs/Widgets/HomeButton/HomeButtonSpec.js index fcdf68395a9c..0767fdc2171b 100644 --- a/Specs/Widgets/HomeButton/HomeButtonSpec.js +++ b/Specs/Widgets/HomeButton/HomeButtonSpec.js @@ -1,12 +1,10 @@ /*global defineSuite*/ defineSuite([ 'Widgets/HomeButton/HomeButton', - 'Core/Ellipsoid', 'Specs/createScene', 'Specs/destroyScene' ], function( HomeButton, - Ellipsoid, createScene, destroyScene) { "use strict"; @@ -25,18 +23,15 @@ defineSuite([ var homeButton = new HomeButton(document.body, scene); expect(homeButton.container).toBe(document.body); expect(homeButton.viewModel.scene).toBe(scene); - expect(homeButton.viewModel.ellipsoid).toBe(Ellipsoid.WGS84); expect(homeButton.isDestroyed()).toEqual(false); homeButton.destroy(); expect(homeButton.isDestroyed()).toEqual(true); }); it('constructor sets expected values', function() { - var ellipsoid = new Ellipsoid(); - var homeButton = new HomeButton(document.body, scene, ellipsoid); + var homeButton = new HomeButton(document.body, scene); expect(homeButton.container).toBe(document.body); expect(homeButton.viewModel.scene).toBe(scene); - expect(homeButton.viewModel.ellipsoid).toBe(ellipsoid); homeButton.destroy(); }); diff --git a/Specs/Widgets/HomeButton/HomeButtonViewModelSpec.js b/Specs/Widgets/HomeButton/HomeButtonViewModelSpec.js index a69fb5cdbb6f..1ace1e817a48 100644 --- a/Specs/Widgets/HomeButton/HomeButtonViewModelSpec.js +++ b/Specs/Widgets/HomeButton/HomeButtonViewModelSpec.js @@ -29,14 +29,6 @@ defineSuite([ it('constructor sets default values', function() { var viewModel = new HomeButtonViewModel(scene); expect(viewModel.scene).toBe(scene); - expect(viewModel.ellipsoid).toBe(Ellipsoid.WGS84); - }); - - it('constructor sets expected values', function() { - var ellipsoid = new Ellipsoid(); - var viewModel = new HomeButtonViewModel(scene, ellipsoid); - expect(viewModel.scene).toBe(scene); - expect(viewModel.ellipsoid).toBe(ellipsoid); }); it('throws if scene is undefined', function() { From 4ef69e5fa9c54d320871ce74687a23026eca04f5 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 8 May 2014 15:38:59 -0400 Subject: [PATCH 17/74] Fix tile tests and state machine. --- Source/Scene/Tile.js | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 7a64b60cf103..2f7907590382 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -1,7 +1,6 @@ /*global define*/ define([ '../Core/BoundingSphere', - '../Core/Cartesian2', '../Core/Cartesian3', '../Core/Cartesian4', '../Core/Cartographic', @@ -9,11 +8,8 @@ define([ '../Core/defined', '../Core/defineProperties', '../Core/DeveloperError', - '../Core/GeographicProjection', '../Core/IntersectionTests', - '../Core/Math', '../Core/Rectangle', - '../Core/WebMercatorProjection', './ImageryState', './SceneMode', './TerrainState', @@ -26,7 +22,6 @@ define([ '../Renderer/TextureWrap' ], function( BoundingSphere, - Cartesian2, Cartesian3, Cartesian4, Cartographic, @@ -34,11 +29,8 @@ define([ defined, defineProperties, DeveloperError, - GeographicProjection, IntersectionTests, - CesiumMath, Rectangle, - WebMercatorProjection, ImageryState, SceneMode, TerrainState, @@ -238,6 +230,7 @@ define([ this.upsampledTerrain = undefined; this.pickBoundingSphere = new BoundingSphere(); + this.pickTerrain = undefined; }; defineProperties(Tile.prototype, { @@ -305,7 +298,7 @@ define([ var scratchV2 = new Cartesian3(); Tile.prototype.pick = function(ray, frameState, result) { - var terrain = defaultValue(this.loadedTerrain, this.upsampledTerrain); + var terrain = this.pickTerrain; if (!defined(terrain)) { return undefined; } @@ -360,6 +353,11 @@ define([ this.upsampledTerrain = undefined; } + if (defined(this.pickTerrain)) { + this.pickTerrain.freeResources(); + this.pickTerrain = undefined; + } + var i, len; var imageryList = this.imagery; @@ -424,8 +422,7 @@ define([ var isRenderable = defined(this.vertexArray); // But it's not done loading until our two state machines are terminated. - var isDoneLoading = defined(this.loadedTerrain) && this.loadedTerrain.state === TerrainState.READY; - isDoneLoading = isDoneLoading || (defined(this.upsampledTerrain) && this.upsampledTerrain.state === TerrainState.READY); + var isDoneLoading = !defined(this.loadedTerrain) && !defined(this.upsampledTerrain); // If this tile's terrain and imagery are just upsampled from its parent, mark the tile as // upsampled only. We won't refine a tile if its four children are upsampled only. @@ -545,7 +542,7 @@ define([ var upsampled = tile.upsampledTerrain; var suspendUpsampling = false; - if (defined(loaded) && loaded.state !== TerrainState.READY) { + if (defined(loaded)) { loaded.processLoadStateMachine(context, terrainProvider, tile.x, tile.y, tile.level); // Publish the terrain data on the tile as soon as it is available. @@ -578,6 +575,11 @@ define([ if (loaded.state === TerrainState.READY) { loaded.publishToTile(tile); + + // No further loading or upsampling is necessary. + tile.pickTerrain = defaultValue(tile.loadedTerrain, tile.upsampledTerrain); + tile.loadedTerrain = undefined; + tile.upsampledTerrain = undefined; } else if (loaded.state === TerrainState.FAILED) { // Loading failed for some reason, or data is simply not available, // so no need to continue trying to load. Any retrying will happen before we @@ -586,7 +588,7 @@ define([ } } - if (!suspendUpsampling && defined(upsampled) && upsampled.state !== TerrainState.READY) { + if (!suspendUpsampling && defined(upsampled)) { upsampled.processUpsampleStateMachine(context, terrainProvider, tile.x, tile.y, tile.level); // Publish the terrain data on the tile as soon as it is available. @@ -609,6 +611,10 @@ define([ if (upsampled.state === TerrainState.READY) { upsampled.publishToTile(tile); + + // No further upsampling is necessary. We need to continue loading, though. + tile.pickTerrain = tile.upsampledTerrain; + tile.upsampledTerrain = undefined; } else if (upsampled.state === TerrainState.FAILED) { // Upsampling failed for some reason. This is pretty much a catastrophic failure, // but maybe we'll be saved by loading. From b8a993e30b66e6681726d90186e32c593dd4642a Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 9 May 2014 12:22:07 -0400 Subject: [PATCH 18/74] Tilt around picked point. --- Source/Scene/CameraEventAggregator.js | 48 +++++++- Source/Scene/ScreenSpaceCameraController.js | 130 ++++++++++++++------ 2 files changed, 135 insertions(+), 43 deletions(-) diff --git a/Source/Scene/CameraEventAggregator.js b/Source/Scene/CameraEventAggregator.js index 9e8ae33e26c1..530c9c6568a3 100644 --- a/Source/Scene/CameraEventAggregator.js +++ b/Source/Scene/CameraEventAggregator.js @@ -42,11 +42,13 @@ define([ var update = aggregator._update; var isDown = aggregator._isDown; + var eventStartPosition = aggregator._eventStartPosition; var pressTime = aggregator._pressTime; var releaseTime = aggregator._releaseTime; update[key] = true; isDown[key] = false; + eventStartPosition[key] = new Cartesian2(); var movement = aggregator._movement[key]; if (!defined(movement)) { @@ -63,10 +65,11 @@ define([ }; movement.prevAngle = 0.0; - aggregator._eventHandler.setInputAction(function() { + aggregator._eventHandler.setInputAction(function(event) { aggregator._buttonsDown++; isDown[key] = true; pressTime[key] = new Date(); + Cartesian2.clone(event.position, eventStartPosition[key]); }, ScreenSpaceEventType.PINCH_START, modifier); aggregator._eventHandler.setInputAction(function() { @@ -134,10 +137,12 @@ define([ var key = getKey(type, modifier); var isDown = aggregator._isDown; + var eventStartPosition = aggregator._eventStartPosition; var pressTime = aggregator._pressTime; var releaseTime = aggregator._releaseTime; isDown[key] = false; + eventStartPosition[key] = new Cartesian2(); var lastMovement = aggregator._lastMovement[key]; if (!defined(lastMovement)) { @@ -161,11 +166,12 @@ define([ up = ScreenSpaceEventType.MIDDLE_UP; } - aggregator._eventHandler.setInputAction(function() { + aggregator._eventHandler.setInputAction(function(event) { aggregator._buttonsDown++; lastMovement.valid = false; isDown[key] = true; pressTime[key] = new Date(); + Cartesian2.clone(event.position, eventStartPosition[key]); }, down, modifier); aggregator._eventHandler.setInputAction(function() { @@ -230,6 +236,8 @@ define([ } } } + + Cartesian2.clone(mouseMovement.endPosition, aggregator._currentMousePosition); }, ScreenSpaceEventType.MOUSE_MOVE, modifier); } @@ -258,11 +266,14 @@ define([ this._movement = {}; this._lastMovement = {}; this._isDown = {}; + this._eventStartPosition = {}; this._pressTime = {}; this._releaseTime = {}; this._buttonsDown = 0; + this._currentMousePosition = new Cartesian2(); + listenToWheel(this, undefined); listenToPinch(this, undefined, canvas); listenMouseButtonDownUp(this, undefined, CameraEventType.LEFT_DRAG); @@ -285,6 +296,16 @@ define([ } }; + /** + * Gets the current mouse position. + * @memberof CameraEventAggregator + * + * @returns {Cartesian2} The current mouse position. + */ + CameraEventAggregator.prototype.getCurrentMousePosition = function() { + return this._currentMousePosition; + }; + /** * Gets if a mouse button down or touch has started and has been moved. * @memberof CameraEventAggregator @@ -367,6 +388,29 @@ define([ return this._isDown[key]; }; + /** + * Gets the mouse position that started the aggregation. + * @memberof CameraEventAggregator + * + * @param {CameraEventType} type The camera event type. + * @param {KeyboardEventModifier} [modifier] The keyboard modifier. + * @returns {Cartesian2} The mouse position. + */ + CameraEventAggregator.prototype.getStartMousePosition = function(type, modifier) { + //>>includeStart('debug', pragmas.debug); + if (!defined(type)) { + throw new DeveloperError('type is required.'); + } + //>>includeEnd('debug'); + + if (type === CameraEventType.WHEEL) { + return this._currentMousePosition; + } + + var key = getKey(type, modifier); + return this._eventStartPosition[key]; + }; + /** * Gets whether any mouse button is down, a touch has started, or the wheel has been moved. * @memberof CameraEventAggregator diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index be82ab3c0ec9..2226ec403f31 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -274,6 +274,9 @@ define([ this._horizontalRotationAxis = undefined; + this._tiltCenterMousePosition = new Cartesian2(); + this._tiltCenter = new Cartesian3(); + // Constants, Make any of these public? var radius = this._ellipsoid.maximumRadius; this._zoomFactor = 5.0; @@ -396,7 +399,8 @@ define([ } if (!aggregator.isButtonDown(type, modifier)) { - action(object, movementState, frameState); + var startPosition = aggregator.getStartMousePosition(type, modifier); + action(object, startPosition, movementState, frameState); } } else { movementState.active = false; @@ -424,10 +428,11 @@ define([ var modifier = eventType.modifier; var movement = aggregator.isMoving(type, modifier) && aggregator.getMovement(type, modifier); + var startPosition = aggregator.getStartMousePosition(type, modifier); if (controller.enableInputs && enabled) { if (movement) { - action(controller, movement, frameState); + action(controller, startPosition, movement, frameState); } else if (inertiaConstant < 1.0) { maintainInertia(aggregator, frameState, type, modifier, inertiaConstant, action, controller, inertiaStateName); } @@ -435,7 +440,7 @@ define([ } } - function handleZoom(object, movement, zoomFactor, distanceMeasure, unitPositionDotDirection) { + function handleZoom(object, startPosition, movement, zoomFactor, distanceMeasure, unitPositionDotDirection) { var percentage = 1.0; if (defined(unitPositionDotDirection)) { percentage = CesiumMath.clamp(Math.abs(unitPositionDotDirection), 0.25, 1.0); @@ -477,7 +482,7 @@ define([ var scratchTranslateP0 = new Cartesian3(); var scratchTranslateP1 = new Cartesian3(); - function translate2D(controller, movement) { + function translate2D(controller, startPosition, movement, frameState) { var camera = controller._camera; var start = camera.getPickRay(movement.startPosition, translate2DStart).origin; var end = camera.getPickRay(movement.endPosition, translate2DEnd).origin; @@ -494,19 +499,19 @@ define([ } } - function zoom2D(controller, movement) { + function zoom2D(controller, startPosition, movement, frameState) { if (defined(movement.distance)) { movement = movement.distance; } - handleZoom(controller, movement, controller._zoomFactor, controller._camera.getMagnitude()); + handleZoom(controller, startPosition, movement, controller._zoomFactor, controller._camera.getMagnitude()); } var twist2DStart = new Cartesian2(); var twist2DEnd = new Cartesian2(); - function twist2D(controller, movement) { + function twist2D(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { - singleAxisTwist2D(controller, movement.angleAndHeight); + singleAxisTwist2D(controller, startPosition, movement.angleAndHeight, frameState); return; } @@ -536,7 +541,7 @@ define([ controller._camera.twistRight(theta); } - function singleAxisTwist2D(controller, movement) { + function singleAxisTwist2D(controller, startPosition, movement, frameState) { var rotateRate = controller._rotateFactor * controller._rotateRateRangeAdjustment; if (rotateRate > controller._maximumRotateRate) { @@ -585,7 +590,7 @@ define([ var translateCVOrigin = new Cartesian3(); var translateCVPlane = new Plane(Cartesian3.ZERO, 0.0); - function translateCV(controller, movement, frameState) { + function translateCV(controller, startPosition, movement, frameState) { var camera = controller._camera; var startRay = camera.getPickRay(movement.startPosition, translateCVStartRay); var endRay = camera.getPickRay(movement.endPosition, translateCVEndRay); @@ -634,7 +639,7 @@ define([ var rotateCVWindowRay = new Ray(); var rotateCVCenter = new Cartesian3(); var rotateTransform = new Matrix4(); - function rotateCV(controller, movement, frameState) { + function rotateCV(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { movement = movement.angleAndHeight; } @@ -664,14 +669,14 @@ define([ var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; - rotate3D(controller, movement, frameState, transform, Cartesian3.UNIT_Z); + rotate3D(controller, startPosition, movement, frameState, transform, Cartesian3.UNIT_Z); controller.globe = oldGlobe; } var zoomCVWindowPos = new Cartesian2(); var zoomCVWindowRay = new Ray(); - function zoomCV(controller, movement) { + function zoomCV(controller, startPosition, movement, frameState) { if (defined(movement.distance)) { movement = movement.distance; } @@ -686,7 +691,7 @@ define([ var direction = ray.direction; var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); - handleZoom(controller, movement, controller._zoomFactor, scalar); + handleZoom(controller, startPosition, movement, controller._zoomFactor, scalar); } function updateCV(controller, frameState) { @@ -723,26 +728,26 @@ define([ var scratchRadii = new Cartesian3(); var scratchEllipsoid = new Ellipsoid(); - function spin3D(controller, movement, frameState) { + function spin3D(controller, startPosition, movement, frameState) { if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; if (defined(controller._globe) && height < controller.minimumTerrainHeight) { var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); if (!defined(mousePos)) { - pan3D(controller, movement, frameState, controller._ellipsoid); + pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); } else { var magnitude = Cartesian3.magnitude(mousePos); var radii = scratchRadii; radii.x = radii.y = radii.z = magnitude; var ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); - pan3D(controller, movement, frameState, ellipsoid); + pan3D(controller, startPosition, movement, frameState, ellipsoid); } } else { - pan3D(controller, movement, frameState, controller._ellipsoid); + pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); } } else { - rotate3D(controller, movement, frameState); + rotate3D(controller, startPosition, movement, frameState); } } @@ -751,7 +756,10 @@ define([ var rotate3DNegateScratch = new Cartesian3(); var rotate3DInverseMatrixScratch = new Matrix4(); - function rotate3D(controller, movement, frameState, transform, constrainedAxis, restrictedAngle) { + function rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, rotateOnlyVertical, rotateOnlyHorizontal) { + rotateOnlyVertical = defaultValue(rotateOnlyVertical, false); + rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, true); + var camera = controller._camera; var oldAxis = camera.constrainedAxis; if (defined(constrainedAxis)) { @@ -812,8 +820,14 @@ define([ } } - camera.rotateRight(deltaPhi, transform); - camera.rotateUp(deltaTheta, transform); + + if (!rotateOnlyVertical) { + camera.rotateRight(deltaPhi, transform); + } + + if (!rotateOnlyHorizontal) { + camera.rotateUp(deltaTheta, transform); + } if (defined(restrictedAngle)) { var direction = Cartesian3.clone(camera.directionWC, rotate3DRestrictedDirection); @@ -838,7 +852,7 @@ define([ var pan3DTemp2 = new Cartesian3(); var pan3DTemp3 = new Cartesian3(); - function pan3D(controller, movement, frameState, ellipsoid) { + function pan3D(controller, startPosition, movement, frameState, ellipsoid) { var camera = controller._camera; var p0 = camera.pickEllipsoid(movement.startPosition, ellipsoid, pan3DP0); var p1 = camera.pickEllipsoid(movement.endPosition, ellipsoid, pan3DP1); @@ -923,7 +937,7 @@ define([ } var zoom3DUnitPosition = new Cartesian3(); - function zoom3D(controller, movement) { + function zoom3D(controller, startPosition, movement, frameState) { if (defined(movement.distance)) { movement = movement.distance; } @@ -934,7 +948,7 @@ define([ var height = ellipsoid.cartesianToCartographic(camera.position).height; var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition); - handleZoom(controller, movement, controller._zoomFactor, height, Cartesian3.dot(unitPosition, camera.direction)); + handleZoom(controller, startPosition, movement, controller._zoomFactor, height, Cartesian3.dot(unitPosition, camera.direction)); } var tilt3DWindowPos = new Cartesian2(); @@ -943,7 +957,7 @@ define([ var tilt3DCenter = Cartesian4.clone(Cartesian4.UNIT_W); var tilt3DTransform = new Matrix4(); - function tilt3D(controller, movement, frameState) { + function tilt3D(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { movement = movement.angleAndHeight; } @@ -958,40 +972,74 @@ define([ return; } - var windowPosition = tilt3DWindowPos; - windowPosition.x = controller._canvas.clientWidth / 2; - windowPosition.y = controller._canvas.clientHeight / 2; - var ray = camera.getPickRay(windowPosition, tilt3DRay); + var ray; + var intersection; + var grazingAltitudeLocation; + var grazingAltitudeCart; var center; - var intersection; - if (defined(controller._globe) && height < controller.minimumTerrainHeight) { - center = controller._globe.pick(ray, frameState, tilt3DCenter); + if (Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) { + center = Cartesian3.clone(controller._tiltCenter, tilt3DCenter); + } else { + ray = camera.getPickRay(startPosition, tilt3DRay); + if (defined(controller._globe) && height < controller.minimumTerrainHeight) { + center = controller._globe.pick(ray, frameState, tilt3DCenter); + } + + if (!defined(center)) { + intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); + if (defined(intersection)) { + center = Ray.getPoint(ray, intersection.start, tilt3DCenter); + } else { + grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, ellipsoid); + if (!defined(grazingAltitudeLocation)) { + return; + } + grazingAltitudeCart = ellipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); + grazingAltitudeCart.height = 0.0; + center = ellipsoid.cartographicToCartesian(grazingAltitudeCart, tilt3DCenter); + } + } + + Cartesian2.clone(startPosition, controller._tiltCenterMousePosition); + Cartesian3.clone(center, controller._tiltCenter); } - if (!defined(center)) { - intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); + var verticalCenter; + + var windowPosition = tilt3DWindowPos; + windowPosition.x = controller._canvas.clientWidth / 2; + windowPosition.y = controller._tiltCenterMousePosition.y; + ray = camera.getPickRay(windowPosition, tilt3DRay); + + var mag = Cartesian3.magnitude(center); + var newEllipsoid = Ellipsoid.fromCartesian3(new Cartesian3(mag, mag, mag)); + + if (!defined(verticalCenter)) { + intersection = IntersectionTests.rayEllipsoid(ray, newEllipsoid); if (defined(intersection)) { - center = Ray.getPoint(ray, intersection.start, tilt3DCenter); + verticalCenter = Ray.getPoint(ray, intersection.start); } else { - var grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, ellipsoid); + grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, newEllipsoid); if (!defined(grazingAltitudeLocation)) { return; } - var grazingAltitudeCart = ellipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); + grazingAltitudeCart = newEllipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); grazingAltitudeCart.height = 0.0; - center = ellipsoid.cartographicToCartesian(grazingAltitudeCart, tilt3DCenter); + verticalCenter = newEllipsoid.cartographicToCartesian(grazingAltitudeCart); } } var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); + var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid); var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); - rotate3D(controller, movement, frameState, transform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle); + rotate3D(controller, startPosition, movement, frameState, transform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle, false, true); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle, true, false); controller.globe = oldGlobe; } @@ -1000,7 +1048,7 @@ define([ var look3DEndPos = new Cartesian2(); var look3DStartRay = new Ray(); var look3DEndRay = new Ray(); - function look3D(controller, movement) { + function look3D(controller, startPosition, movement, frameState) { var camera = controller._camera; var startPos = look3DStartPos; From 7ac5b6d03dad96707ca8fcfab92cbec38b51fd7f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 9 May 2014 16:15:17 -0400 Subject: [PATCH 19/74] Remove tilt from grazing altitude. Fixes tilting for horizon views and the mouse starts from space. --- Source/Scene/ScreenSpaceCameraController.js | 26 +++++---------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 2226ec403f31..63239c9e11be 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -989,17 +989,10 @@ define([ if (!defined(center)) { intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); - if (defined(intersection)) { - center = Ray.getPoint(ray, intersection.start, tilt3DCenter); - } else { - grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, ellipsoid); - if (!defined(grazingAltitudeLocation)) { - return; - } - grazingAltitudeCart = ellipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); - grazingAltitudeCart.height = 0.0; - center = ellipsoid.cartographicToCartesian(grazingAltitudeCart, tilt3DCenter); + if (!defined(intersection)) { + return; } + center = Ray.getPoint(ray, intersection.start, tilt3DCenter); } Cartesian2.clone(startPosition, controller._tiltCenterMousePosition); @@ -1018,17 +1011,10 @@ define([ if (!defined(verticalCenter)) { intersection = IntersectionTests.rayEllipsoid(ray, newEllipsoid); - if (defined(intersection)) { - verticalCenter = Ray.getPoint(ray, intersection.start); - } else { - grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, newEllipsoid); - if (!defined(grazingAltitudeLocation)) { - return; - } - grazingAltitudeCart = newEllipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); - grazingAltitudeCart.height = 0.0; - verticalCenter = newEllipsoid.cartographicToCartesian(grazingAltitudeCart); + if (!defined(intersection)) { + return; } + verticalCenter = Ray.getPoint(ray, intersection.start); } var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); From 4bd2f5497a278a329ed13a453220f7db65eef0f0 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 12 May 2014 14:21:18 -0400 Subject: [PATCH 20/74] Better mouse handling around the constrained axis. --- Source/Scene/Camera.js | 4 +-- Source/Scene/ScreenSpaceCameraController.js | 38 +-------------------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 9c42babc1c58..c47df76458b3 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -989,13 +989,13 @@ define([ var dot = Cartesian3.dot(p, constrainedAxis); var angleToAxis = Math.acos(dot); if (angle > 0 && angle > angleToAxis) { - angle = angleToAxis; + angle = angleToAxis - CesiumMath.EPSILON4; } dot = Cartesian3.dot(p, Cartesian3.negate(constrainedAxis, rotateVertScratchNegate)); angleToAxis = Math.acos(dot); if (angle < 0 && -angle > angleToAxis) { - angle = -angleToAxis; + angle = -angleToAxis + CesiumMath.EPSILON4; } var tangent = Cartesian3.cross(constrainedAxis, p, rotateVertScratchTan); diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 63239c9e11be..7adace66ed01 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -758,7 +758,7 @@ define([ function rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, rotateOnlyVertical, rotateOnlyHorizontal) { rotateOnlyVertical = defaultValue(rotateOnlyVertical, false); - rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, true); + rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, false); var camera = controller._camera; var oldAxis = camera.constrainedAxis; @@ -785,42 +785,6 @@ define([ var deltaPhi = rotateRate * phiWindowRatio * Math.PI * 2.0; var deltaTheta = rotateRate * thetaWindowRatio * Math.PI; - if (defined(camera.constrainedAxis) && !defined(transform)) { - var positionNormal = Cartesian3.normalize(camera.position, rotate3DScratchCartesian3); - var northParallel = Cartesian3.equalsEpsilon(positionNormal, camera.constrainedAxis, CesiumMath.EPSILON2); - var southParallel = Cartesian3.equalsEpsilon(positionNormal, Cartesian3.negate(camera.constrainedAxis, rotate3DNegateScratch), CesiumMath.EPSILON2); - - if (!northParallel && !southParallel) { - var up; - if (Cartesian3.dot(camera.position, camera.direction) + 1 < CesiumMath.EPSILON4) { - up = camera.up; - } else { - up = camera.direction; - } - - var east; - if (Cartesian3.equalsEpsilon(camera.constrainedAxis, positionNormal, CesiumMath.EPSILON2)) { - east = camera.right; - } else { - east = Cartesian3.cross(camera.constrainedAxis, positionNormal, rotate3DScratchCartesian3); - Cartesian3.normalize(east, east); - } - - var rDotE = Cartesian3.dot(camera.right, east); - var signRDotE = (CesiumMath.sign(rDotE) < 0.0) ? -1.0 : 1.0; - rDotE = Math.abs(rDotE); - var uDotA = Cartesian3.dot(up, camera.constrainedAxis); - var uDotE = Cartesian3.dot(up, east); - var signInnerSum = ((uDotA > 0.0 && uDotE > 0.0) || (uDotA < 0.0 && uDotE < 0.0)) ? -1.0 : 1.0; - uDotA = Math.abs(uDotA); - - var originalDeltaTheta = deltaTheta; - deltaTheta = signRDotE * (deltaTheta * uDotA - signInnerSum * deltaPhi * (1.0 - rDotE)); - deltaPhi = signRDotE * (deltaPhi * rDotE + signInnerSum * originalDeltaTheta * (1.0 - uDotA)); - } - } - - if (!rotateOnlyVertical) { camera.rotateRight(deltaPhi, transform); } From 3d076979e200e09a270e300b125f24ed41049392 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 12 May 2014 16:55:18 -0400 Subject: [PATCH 21/74] Fix rolling over constrained axis when tilting. --- Source/Scene/ScreenSpaceCameraController.js | 34 ++++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 7adace66ed01..3fe8ca539856 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -973,13 +973,11 @@ define([ var mag = Cartesian3.magnitude(center); var newEllipsoid = Ellipsoid.fromCartesian3(new Cartesian3(mag, mag, mag)); - if (!defined(verticalCenter)) { - intersection = IntersectionTests.rayEllipsoid(ray, newEllipsoid); - if (!defined(intersection)) { - return; - } - verticalCenter = Ray.getPoint(ray, intersection.start); + intersection = IntersectionTests.rayEllipsoid(ray, newEllipsoid); + if (!defined(intersection)) { + return; } + verticalCenter = Ray.getPoint(ray, intersection.start); var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid); @@ -988,8 +986,28 @@ define([ controller.globe = Ellipsoid.UNIT_SPHERE; var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); - rotate3D(controller, startPosition, movement, frameState, transform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle, false, true); - rotate3D(controller, startPosition, movement, frameState, verticalTransform, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_TWO - angle, true, false); + var constrainedAxis = Cartesian3.UNIT_Z; + var restrictedAngle = CesiumMath.PI_OVER_TWO - angle; + rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true); + + var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2), Cartesian3.normalize(camera.position)); + if (Cartesian3.dot(camera.right, tangent) < 0.0) { + + if (movement.startPosition.y > movement.endPosition.y) { + constrainedAxis = undefined; + restrictedAngle = undefined; + } + + var oldConstrainedAxis = camera.constrainedAxis; + camera.constrainedAxis = undefined; + + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); + + camera.constrainedAxis = oldConstrainedAxis; + } else { + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); + } + controller.globe = oldGlobe; } From 086214cc430292db73ad5a8e517a36832a8dc663 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 12 May 2014 17:25:59 -0400 Subject: [PATCH 22/74] Clean up allocations. --- Source/Scene/ScreenSpaceCameraController.js | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 3fe8ca539856..9f0903a562ac 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -917,9 +917,12 @@ define([ var tilt3DWindowPos = new Cartesian2(); var tilt3DRay = new Ray(); - var tilt3DCart = new Cartographic(); - var tilt3DCenter = Cartesian4.clone(Cartesian4.UNIT_W); + var tilt3DCenter = new Cartesian3(); + var tilt3DVerticalCenter = new Cartesian3(); var tilt3DTransform = new Matrix4(); + var tilt3DVerticalTransform = new Matrix4(); + var tilt3DNormal = new Cartesian3(); + var tilt3DCartesian3 = new Cartesian3(); function tilt3D(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { @@ -936,18 +939,15 @@ define([ return; } + var center; var ray; var intersection; - var grazingAltitudeLocation; - var grazingAltitudeCart; - - var center; if (Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) { center = Cartesian3.clone(controller._tiltCenter, tilt3DCenter); } else { ray = camera.getPickRay(startPosition, tilt3DRay); - if (defined(controller._globe) && height < controller.minimumTerrainHeight) { + if (defined(controller._globe)) { center = controller._globe.pick(ray, frameState, tilt3DCenter); } @@ -971,16 +971,17 @@ define([ ray = camera.getPickRay(windowPosition, tilt3DRay); var mag = Cartesian3.magnitude(center); - var newEllipsoid = Ellipsoid.fromCartesian3(new Cartesian3(mag, mag, mag)); + var radii = Cartesian3.fromElements(mag, mag, mag, scratchRadii); + var newEllipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); intersection = IntersectionTests.rayEllipsoid(ray, newEllipsoid); if (!defined(intersection)) { return; } - verticalCenter = Ray.getPoint(ray, intersection.start); + verticalCenter = Ray.getPoint(ray, intersection.start, tilt3DVerticalCenter); var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); - var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid); + var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid, tilt3DVerticalTransform); var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; @@ -990,7 +991,7 @@ define([ var restrictedAngle = CesiumMath.PI_OVER_TWO - angle; rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true); - var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2), Cartesian3.normalize(camera.position)); + var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); if (Cartesian3.dot(camera.right, tangent) < 0.0) { if (movement.startPosition.y > movement.endPosition.y) { @@ -1008,7 +1009,6 @@ define([ rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); } - controller.globe = oldGlobe; } From 4d3a8e32a36a6c11d41def3753baa7a61c9a4dda Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 12 May 2014 19:30:06 -0400 Subject: [PATCH 23/74] Add tilt around mouse position to Columbus view. --- Source/Scene/ScreenSpaceCameraController.js | 73 ++++++++++++++++++--- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 9f0903a562ac..94762c5140db 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -638,24 +638,47 @@ define([ var rotateCVWindowPos = new Cartesian2(); var rotateCVWindowRay = new Ray(); var rotateCVCenter = new Cartesian3(); - var rotateTransform = new Matrix4(); + var rotateCVVerticalCenter = new Cartesian3(); + var rotateCVTransform = new Matrix4(); + var rotateCVVerticalTransform = new Matrix4(); + var rotateCVOrigin = new Cartesian3(); + var rotateCVPlane = new Plane(Cartesian3.ZERO, 0.0); + var rotateCVCartesian3 = new Cartesian3(); + function rotateCV(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { movement = movement.angleAndHeight; } - var windowPosition = rotateCVWindowPos; - windowPosition.x = controller._canvas.clientWidth / 2; - windowPosition.y = controller._canvas.clientHeight / 2; - var ray = controller._camera.getPickRay(windowPosition, rotateCVWindowRay); - var normal = Cartesian3.UNIT_X; + var ellipsoid = controller._ellipsoid; + var camera = controller._camera; var center; + var ray; + var intersection; - if (defined(controller._globe) && controller._camera.position.z < controller.minimumTerrainHeight) { - center = controller._globe.pick(ray, frameState, rotateCVCenter); + if (Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) { + center = Cartesian3.clone(controller._tiltCenter, rotateCVCenter); + } else { + ray = camera.getPickRay(startPosition, rotateCVWindowRay); + if (defined(controller._globe)) { + center = controller._globe.pick(ray, frameState, rotateCVCenter); + } + + if (!defined(center)) { + intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); + if (!defined(intersection)) { + return; + } + center = Ray.getPoint(ray, intersection.start, rotateCVCenter); + } + + Cartesian2.clone(startPosition, controller._tiltCenterMousePosition); + Cartesian3.clone(center, controller._tiltCenter); } + var normal = Cartesian3.UNIT_X; + if (!defined(center)) { var position = ray.origin; var direction = ray.direction; @@ -664,12 +687,42 @@ define([ Cartesian3.add(position, center, center); } - var transform = Matrix4.fromTranslation(center, rotateTransform); + var windowPosition = rotateCVWindowPos; + windowPosition.x = controller._canvas.clientWidth / 2; + windowPosition.y = controller._tiltCenterMousePosition.y; + ray = camera.getPickRay(windowPosition, rotateCVWindowRay); + + var origin = Cartesian3.clone(Cartesian3.ZERO, rotateCVOrigin); + origin.x = center.x; + + var plane = Plane.fromPointNormal(origin, normal, rotateCVPlane); + var verticalCenter = IntersectionTests.rayPlane(ray, plane, rotateCVVerticalCenter); + + var transform = Matrix4.fromTranslation(center, rotateCVTransform); + var verticalTransform = Matrix4.fromTranslation(verticalCenter, rotateCVVerticalTransform); var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; - rotate3D(controller, startPosition, movement, frameState, transform, Cartesian3.UNIT_Z); + var constrainedAxis = Cartesian3.UNIT_Z; + rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, undefined, false, true); + + var tangent = Cartesian3.cross(Cartesian3.UNIT_Z, Cartesian3.normalize(camera.position, rotateCVCartesian3), rotateCVCartesian3); + if (Cartesian3.dot(camera.right, tangent) < 0.0) { + + if (movement.startPosition.y > movement.endPosition.y) { + constrainedAxis = undefined; + } + + var oldConstrainedAxis = camera.constrainedAxis; + camera.constrainedAxis = undefined; + + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); + + camera.constrainedAxis = oldConstrainedAxis; + } else { + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); + } controller.globe = oldGlobe; } From ea85024f298b705b075b766d241458ce183ba60c Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 15 May 2014 20:21:50 -0400 Subject: [PATCH 24/74] Fix tests. --- Specs/Scene/CameraSpec.js | 8 ++-- .../Scene/ScreenSpaceCameraControllerSpec.js | 37 +++++-------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/Specs/Scene/CameraSpec.js b/Specs/Scene/CameraSpec.js index 2acbcf7a7f41..b1611006bb31 100644 --- a/Specs/Scene/CameraSpec.js +++ b/Specs/Scene/CameraSpec.js @@ -568,10 +568,10 @@ defineSuite([ it('rotate past constrained axis stops at constained axis', function() { camera.constrainedAxis = Cartesian3.UNIT_Y; camera.rotateUp(Math.PI); - expect(camera.up).toEqualEpsilon(Cartesian3.negate(dir), CesiumMath.EPSILON15); - expect(camera.direction).toEqualEpsilon(up, CesiumMath.EPSILON15); - expect(camera.right).toEqualEpsilon(right, CesiumMath.EPSILON15); - expect(camera.position).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Y), CesiumMath.EPSILON15); + expect(camera.up).toEqualEpsilon(Cartesian3.negate(dir), CesiumMath.EPSILON4); + expect(camera.direction).toEqualEpsilon(up, CesiumMath.EPSILON4); + expect(camera.right).toEqualEpsilon(right, CesiumMath.EPSILON4); + expect(camera.position).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Y), CesiumMath.EPSILON4); }); it('zooms out 2D', function() { diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 08f539c558db..4e3b8df64ea7 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -538,6 +538,8 @@ defineSuite([ var startPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2); var endPosition = new Cartesian2(3 * canvas.clientWidth / 8, 3 * canvas.clientHeight / 8); + camera.position.y = -100.0; + MockCanvas.moveMouse(canvas, MouseButtons.MIDDLE, startPosition, endPosition); updateController(frameState); expect(Cartesian3.dot(Cartesian3.normalize(camera.position), Cartesian3.UNIT_Z)).toBeGreaterThan(0.0); @@ -761,25 +763,6 @@ defineSuite([ expect(intersection).toBeDefined(); }); - it('tilts when the view direction does not intersect the ellipsoid', function() { - var frameState = setUp3D(); - var position = Cartesian3.clone(camera.position); - var startPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2); - var endPosition = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 4); - - camera.lookRight(CesiumMath.PI_OVER_TWO); - var ray = new Ray(camera.positionWC, camera.directionWC); - var intersection = IntersectionTests.rayEllipsoid(ray, frameState.scene2D.projection.ellipsoid); - expect(intersection).not.toBeDefined(); - - MockCanvas.moveMouse(canvas, MouseButtons.MIDDLE, startPosition, endPosition); - updateController(frameState); - expect(camera.position).not.toEqual(position); - expect(camera.direction).not.toEqualEpsilon(Cartesian3.normalize(Cartesian3.negate(camera.position)), CesiumMath.EPSILON14); - expect(Cartesian3.cross(camera.direction, camera.up)).toEqualEpsilon(camera.right, CesiumMath.EPSILON14); - expect(Cartesian3.cross(camera.right, camera.direction)).toEqualEpsilon(camera.up, CesiumMath.EPSILON14); - }); - it('does not tilt in the wrong direction', function() { var frameState = setUp3D(); var position = Cartesian3.clone(camera.position); @@ -808,7 +791,7 @@ defineSuite([ updateController(frameState); var height = Ellipsoid.WGS84.cartesianToCartographic(camera.position).height; - expect(height).toBeLessThan(controller.minimumZoomDistance); + expect(height).toBeLessThan(controller.minimumZoomDistance + 10.0); expect(Cartesian3.cross(camera.direction, camera.up)).toEqualEpsilon(camera.right, CesiumMath.EPSILON14); expect(Cartesian3.cross(camera.right, camera.direction)).toEqualEpsilon(camera.up, CesiumMath.EPSILON14); }); @@ -861,10 +844,10 @@ defineSuite([ MockCanvas.moveMouse(canvas, MouseButtons.LEFT, startPosition, endPosition); updateController(frameState); - expect(camera.position).toEqualEpsilon(Cartesian3.multiplyByScalar(axis, Cartesian3.magnitude(camera.position)), CesiumMath.EPSILON8); - expect(camera.direction).toEqualEpsilon(Cartesian3.negate(axis), CesiumMath.EPSILON14); + expect(Cartesian3.normalize(camera.position)).toEqualEpsilon(axis, CesiumMath.EPSILON4); + expect(camera.direction).toEqualEpsilon(Cartesian3.negate(axis), CesiumMath.EPSILON4); expect(Cartesian3.dot(camera.up, axis)).toBeLessThan(CesiumMath.EPSILON2); - expect(camera.right).toEqualEpsilon(Cartesian3.cross(camera.direction, camera.up), CesiumMath.EPSILON14); + expect(camera.right).toEqualEpsilon(Cartesian3.cross(camera.direction, camera.up), CesiumMath.EPSILON4); }); it('pans with constrained axis and is tilted', function() { @@ -905,10 +888,10 @@ defineSuite([ MockCanvas.moveMouse(canvas, MouseButtons.LEFT, startPosition, endPosition); updateController(frameState); - expect(Cartesian3.dot(camera.position, axis)).toBeLessThan(CesiumMath.EPSILON2); - expect(camera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.normalize(camera.position)), CesiumMath.EPSILON15); - expect(camera.right).toEqualEpsilon(axis, CesiumMath.EPSILON15); - expect(camera.up).toEqualEpsilon(Cartesian3.cross(camera.right, camera.direction), CesiumMath.EPSILON15); + expect(Cartesian3.normalize(camera.position)).toEqualEpsilon(Cartesian3.UNIT_Z, CesiumMath.EPSILON4); + expect(camera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Z), CesiumMath.EPSILON4); + expect(camera.right).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Y), CesiumMath.EPSILON4); + expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_X, CesiumMath.EPSILON4); }); it('controller does not modify the camera after re-enabling motion', function() { From 5833629678beff05c1c46f8b593de8f0f9fc9a1c Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 22 May 2014 20:09:49 -0400 Subject: [PATCH 25/74] Fix tilting in Columbus view after merge. --- Source/Scene/ScreenSpaceCameraController.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index cf4c757a38fa..e39242a0ca2c 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -696,8 +696,20 @@ define([ var plane = Plane.fromPointNormal(origin, normal, rotateCVPlane); var verticalCenter = IntersectionTests.rayPlane(ray, plane, rotateCVVerticalCenter); - var transform = Matrix4.fromTranslation(center, rotateCVTransform); - var verticalTransform = Matrix4.fromTranslation(verticalCenter, rotateCVVerticalTransform); + var projection = controller._camera._projection; + ellipsoid = projection.ellipsoid; + + Cartesian3.fromElements(center.y, center.z, center.x, center); + var cart = projection.unproject(center, rotateCVCart); + ellipsoid.cartographicToCartesian(cart, center); + + var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, rotateCVTransform); + + Cartesian3.fromElements(verticalCenter.y, verticalCenter.z, verticalCenter.x, verticalCenter); + cart = projection.unproject(verticalCenter, rotateCVCart); + ellipsoid.cartographicToCartesian(cart, verticalCenter); + + var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, ellipsoid, rotateCVVerticalTransform); var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; From dd4b158c04ddd9cb40b9061f682d77138419b6b3 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 28 May 2014 17:53:26 -0400 Subject: [PATCH 26/74] Add terrain picking that goes through the quad tree instead of the list of rendered tiles. --- Source/Scene/Globe.js | 10 ++- Source/Scene/GlobeSurface.js | 72 ++++++++++++++++++++- Source/Scene/ScreenSpaceCameraController.js | 8 +-- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 72a267bff9a4..901f752ef50d 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -294,8 +294,12 @@ define([ } }); + Globe.prototype.pick = function(ray, frameState, result) { + return this._surface.pick(ray, frameState, result); + }; + /** - * Find an intersection between a ray and the globe. + * Find an intersection between a ray and the globe surface that was rendered. * @memberof Globe * * @param {Ray} ray The ray to test for intersection. @@ -308,8 +312,8 @@ define([ * var ray = scene.camera.getPickRay(windowCoordinates); * var intersection = globe.pick(ray, scene.frameState); */ - Globe.prototype.pick = function(ray, frameState, result) { - return this._surface.pick(ray, frameState, result); + Globe.prototype.pickRenderedSurface = function(ray, frameState, result) { + return this._surface.pickRenderedSurface(ray, frameState, result); }; var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 3544e913734d..062fd4eb59c2 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -280,8 +280,76 @@ define([ stop : 0.0 }; + GlobeSurface.prototype.pick = function(ray, frameState, result) { + var stack = []; + var sphereIntersections = scratchSphereIntersections; + sphereIntersections.length = 0; + + var tile; + var i; + + var levelZeroTiles = this._levelZeroTiles; + var length = levelZeroTiles.length; + for (i = 0; i < length; ++i) { + stack.push(levelZeroTiles[i]); + } + + while (stack.length > 0) { + tile = stack.pop(); + if (tile.state < TileState.READY) { + while (!defined(tile.pickTerrain) && defined(parent)) { + tile = tile.parent; + } + + sphereIntersections.push(tile); + continue; + } + + var boundingVolume = tile.pickBoundingSphere; + if (frameState.mode !== SceneMode.SCENE3D) { + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.scene2D.projection, tile.minimumHeight, tile.maximumHeight, boundingVolume); + Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); + } else { + BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); + } + + var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); + if (defined(boundingSphereIntersection)) { + var children = tile.children; + var childrenLength = children.length; + for (i = 0; i < childrenLength; ++i) { + stack.push(children[i]); + } + } + } + + sphereIntersections.sort(createComparePickTileFunction(ray.origin)); + + var currentTile = sphereIntersections[0]; + var uniqueIntersections = [currentTile]; + length = sphereIntersections.length; + for (i = 1; i < length; ++i) { + tile = sphereIntersections[i]; + if (tile !== currentTile) { + uniqueIntersections.push(tile); + currentTile = tile; + } + } + + var intersection; + length = sphereIntersections.length; + for (i = 0; i < length; ++i) { + intersection = uniqueIntersections[i].pick(ray, frameState, result); + if (defined(intersection)) { + break; + } + } + + return intersection; + }; + /** - * Find an intersection between a ray and the globe surface. + * Find an intersection between a ray and the globe surface that was rendered. * @memberof GlobeSurface * * @param {Ray} ray The ray to test for intersection. @@ -294,7 +362,7 @@ define([ * var ray = scene.camera.getPickRay(windowCoordinates); * var intersection = surface.pick(ray, scene.frameState); */ - GlobeSurface.prototype.pick = function(ray, frameState, result) { + GlobeSurface.prototype.pickRenderedSurface = function(ray, frameState, result) { var sphereIntersections = scratchSphereIntersections; sphereIntersections.length = 0; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index e39242a0ca2c..c786b61bbdc5 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -598,7 +598,7 @@ define([ var startPlanePos; if (defined(controller._globe) && controller._camera.position.z < controller.minimumTerrainHeight) { - startPlanePos = controller._globe.pick(startRay, frameState, translateCVStartPos); + startPlanePos = controller._globe.pickRenderedSurface(startRay, frameState, translateCVStartPos); if (defined(startPlanePos)) { origin.x = startPlanePos.x; } @@ -660,7 +660,7 @@ define([ } else { ray = camera.getPickRay(startPosition, rotateCVWindowRay); if (defined(controller._globe)) { - center = controller._globe.pick(ray, frameState, rotateCVCenter); + center = controller._globe.pickRenderedSurface(ray, frameState, rotateCVCenter); } if (!defined(center)) { @@ -796,7 +796,7 @@ define([ var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; if (defined(controller._globe) && height < controller.minimumTerrainHeight) { var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); - var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); + var mousePos = controller._globe.pickRenderedSurface(startRay, frameState, scratchMousePos); if (!defined(mousePos)) { pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); } else { @@ -1011,7 +1011,7 @@ define([ } else { ray = camera.getPickRay(startPosition, tilt3DRay); if (defined(controller._globe)) { - center = controller._globe.pick(ray, frameState, tilt3DCenter); + center = controller._globe.pickRenderedSurface(ray, frameState, tilt3DCenter); } if (!defined(center)) { From 5f79912b8c040c64f6450518dcde65842dcb6419 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 30 May 2014 20:05:03 -0400 Subject: [PATCH 27/74] Initial camera collision detection/response with terrain. Only works with zooming and tilting in 3D. --- Source/Scene/GlobeSurface.js | 8 +- Source/Scene/ScreenSpaceCameraController.js | 86 +++++++++++++++++++-- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 062fd4eb59c2..f6f5af726cb2 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -323,11 +323,15 @@ define([ } } + length = sphereIntersections.length; + if (length === 0) { + return undefined; + } + sphereIntersections.sort(createComparePickTileFunction(ray.origin)); var currentTile = sphereIntersections[0]; var uniqueIntersections = [currentTile]; - length = sphereIntersections.length; for (i = 1; i < length; ++i) { tile = sphereIntersections[i]; if (tile !== currentTile) { @@ -337,7 +341,7 @@ define([ } var intersection; - length = sphereIntersections.length; + length = uniqueIntersections.length; for (i = 0; i < length; ++i) { intersection = uniqueIntersections[i].pick(ray, frameState, result); if (defined(intersection)) { diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index c786b61bbdc5..3380477a39a1 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -432,7 +432,71 @@ define([ } } - function handleZoom(object, startPosition, movement, zoomFactor, distanceMeasure, unitPositionDotDirection) { + var adjustForTerrainRay = new Ray(); + var adjustForTerrainCartesian3 = new Cartesian3(); + + function adjustForTerrain(controller, frameState, distance, direction) { + if (!defined(controller._globe)) { + return distance; + } + + var camera = controller._camera; + + var ray = adjustForTerrainRay; + Cartesian3.clone(camera.position, ray.origin); + if (distance < 0.0) { + Cartesian3.negate(direction, ray.direction); + } else { + Cartesian3.clone(direction, ray.direction); + } + + var intersection = controller._globe.pick(ray, frameState, adjustForTerrainCartesian3); + if (!defined(intersection)) { + return distance; + } + + var dist = Cartesian3.distance(ray.origin, intersection) - controller.minimumZoomDistance; + if (dist <= 0.0 || distance === 0.0) { + return 0.0; + } + + var ratio = CesiumMath.clamp(dist / Math.abs(distance), 0.0, 1.0); + return distance * ratio; + } + + function adjustRotateForTerrain(controller, frameState, radius, angle, direction, globeOverride) { + var globe = defaultValue(globeOverride, controller._globe); + if (!defined(globe)) { + return angle; + } + + var camera = controller._camera; + + var distance = 2.0 * radius * Math.sin(Math.abs(angle) * 0.5); + + var ray = adjustForTerrainRay; + Cartesian3.clone(camera.position, ray.origin); + if (angle < 0.0) { + Cartesian3.negate(direction, ray.direction); + } else { + Cartesian3.clone(direction, ray.direction); + } + + var intersection = globe.pick(ray, frameState, adjustForTerrainCartesian3); + if (!defined(intersection)) { + return angle; + } + + var dist = Cartesian3.distance(ray.origin, intersection) - controller.minimumZoomDistance; + if (dist <= 0.0 || distance === 0.0) { + return 0.0; + } + + var ratio = CesiumMath.clamp(dist / distance, 0.0, 1.0); + return angle * ratio; + } + + function handleZoom(object, startPosition, movement, frameState, zoomFactor, distanceMeasure, unitPositionDotDirection) { var percentage = 1.0; if (defined(unitPositionDotDirection)) { percentage = CesiumMath.clamp(Math.abs(unitPositionDotDirection), 0.25, 1.0); @@ -466,6 +530,7 @@ define([ distance = distanceMeasure - maxHeight; } + distance = adjustForTerrain(object, frameState, distance, object._camera.direction); object._camera.zoomIn(distance); } @@ -496,7 +561,7 @@ define([ movement = movement.distance; } - handleZoom(controller, startPosition, movement, controller._zoomFactor, controller._camera.getMagnitude()); + handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, controller._camera.getMagnitude()); } var twist2DStart = new Cartesian2(); @@ -754,7 +819,7 @@ define([ var direction = ray.direction; var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); - handleZoom(controller, startPosition, movement, controller._zoomFactor, scalar); + handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, scalar); } function updateCV(controller, frameState) { @@ -819,7 +884,7 @@ define([ var rotate3DNegateScratch = new Cartesian3(); var rotate3DInverseMatrixScratch = new Matrix4(); - function rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, rotateOnlyVertical, rotateOnlyHorizontal) { + function rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, rotateOnlyVertical, rotateOnlyHorizontal, globeOverride) { rotateOnlyVertical = defaultValue(rotateOnlyVertical, false); rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, false); @@ -848,11 +913,16 @@ define([ var deltaPhi = rotateRate * phiWindowRatio * Math.PI * 2.0; var deltaTheta = rotateRate * thetaWindowRatio * Math.PI; + var center = !defined(transform) ? Cartesian3.ZERO : Matrix4.getColumn(transform, 3); + var radius = Cartesian3.distance(camera.position, center); + if (!rotateOnlyVertical) { + deltaPhi = adjustRotateForTerrain(controller, frameState, radius, deltaPhi, camera.right, globeOverride); camera.rotateRight(deltaPhi, transform); } if (!rotateOnlyHorizontal) { + deltaTheta = adjustRotateForTerrain(controller, frameState, radius, deltaTheta, camera.up, globeOverride); camera.rotateUp(deltaTheta, transform); } @@ -975,7 +1045,7 @@ define([ var height = ellipsoid.cartesianToCartographic(camera.position).height; var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition); - handleZoom(controller, startPosition, movement, controller._zoomFactor, height, Cartesian3.dot(unitPosition, camera.direction)); + handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, height, Cartesian3.dot(unitPosition, camera.direction)); } var tilt3DWindowPos = new Cartesian2(); @@ -1052,7 +1122,7 @@ define([ var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); var constrainedAxis = Cartesian3.UNIT_Z; var restrictedAngle = CesiumMath.PI_OVER_TWO - angle; - rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true); + rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true, oldGlobe); var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); if (Cartesian3.dot(camera.right, tangent) < 0.0) { @@ -1065,11 +1135,11 @@ define([ var oldConstrainedAxis = camera.constrainedAxis; camera.constrainedAxis = undefined; - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, oldGlobe); camera.constrainedAxis = oldConstrainedAxis; } else { - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, oldGlobe); } controller.globe = oldGlobe; From 994fb4405ee5054d4fa08878091b569d9df9ba13 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 4 Jun 2014 16:11:55 -0400 Subject: [PATCH 28/74] Fix tests. --- Specs/Core/IntersectionTestsSpec.js | 8 ++++---- Specs/Scene/ScreenSpaceCameraControllerSpec.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Specs/Core/IntersectionTestsSpec.js b/Specs/Core/IntersectionTestsSpec.js index 8a25dde9f5d8..aafa2cc79d41 100644 --- a/Specs/Core/IntersectionTestsSpec.js +++ b/Specs/Core/IntersectionTestsSpec.js @@ -60,25 +60,25 @@ defineSuite([ it('rayTriangle throws without ray', function() { expect(function() { IntersectionTests.rayTriangle(); - }).toThrow(); + }).toThrowDeveloperError(); }); it('rayTriangle throws without p0', function() { expect(function() { IntersectionTests.rayTriangle(new Ray()); - }).toThrow(); + }).toThrowDeveloperError(); }); it('rayTriangle throws without p1', function() { expect(function() { IntersectionTests.rayTriangle(new Ray(), new Cartesian3()); - }).toThrow(); + }).toThrowDeveloperError(); }); it('rayTriangle throws without p2', function() { expect(function() { IntersectionTests.rayTriangle(new Ray(), new Cartesian3(), new Cartesian3()); - }).toThrow(); + }).toThrowDeveloperError(); }); it('rayTriangle intersects front face', function() { diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 67e617f56d8d..ca10524b0e93 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -547,7 +547,7 @@ defineSuite([ expect(Cartesian3.dot(Cartesian3.normalize(camera.position), Cartesian3.UNIT_Z)).toBeGreaterThan(0.0); expect(Cartesian3.dot(camera.direction, Cartesian3.UNIT_Z)).toBeLessThan(0.0); expect(Cartesian3.dot(camera.up, Cartesian3.UNIT_Z)).toBeGreaterThan(0.0); - expect(Cartesian3.dot(camera.right, Cartesian3.UNIT_Z)).toBeLessThan(CesiumMath.EPSILON7); + expect(Cartesian3.dot(camera.right, Cartesian3.UNIT_Z)).toBeLessThan(CesiumMath.EPSILON6); }); it('rotates in Columus view with camera transform set', function() { From 012a92fc2427cc2ef71543da507658212de64351 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 4 Jun 2014 18:08:00 -0400 Subject: [PATCH 29/74] Add sphere-plane and circle-circle intersection tests. --- Source/Core/IntersectionTests.js | 212 ++++++++++++++++++++++++++----- 1 file changed, 181 insertions(+), 31 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index d3e85096c0fb..aff2eed77b4b 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -1,5 +1,6 @@ /*global define*/ define([ + './Cartesian2', './Cartesian3', './Cartographic', './defaultValue', @@ -10,6 +11,7 @@ define([ './QuadraticRealPolynomial', './QuarticRealPolynomial' ], function( + Cartesian2, Cartesian3, Cartographic, defaultValue, @@ -162,6 +164,49 @@ define([ return Cartesian3.add(origin, result, result); }; + function solveQuadratic(a, b, c, result) { + var det = b * b - 4.0 * a * c; + if (det < 0.0) { + return undefined; + } else if (det > 0.0) { + var denom = 1.0 / (2.0 * a); + var disc = Math.sqrt(det); + var root0 = (-b + disc) * denom; + var root1 = (-b - disc) * denom; + + if (root0 <= 0.0 && root1 <= 0.0) { + return undefined; + } else if (root0 < 0.0) { + root0 = 0.0; + } else if (root1 < 0.0) { + root1 = 0.0; + } + + if (root0 < root1) { + result.root0 = root0; + result.root1 = root1; + } else { + result.root0 = root1; + result.root1 = root0; + } + + return result; + } + + var root = -b / (2.0 * a); + if (root === 0.0) { + return undefined; + } + + result.root0 = result.root1 = root; + return result; + } + + var raySphereRoots = { + root0 : 0.0, + root1 : 0.0 + }; + /** * Computes the intersection points of a ray with a sphere. * @memberof IntersectionTests @@ -197,40 +242,13 @@ define([ var b = 2.0 * Cartesian3.dot(direction, diff); var c = Cartesian3.magnitudeSquared(diff) - radiusSquared; - var det = b * b - 4.0 * a * c; - if (det < 0.0) { - return undefined; - } else if (det > 0.0) { - var denom = 1.0 / (2.0 * a); - var disc = Math.sqrt(det); - var root0 = (-b + disc) * denom; - var root1 = (-b - disc) * denom; - - if (root0 <= 0.0 && root1 <= 0.0) { - return undefined; - } else if (root0 < 0.0) { - root0 = 0.0; - } else if (root1 < 0.0) { - root1 = 0.0; - } - - if (root0 < root1) { - result.start = root0; - result.stop = root1; - } else { - result.start = root1; - result.stop = root0; - } - - return result; - } - - var root = -b / (2.0 * a); - if (root === 0.0) { + var roots = solveQuadratic(a, b, c, raySphereRoots); + if (!defined(roots)) { return undefined; } - result.start = result.stop = root; + result.start = roots.root0; + result.stop = roots.root1; return result; }; @@ -729,5 +747,137 @@ define([ return undefined; }; + IntersectionTests.spherePlane = function(sphere, plane, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + if (!defined(plane)) { + throw new DeveloperError('plane is required.'); + } + //>>includeEnd('debug'); + + var sphereCenter = sphere.center; + var sphereRadius = sphere.radius; + + var planeNormal = plane.normal; + var planeD = plane.distance; + + var p = Cartesian3.dot(planeNormal, sphereCenter) + planeD; + var planeNormalMagSqrd = Cartesian3.magnitudeSquared(planeNormal); + var d = Math.abs(p) / Math.sqrt(planeNormalMagSqrd); + + if (sphereRadius < d) { + return undefined; + } + + if (!defined(result)) { + result = {}; + } + + var center = result.center; + if (!defined(result.center)) { + center = result.center = new Cartesian3(); + } + + var c = p / planeNormalMagSqrd; + Cartesian3.multiplyByScalar(planeNormal, c, center); + Cartesian3.subtract(sphereCenter, center, center); + + result.radius = (sphereRadius > d) ? Math.sqrt(sphereRadius * sphereRadius - d * d) : 0.0; + + return result; + }; + + var scratchCircleCircleRoots = { + root0 : 0.0, + root1 : 0.0 + }; + + IntersectionTests.circleCircle = function(c0, c1, result) { + var p0 = c0.center; + var p1 = c1.center; + if (Cartesian2.equalsEpsilon(p0, p1, CesiumMath.EPSILON6)) { + return undefined; + } + + var r0 = c0.radius; + var r1 = c1.radius; + + if (Cartesian2.distance(p0, p1) > r0 + r1) { + return undefined; + } + + var x0 = p0.x; + var y0 = p0.y; + + var x1 = p1.x; + var y1 = p1.y; + + var r0Sqrd = r0 * r0; + var x0Sqrd = x0 * x0; + var y0Sqrd = y0 * y0; + + var q = r0Sqrd - r1 * r1 + y1 * y1 - y0Sqrd + x1 * x1 - x0Sqrd; + + if (!CesiumMath.equalsEpsilon(y1, y0, CesiumMath.EPSILON6)) { + var invYDiff = 1.0 / (y1 - y0); + q *= 0.5 * invYDiff; + var r = (x1 - x0) * invYDiff; + + var a = 1.0 + r * r; + var b = 2.0 * (r * y0 - q * r - x0); + var c = x0Sqrd + q * q - 2.0 * q * y0 + y0Sqrd - r0Sqrd; + + var roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); + if (!defined(roots)) { + return undefined; + } + + x0 = roots.root0; + y0 = q - roots.root0 * r; + + x1 = roots.root1; + y1 = q - roots.root1 * r; + } else { + q /= 2.0 * (x1 - x0); + + a = 1.0; + b = -2.0 * y0; + c = y0Sqrd - r0Sqrd + q * q - 2.0 * q * x0 + x0Sqrd; + + var roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); + if (!defined(roots)) { + return undefined; + } + + x0 = x1 = q; + y0 = roots.root0; + y1 = roots.root1; + } + + if (!defined(result)) { + result = []; + } + + var cartesian = result[0]; + if (!defined(cartesian)) { + cartesian = result[0] = new Cartesian2(); + } + + cartesian.x = x0; + cartesian.y = y0; + + cartesian = result[1]; + if (!defined(cartesian)) { + cartesian = result[1] = new Cartesian2(); + } + + cartesian.x = x1; + cartesian.y = y1; + + return result; + }; + return IntersectionTests; }); From 2a506e0e3eba1f39264db3be56ac80c847c46798 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 5 Jun 2014 17:28:18 -0400 Subject: [PATCH 30/74] Add plane-plane intersection and 2D line-circle intersection. --- Source/Core/IntersectionTests.js | 152 ++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 5 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index aff2eed77b4b..5b711f5edb52 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -820,16 +820,21 @@ define([ var q = r0Sqrd - r1 * r1 + y1 * y1 - y0Sqrd + x1 * x1 - x0Sqrd; + var a; + var b; + var c; + var roots; + if (!CesiumMath.equalsEpsilon(y1, y0, CesiumMath.EPSILON6)) { var invYDiff = 1.0 / (y1 - y0); q *= 0.5 * invYDiff; var r = (x1 - x0) * invYDiff; - var a = 1.0 + r * r; - var b = 2.0 * (r * y0 - q * r - x0); - var c = x0Sqrd + q * q - 2.0 * q * y0 + y0Sqrd - r0Sqrd; + a = 1.0 + r * r; + b = 2.0 * (r * y0 - q * r - x0); + c = x0Sqrd + q * q - 2.0 * q * y0 + y0Sqrd - r0Sqrd; - var roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); + roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); if (!defined(roots)) { return undefined; } @@ -846,7 +851,7 @@ define([ b = -2.0 * y0; c = y0Sqrd - r0Sqrd + q * q - 2.0 * q * x0 + x0Sqrd; - var roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); + roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); if (!defined(roots)) { return undefined; } @@ -879,5 +884,142 @@ define([ return result; }; + var scratchDirection = new Cartesian3(); + var xName = 'x'; + var yName = 'y'; + var zName = 'z'; + + IntersectionTests.planePlaneIntersection = function(p0, p1, result) { + var p0Normal = p0.normal; + var p1Normal = p1.normal; + + var direction = Cartesian3.cross(p0Normal, p1Normal, scratchDirection); + if (Cartesian3.equalsEpsilon(direction, Cartesian3.ZERO, CesiumMath.EPSILON6)) { + return undefined; + } + + if (!defined(result)) { + result = { + direction : new Cartesian3(), + point : new Cartesian3() + }; + } + + Cartesian3.normalize(direction, result.direction); + + var absX = Math.abs(p0Normal.x); + var absY = Math.abs(p0Normal.y); + var absZ = Math.abs(p0Normal.z); + + var a1; + var a2; + var b1; + var b2; + + var aName; + var bName; + var zeroName; + + if (absX < absY) { + if (absX < absZ) { + zeroName = xName; + aName = yName; + bName = zName; + + a1 = p0Normal.y; + a2 = p1Normal.y; + b1 = p0Normal.z; + b2 = p1Normal.z; + } else { + zeroName = zName; + aName = xName; + bName = yName; + + a1 = p0Normal.x; + a2 = p1Normal.x; + b1 = p0Normal.y; + b2 = p1Normal.y; + } + } else if (absY < absZ) { + zeroName = yName; + aName = xName; + bName = zName; + + a1 = p0Normal.x; + a2 = p1Normal.x; + b1 = p0Normal.z; + b2 = p1Normal.z; + } else { + zeroName = zName; + aName = xName; + bName = yName; + + a1 = p0Normal.x; + a2 = p1Normal.x; + b1 = p0Normal.y; + b2 = p1Normal.y; + } + + var d1 = p0.distance; + var d2 = p1.distance; + + var point = result.point; + point[zeroName] = 0.0; + + if (CesiumMath.equalsEpsilon(a1, 0.0, CesiumMath.EPSILON6)) { + point[aName] = 0.0; + point[bName] = -d1 / b1; + } else if (CesiumMath.equalsEpsilon(b1, 0.0, CesiumMath.EPSILON6)) { + point[bName] = 0.0; + point[aName] = -d1 / a1; + } else { + var ratio = a2 / a1; + point[bName] = (ratio * d1 - d2) / (b2 - ratio * b1); + point[aName] = (-b1 * point[bName] - d1) / b1; + } + }; + + var scratchLineCircleDiff = new Cartesian2(); + var scratchLineCircleRoots = { + root0 : 0.0, + root1 : 0.0 + }; + + IntersectionTests.lineCircle = function(line, circle, result) { + var direction = line.direction; + var point = line.point; + + var center = circle.center; + var radius = circle.radius; + + var diff = Cartesian2.subtract(point, center, scratchLineCircleDiff); + + var a = Cartesian2.magnitudeSquared(direction); + var b = 2.0 * Cartesian2.dot(direction, diff); + var c = Cartesian2.magnitudeSquared(diff) - radius * radius; + + var roots = solveQuadratic(a, b, c, scratchLineCircleRoots); + if (!defined(roots)) { + return undefined; + } + + if (!defined(result)) { + result = { + intersection0 : new Cartesian2(), + intersection1 : new Cartesian2() + }; + } + + var pointOnCircle = result.intersection0; + Cartesian2.multiplyByScalar(direction, roots.root0, pointOnCircle); + Cartesian2.add(point, pointOnCircle, pointOnCircle); + + pointOnCircle = result.intersection1; + Cartesian2.multiplyByScalar(direction, roots.root1, pointOnCircle); + Cartesian2.add(point, pointOnCircle, pointOnCircle); + + return result; + }; + return IntersectionTests; }); From 69f6483c74e8b973d1acda291488e08be60a41e4 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 5 Jun 2014 19:30:18 -0400 Subject: [PATCH 31/74] Add triangle-circle arc intersection test. --- Source/Core/IntersectionTests.js | 105 +++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 9e3e996306d3..8d92c75fb328 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -8,6 +8,8 @@ define([ './DeveloperError', './Math', './Matrix3', + './Matrix4', + './Plane', './QuadraticRealPolynomial', './QuarticRealPolynomial' ], function( @@ -19,6 +21,8 @@ define([ DeveloperError, CesiumMath, Matrix3, + Matrix4, + Plane, QuadraticRealPolynomial, QuarticRealPolynomial) { "use strict"; @@ -1020,5 +1024,106 @@ define([ return result; }; + var scratchCartesian1 = new Cartesian3(); + var scratchCartesian2 = new Cartesian3(); + var scratchCartesian3 = new Cartesian3(); + var scratchTrianglePlane = new Plane(new Cartesian3(), 0.0); + var scratchCirclePlane = new Plane(new Cartesian3(), 0.0); + var scratchPlanePlaneIntersection = { + direction : new Cartesian3(), + point : new Cartesian3() + }; + var scratchCircle = { + center : new Cartesian3(), + radius : 0.0 + }; + var scratchLineCircleIntersection = { + intersection0 : new Cartesian2(), + intersection1 : new Cartesian2() + }; + var scratchTransform = new Matrix4(); + var scratchInvTransform = new Matrix4(); + + IntersectionTests.triangleArc = function(p0, p1, p2, center, radius, v0, v1, result) { + Cartesian3.subtract(p1, p0, scratchCartesian1); + Cartesian3.subtract(p2, p0, scratchCartesian2); + var normal = Cartesian3.cross(scratchCartesian1, scratchCartesian3, scratchCartesian3); + + if (Cartesian3.equalsEpsilon(normal, Cartesian3.ZERO, CesiumMath.EPSILON6)) { + return undefined; + } + + Cartesian3.normalize(normal, normal); + var trianglePlane = Plane.fromPointNormal(p0, normal, scratchTrianglePlane); + + normal = Cartesian3.cross(v0, v1, normal); + if (Cartesian3.equalsEpsilon(normal, Cartesian3.ZERO, CesiumMath.EPSILON6)) { + return undefined; + } + + Cartesian3.normalize(normal, normal); + var circlePlane = Plane.fromPointNormal(center, normal, scratchCirclePlane); + + var line = IntersectionTests.planePlane(trianglePlane, circlePlane, scratchPlanePlaneIntersection); + if (!defined(line)) { + return undefined; + } + + var xBasis = Cartesian3.normalize(v0, scratchCartesian1); + var zBasis = circlePlane.normal; + var yBasis = Cartesian3.cross(zBasis, xBasis, scratchCartesian2); + + var transform = scratchTransform; + transform[0] = xBasis.x; + transform[1] = xBasis.y; + transform[2] = xBasis.z; + transform[3] = 0.0; + transform[4] = yBasis.x; + transform[5] = yBasis.y; + transform[6] = yBasis.z; + transform[7] = 0.0; + transform[8] = zBasis.x; + transform[9] = zBasis.y; + transform[10] = zBasis.z; + transform[11] = 0.0; + transform[12] = center.x; + transform[13] = center.y; + transform[14] = center.z; + transform[15] = 0.0; + + Matrix4.multiplyByPoint(transform, line.point, line.point); + Matrix4.mulitplyByPointAsVector(transform, line.direction, line.direction); + Matrix4.multiplyByPointAsVector(transform, v1, scratchCartesian1); + + var circle = scratchCircle; + circle.radius = Cartesian3.magnitude(v0); + + var intersections = IntersectionTests.lineCircle(line, circle, scratchLineCircleIntersection); + if (!defined(intersections)) { + return undefined; + } + + var angle = Math.atan2(scratchCartesian1.y, scratchCartesian1.x); + + var intersection = intersections.intersection0; + var intersectionAngle = Math.atan2(intersection.y, intersection.x); + if (((angle > 0 && intersectionAngle >= 0) || (angle < 0 && intersectionAngle <= 0)) && intersectionAngle <= angle) { + intersection = Cartesian2.clone(intersections.intersection0, scratchCartesian1); + } else { + intersection = intersections.intersection1; + intersectionAngle = Math.atan2(intersection.y, intersection.x); + if (((angle > 0 && intersectionAngle >= 0) || (angle < 0 && intersectionAngle <= 0)) && intersectionAngle <= angle) { + intersection = Cartesian2.clone(intersections.intersection1, scratchCartesian1); + } else { + return undefined; + } + } + + intersection.z = 0.0; + + var invTransform = Matrix4.inverseTransformation(transform, scratchInvTransform); + return Matrix4.multiplyByPoint(invTransform, intersection, result); + }; + return IntersectionTests; }); From edb0f7fc7d2db3735a434789c0a9155bb66639a7 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 5 Jun 2014 19:53:12 -0400 Subject: [PATCH 32/74] Add terrain arc intersection test. --- Source/Core/IntersectionTests.js | 2 +- Source/Scene/Globe.js | 4 ++ Source/Scene/GlobeSurface.js | 81 ++++++++++++++++++++++++++++++++ Source/Scene/Tile.js | 33 +++++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 8d92c75fb328..7276a3af7601 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -1096,7 +1096,7 @@ define([ Matrix4.multiplyByPointAsVector(transform, v1, scratchCartesian1); var circle = scratchCircle; - circle.radius = Cartesian3.magnitude(v0); + circle.radius = radius; var intersections = IntersectionTests.lineCircle(line, circle, scratchLineCircleIntersection); if (!defined(intersections)) { diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index eec86bd57a2f..9c80b1c4fe34 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -293,6 +293,10 @@ define([ } }); + Globe.prototype.intersectArc = function(center, radius, v0, v1, frameState, result) { + return this._surface.intersectArc(center, radius, v0, v1, frameState, result); + }; + Globe.prototype.pick = function(ray, frameState, result) { return this._surface.pick(ray, frameState, result); }; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index da6958dd5d6c..842a128d03fb 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -17,6 +17,7 @@ define([ '../Core/Intersect', '../Core/IntersectionTests', '../Core/Matrix4', + '../Core/Plane', '../Core/PrimitiveType', '../Core/Queue', '../Core/Rectangle', @@ -50,6 +51,7 @@ define([ Intersect, IntersectionTests, Matrix4, + Plane, PrimitiveType, Queue, Rectangle, @@ -280,6 +282,85 @@ define([ stop : 0.0 }; + GlobeSurface.prototype.intersectArc = function(center, radius, v0, v1, frameState, result) { + var stack = []; + var sphereIntersections = scratchSphereIntersections; + sphereIntersections.length = 0; + + var tile; + var i; + + var levelZeroTiles = this._levelZeroTiles; + var length = levelZeroTiles.length; + for (i = 0; i < length; ++i) { + stack.push(levelZeroTiles[i]); + } + + var normal = Cartesian3.cross(v0 , v1); + Cartesian3.normalize(normal, normal); + var plane = Plane.fromPointNormal(center, normal); + var boundingVolume; + + while (stack.length > 0) { + tile = stack.pop(); + if (tile.state < TileState.READY) { + while (!defined(tile.pickTerrain) && defined(parent)) { + tile = tile.parent; + } + + sphereIntersections.push(tile); + continue; + } + + boundingVolume = tile.pickBoundingSphere; + if (frameState.mode !== SceneMode.SCENE3D) { + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.scene2D.projection, tile.minimumHeight, tile.maximumHeight, boundingVolume); + Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); + } else { + BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); + } + + var sphereSphereIntersection = Cartesian3.distance(center, boundingVolume.center) <= radius + boundingVolume.radius; + //var spherePlaneIntersection = defined(IntersectionTests.spherePlane(boundingVolume, plane)); + var spherePlaneIntersection = BoundingSphere.intersect(boundingVolume, plane) === Intersect.INTERSECTING; + if (sphereSphereIntersection && spherePlaneIntersection) { + var children = tile.children; + var childrenLength = children.length; + for (i = 0; i < childrenLength; ++i) { + stack.push(children[i]); + } + } + } + + length = sphereIntersections.length; + if (length === 0) { + return undefined; + } + + sphereIntersections.sort(createComparePickTileFunction(frameState.camera.position)); + + var currentTile = sphereIntersections[0]; + var uniqueIntersections = [currentTile]; + for (i = 1; i < length; ++i) { + tile = sphereIntersections[i]; + if (tile !== currentTile) { + uniqueIntersections.push(tile); + currentTile = tile; + } + } + + var intersection; + length = uniqueIntersections.length; + for (i = 0; i < length; ++i) { + intersection = uniqueIntersections[i].intersectArc(center, radius, v0, v1, frameState, result); + if (defined(intersection)) { + break; + } + } + + return intersection; + }; + GlobeSurface.prototype.pick = function(ray, frameState, result) { var stack = []; var sphereIntersections = scratchSphereIntersections; diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 6f578fe7e545..ec7af417fed4 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -362,6 +362,39 @@ define([ return undefined; }; + Tile.prototype.intersectArc = function(center, radius, v0, v1, frameState, result) { + var terrain = this.pickTerrain; + if (!defined(terrain)) { + return undefined; + } + + var mesh = terrain.mesh; + if (!defined(mesh)) { + return undefined; + } + + var vertices = mesh.vertices; + var indices = mesh.indices; + + var length = indices.length; + for (var i = 0; i < length; i += 3) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + + var p0 = getPosition(this, frameState, vertices, i0, scratchV0); + var p1 = getPosition(this, frameState, vertices, i1, scratchV1); + var p2 = getPosition(this, frameState, vertices, i2, scratchV2); + + var intersection = IntersectionTests.triangleArc(p0, p1, p2, center, radius, v0, v1, result); + if (defined(intersection)) { + return intersection; + } + } + + return undefined; + }; + Tile.prototype.freeResources = function() { if (defined(this.waterMaskTexture)) { --this.waterMaskTexture.referenceCount; From 658beaba996cfbcc2fc191c46c3f238b7fccc8e6 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 6 Jun 2014 14:49:58 -0400 Subject: [PATCH 33/74] Some terrain arc intersection fixes. --- Source/Core/IntersectionTests.js | 8 +++-- Source/Scene/GlobeSurface.js | 7 ++--- Source/Scene/ScreenSpaceCameraController.js | 34 +++++++++------------ 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 7276a3af7601..b4f68ae0cef4 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -892,7 +892,7 @@ define([ var yName = 'y'; var zName = 'z'; - IntersectionTests.planePlaneIntersection = function(p0, p1, result) { + IntersectionTests.planePlane = function(p0, p1, result) { var p0Normal = p0.normal; var p1Normal = p1.normal; @@ -980,6 +980,8 @@ define([ point[bName] = (ratio * d1 - d2) / (b2 - ratio * b1); point[aName] = (-b1 * point[bName] - d1) / b1; } + + return result; }; var scratchLineCircleDiff = new Cartesian2(); @@ -1047,7 +1049,7 @@ define([ IntersectionTests.triangleArc = function(p0, p1, p2, center, radius, v0, v1, result) { Cartesian3.subtract(p1, p0, scratchCartesian1); Cartesian3.subtract(p2, p0, scratchCartesian2); - var normal = Cartesian3.cross(scratchCartesian1, scratchCartesian3, scratchCartesian3); + var normal = Cartesian3.cross(scratchCartesian1, scratchCartesian2, scratchCartesian3); if (Cartesian3.equalsEpsilon(normal, Cartesian3.ZERO, CesiumMath.EPSILON6)) { return undefined; @@ -1092,7 +1094,7 @@ define([ transform[15] = 0.0; Matrix4.multiplyByPoint(transform, line.point, line.point); - Matrix4.mulitplyByPointAsVector(transform, line.direction, line.direction); + Matrix4.multiplyByPointAsVector(transform, line.direction, line.direction); Matrix4.multiplyByPointAsVector(transform, v1, scratchCartesian1); var circle = scratchCircle; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 842a128d03fb..7ee919820338 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -296,9 +296,9 @@ define([ stack.push(levelZeroTiles[i]); } - var normal = Cartesian3.cross(v0 , v1); - Cartesian3.normalize(normal, normal); - var plane = Plane.fromPointNormal(center, normal); + var plane = Cartesian3.cross(v0, v1, new Cartesian4()); + Cartesian3.normalize(plane, plane); + plane.w = -Cartesian3.dot(center, plane); var boundingVolume; while (stack.length > 0) { @@ -321,7 +321,6 @@ define([ } var sphereSphereIntersection = Cartesian3.distance(center, boundingVolume.center) <= radius + boundingVolume.radius; - //var spherePlaneIntersection = defined(IntersectionTests.spherePlane(boundingVolume, plane)); var spherePlaneIntersection = BoundingSphere.intersect(boundingVolume, plane) === Intersect.INTERSECTING; if (sphereSphereIntersection && spherePlaneIntersection) { var children = tile.children; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 153826b393a5..e61795b08e9d 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -15,8 +15,10 @@ define([ '../Core/isArray', '../Core/KeyboardEventModifier', '../Core/Math', + '../Core/Matrix3', '../Core/Matrix4', '../Core/Plane', + '../Core/Quaternion', '../Core/Ray', '../Core/Transforms', './AnimationCollection', @@ -39,8 +41,10 @@ define([ isArray, KeyboardEventModifier, CesiumMath, + Matrix3, Matrix4, Plane, + Quaternion, Ray, Transforms, AnimationCollection, @@ -464,7 +468,7 @@ define([ return distance * ratio; } - function adjustRotateForTerrain(controller, frameState, radius, angle, direction, globeOverride) { + function adjustRotateForTerrain(controller, frameState, center, axis, angle, globeOverride) { var globe = defaultValue(globeOverride, controller._globe); if (!defined(globe)) { return angle; @@ -472,28 +476,18 @@ define([ var camera = controller._camera; - var distance = 2.0 * radius * Math.sin(Math.abs(angle) * 0.5); + var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle)); + var v0 = Cartesian3.subtract(camera.position, center); + var v1 = Matrix3.multiplyByVector(rotation, v0); + var radius = Cartesian3.magnitude(v0); - var ray = adjustForTerrainRay; - Cartesian3.clone(camera.position, ray.origin); - if (angle < 0.0) { - Cartesian3.negate(direction, ray.direction); - } else { - Cartesian3.clone(direction, ray.direction); - } - - var intersection = globe.pick(ray, frameState, adjustForTerrainCartesian3); + var intersection = globe.intersectArc(center, radius, v0, v1, frameState, adjustForTerrainCartesian3); if (!defined(intersection)) { return angle; } - var dist = Cartesian3.distance(ray.origin, intersection) - controller.minimumZoomDistance; - if (dist <= 0.0 || distance === 0.0) { - return 0.0; - } - - var ratio = CesiumMath.clamp(dist / distance, 0.0, 1.0); - return angle * ratio; + var newAngle = Math.acos(Cartesian3.dot(intersection, v0) / (Cartesian3.magnitude(intersection) * radius)); + return CesiumMath.sign(angle) * CesiumMath.clamp(newAngle, 0.0, Math.abs(angle)); } function handleZoom(object, startPosition, movement, frameState, zoomFactor, distanceMeasure, unitPositionDotDirection) { @@ -917,12 +911,12 @@ define([ var radius = Cartesian3.distance(camera.position, center); if (!rotateOnlyVertical) { - deltaPhi = adjustRotateForTerrain(controller, frameState, radius, deltaPhi, camera.right, globeOverride); + deltaPhi = adjustRotateForTerrain(controller, frameState, center, camera.right, deltaPhi, globeOverride); camera.rotateRight(deltaPhi, transform); } if (!rotateOnlyHorizontal) { - deltaTheta = adjustRotateForTerrain(controller, frameState, radius, deltaTheta, camera.up, globeOverride); + deltaTheta = adjustRotateForTerrain(controller, frameState, center, camera.up, deltaTheta, globeOverride); camera.rotateUp(deltaTheta, transform); } From ecf42b0f611fa857ff55f717e404bd41e12a2f62 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 6 Jun 2014 20:12:04 -0400 Subject: [PATCH 34/74] Discretize arc of circle into line segments to use for camera-terrain intersection. --- Source/Core/IntersectionTests.js | 62 +++++++++++++++------ Source/Scene/GlobeSurface.js | 39 +++++++++---- Source/Scene/ScreenSpaceCameraController.js | 43 +++++++------- Source/Scene/Tile.js | 20 +++++-- 4 files changed, 114 insertions(+), 50 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 8fc4892d9ffb..513b4f353072 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -8,7 +8,8 @@ define([ './Math', './Matrix3', './QuadraticRealPolynomial', - './QuarticRealPolynomial' + './QuarticRealPolynomial', + './Ray' ], function( Cartesian3, Cartographic, @@ -18,7 +19,8 @@ define([ CesiumMath, Matrix3, QuadraticRealPolynomial, - QuarticRealPolynomial) { + QuarticRealPolynomial, + Ray) { "use strict"; /** @@ -71,19 +73,7 @@ define([ var scratchTVec = new Cartesian3(); var scratchQVec = new Cartesian3(); - /** - * Computes the intersection of a ray and a triangle. - * @memberof IntersectionTests - * - * @param {Ray} ray The ray. - * @param {Cartesian3} p0 The first vertex of the triangle. - * @param {Cartesian3} p1 The second vertex of the triangle. - * @param {Cartesian3} p2 The third vertex of the triangle. - * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle - * and return undefined for intersections with the back face. - * @returns {Cartesian3} The intersection point or undefined if there is no intersections. - */ - IntersectionTests.rayTriangle = function(ray, p0, p1, p2, cullBackFacing, result) { + function rayTriangle(ray, p0, p1, p2, cullBackFacing) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError('ray is required.'); @@ -158,8 +148,46 @@ define([ t = Cartesian3.dot(edge1, q) * invDet; } - result = Cartesian3.multiplyByScalar(direction, t, result); - return Cartesian3.add(origin, result, result); + return t; + } + + /** + * Computes the intersection of a ray and a triangle. + * @memberof IntersectionTests + * + * @param {Ray} ray The ray. + * @param {Cartesian3} p0 The first vertex of the triangle. + * @param {Cartesian3} p1 The second vertex of the triangle. + * @param {Cartesian3} p2 The third vertex of the triangle. + * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle + * and return undefined for intersections with the back face. + * @returns {Cartesian3} The intersection point or undefined if there is no intersections. + */ + IntersectionTests.rayTriangle = function(ray, p0, p1, p2, cullBackFacing, result) { + var t = rayTriangle(ray, p0, p1, p2, cullBackFacing); + if (!defined(t) || t < 0.0) { + return undefined; + } + + result = Cartesian3.multiplyByScalar(ray.direction, t, result); + return Cartesian3.add(ray.origin, result, result); + }; + + var scratchLineSegmentTriangleRay = new Ray(); + + IntersectionTests.lineSegmentTriangle = function(v0, v1, p0, p1, p2, cullBackFacing, result) { + var ray = scratchLineSegmentTriangleRay; + Cartesian3.clone(v0, ray.origin); + Cartesian3.subtract(v1, v0, ray.direction); + Cartesian3.normalize(ray.direction, ray.direction); + + var t = rayTriangle(ray, p0, p1, p2, cullBackFacing); + if (!defined(t) || t < 0.0 || t > Cartesian3.distance(v0, v1)) { + return undefined; + } + + result = Cartesian3.multiplyByScalar(ray.direction, t, result); + return Cartesian3.add(ray.origin, result, result); }; /** diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index da6958dd5d6c..ebf7cc5dd285 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -16,9 +16,11 @@ define([ '../Core/IndexDatatype', '../Core/Intersect', '../Core/IntersectionTests', + '../Core/isArray', '../Core/Matrix4', '../Core/PrimitiveType', '../Core/Queue', + '../Core/Ray', '../Core/Rectangle', '../Core/TerrainProvider', '../Core/WebMercatorProjection', @@ -49,9 +51,11 @@ define([ IndexDatatype, Intersect, IntersectionTests, + isArray, Matrix4, PrimitiveType, Queue, + Ray, Rectangle, TerrainProvider, WebMercatorProjection, @@ -280,10 +284,15 @@ define([ stop : 0.0 }; - GlobeSurface.prototype.pick = function(ray, frameState, result) { + GlobeSurface.prototype.pick = function(rays, frameState, result) { + if (!isArray(rays)) { + rays = [rays]; + } + var stack = []; var sphereIntersections = scratchSphereIntersections; sphereIntersections.length = 0; + var raysLength = rays.length; var tile; var i; @@ -313,12 +322,20 @@ define([ BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); } - var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); - if (defined(boundingSphereIntersection)) { - var children = tile.children; - var childrenLength = children.length; - for (i = 0; i < childrenLength; ++i) { - stack.push(children[i]); + for (i = 0; i < raysLength; ++i) { + var ray = new Ray(); + Cartesian3.clone(rays[i].origin, ray.origin); + Cartesian3.normalize(rays[i].direction, ray.direction); + + //var ray = rays[i]; + var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); + if (defined(boundingSphereIntersection)) { + var children = tile.children; + var childrenLength = children.length; + for (i = 0; i < childrenLength; ++i) { + stack.push(children[i]); + } + break; } } } @@ -328,7 +345,7 @@ define([ return undefined; } - sphereIntersections.sort(createComparePickTileFunction(ray.origin)); + sphereIntersections.sort(createComparePickTileFunction(rays[0].origin)); var currentTile = sphereIntersections[0]; var uniqueIntersections = [currentTile]; @@ -343,7 +360,7 @@ define([ var intersection; length = uniqueIntersections.length; for (i = 0; i < length; ++i) { - intersection = uniqueIntersections[i].pick(ray, frameState, result); + intersection = uniqueIntersections[i].pick(rays, frameState, result); if (defined(intersection)) { break; } @@ -401,10 +418,12 @@ define([ sphereIntersections.sort(createComparePickTileFunction(ray.origin)); + var rays = [ray]; + var intersection; length = sphereIntersections.length; for (i = 0; i < length; ++i) { - intersection = sphereIntersections[i].pick(ray, frameState, result); + intersection = sphereIntersections[i].pick(rays, frameState, result); if (defined(intersection)) { break; } diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 153826b393a5..239749cdae7c 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -15,8 +15,10 @@ define([ '../Core/isArray', '../Core/KeyboardEventModifier', '../Core/Math', + '../Core/Matrix3', '../Core/Matrix4', '../Core/Plane', + '../Core/Quaternion', '../Core/Ray', '../Core/Transforms', './AnimationCollection', @@ -39,8 +41,10 @@ define([ isArray, KeyboardEventModifier, CesiumMath, + Matrix3, Matrix4, Plane, + Quaternion, Ray, Transforms, AnimationCollection, @@ -464,7 +468,7 @@ define([ return distance * ratio; } - function adjustRotateForTerrain(controller, frameState, radius, angle, direction, globeOverride) { + function adjustRotateForTerrain(controller, frameState, center, radius, axis, angle, globeOverride) { var globe = defaultValue(globeOverride, controller._globe); if (!defined(globe)) { return angle; @@ -472,28 +476,29 @@ define([ var camera = controller._camera; - var distance = 2.0 * radius * Math.sin(Math.abs(angle) * 0.5); - - var ray = adjustForTerrainRay; - Cartesian3.clone(camera.position, ray.origin); - if (angle < 0.0) { - Cartesian3.negate(direction, ray.direction); - } else { - Cartesian3.clone(direction, ray.direction); + var numRays = Math.ceil(radius * angle / 1000.0); + var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle / numRays)); + var start = Cartesian3.subtract(camera.position, center); + + var rays = []; + for (var i = 0; i < numRays; ++i) { + var stop = Matrix3.multiplyByVector(rotation, start); + var direction = Cartesian3.subtract(stop, start); + //Cartesian3.normalize(direction, direction); + var origin = Cartesian3.add(center, start); + rays.push(new Ray(origin, direction)); + start = stop; } - var intersection = globe.pick(ray, frameState, adjustForTerrainCartesian3); + var intersection = globe.pick(rays, frameState, adjustForTerrainCartesian3); if (!defined(intersection)) { return angle; } - var dist = Cartesian3.distance(ray.origin, intersection) - controller.minimumZoomDistance; - if (dist <= 0.0 || distance === 0.0) { - return 0.0; - } - - var ratio = CesiumMath.clamp(dist / distance, 0.0, 1.0); - return angle * ratio; + var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.position, center)); + var endDirection = Cartesian3.normalize(Cartesian3.subtract(intersection, center)); + var newAngle = Math.acos(Cartesian3.dot(startDirection, endDirection)); + return CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance / radius, 0.0, Math.abs(angle)); } function handleZoom(object, startPosition, movement, frameState, zoomFactor, distanceMeasure, unitPositionDotDirection) { @@ -917,12 +922,12 @@ define([ var radius = Cartesian3.distance(camera.position, center); if (!rotateOnlyVertical) { - deltaPhi = adjustRotateForTerrain(controller, frameState, radius, deltaPhi, camera.right, globeOverride); + deltaPhi = adjustRotateForTerrain(controller, frameState, center, radius, camera.up, deltaPhi, globeOverride); camera.rotateRight(deltaPhi, transform); } if (!rotateOnlyHorizontal) { - deltaTheta = adjustRotateForTerrain(controller, frameState, radius, deltaTheta, camera.up, globeOverride); + deltaTheta = adjustRotateForTerrain(controller, frameState, center, radius, camera.right, deltaTheta, globeOverride); camera.rotateUp(deltaTheta, transform); } diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 6f578fe7e545..fc00dab91458 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -329,7 +329,7 @@ define([ var scratchV1 = new Cartesian3(); var scratchV2 = new Cartesian3(); - Tile.prototype.pick = function(ray, frameState, result) { + Tile.prototype.pick = function(rays, frameState, result) { var terrain = this.pickTerrain; if (!defined(terrain)) { return undefined; @@ -344,6 +344,8 @@ define([ var indices = mesh.indices; var length = indices.length; + var raysLength = rays.length; + for (var i = 0; i < length; i += 3) { var i0 = indices[i]; var i1 = indices[i + 1]; @@ -353,9 +355,19 @@ define([ var v1 = getPosition(this, frameState, vertices, i1, scratchV1); var v2 = getPosition(this, frameState, vertices, i2, scratchV2); - var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, result); - if (defined(intersection)) { - return intersection; + for (var j = 0; j < raysLength; ++j) { + var ray = rays[j]; + var intersection; + if (Cartesian3.magnitudeSquared(ray) > 1.0) { + intersection = IntersectionTests.lineSegmentTriangle(ray.origin, Cartesian3.add(ray.origin, ray.direction), v0, v1, v2, true, result); + } else { + intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, result); + } + + //var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, result); + if (defined(intersection)) { + return intersection; + } } } From 4df3dde279d96acb00a812995947706697a4bc16 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 15:03:36 -0400 Subject: [PATCH 35/74] Fix intersection when tilting left. Add intersection tests when panning in 3D. --- Source/Scene/ScreenSpaceCameraController.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 239749cdae7c..82847386dbfe 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -476,7 +476,7 @@ define([ var camera = controller._camera; - var numRays = Math.ceil(radius * angle / 1000.0); + var numRays = Math.min(Math.ceil(radius * Math.abs(angle) / 1000.0), 1.0); var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle / numRays)); var start = Cartesian3.subtract(camera.position, center); @@ -484,7 +484,6 @@ define([ for (var i = 0; i < numRays; ++i) { var stop = Matrix3.multiplyByVector(rotation, start); var direction = Cartesian3.subtract(stop, start); - //Cartesian3.normalize(direction, direction); var origin = Cartesian3.add(center, start); rays.push(new Ray(origin, direction)); start = stop; @@ -966,6 +965,8 @@ define([ p0 = camera.worldToCameraCoordinates(p0, p0); p1 = camera.worldToCameraCoordinates(p1, p1); + var cameraPosMag = Cartesian3.magnitude(camera.position); + if (!defined(camera.constrainedAxis)) { Cartesian3.normalize(p0, p0); Cartesian3.normalize(p1, p1); @@ -974,6 +975,7 @@ define([ if (dot < 1.0 && !Cartesian3.equalsEpsilon(axis, Cartesian3.ZERO, CesiumMath.EPSILON14)) { // dot is in [0, 1] var angle = Math.acos(dot); + angle = adjustRotateForTerrain(controller, frameState, Cartesian3.ZERO, cameraPosMag, axis, angle); camera.rotate(axis, angle); } } else { @@ -1033,7 +1035,10 @@ define([ deltaTheta = startTheta - endTheta; } + deltaPhi = adjustRotateForTerrain(controller, frameState, Cartesian3.ZERO, cameraPosMag, camera.up, deltaPhi); camera.rotateRight(deltaPhi); + + deltaTheta = adjustRotateForTerrain(controller, frameState, Cartesian3.ZERO, cameraPosMag, camera.right, deltaTheta); camera.rotateUp(deltaTheta); } } From 4aabf2916f631fdaec8a463045c8224921f60bc7 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 15:23:50 -0400 Subject: [PATCH 36/74] Clean up intersection tests. --- Source/Core/IntersectionTests.js | 396 +------------------------------ 1 file changed, 13 insertions(+), 383 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 943f009cbeba..4d1520d7e276 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -1,6 +1,5 @@ /*global define*/ define([ - './Cartesian2', './Cartesian3', './Cartographic', './defaultValue', @@ -8,13 +7,10 @@ define([ './DeveloperError', './Math', './Matrix3', - './Matrix4', - './Plane', './QuadraticRealPolynomial', './QuarticRealPolynomial', './Ray' ], function( - Cartesian2, Cartesian3, Cartographic, defaultValue, @@ -22,8 +18,6 @@ define([ DeveloperError, CesiumMath, Matrix3, - Matrix4, - Plane, QuadraticRealPolynomial, QuarticRealPolynomial, Ray) { @@ -181,6 +175,19 @@ define([ var scratchLineSegmentTriangleRay = new Ray(); + /** + * Computes the intersection of a line segment and a triangle. + * @memberof IntersectionTests + * + * @param {Cartesian3} v0 The an end point of the line segment. + * @param {Cartesian3} v1 The other end point of the line segment. + * @param {Cartesian3} p0 The first vertex of the triangle. + * @param {Cartesian3} p1 The second vertex of the triangle. + * @param {Cartesian3} p2 The third vertex of the triangle. + * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle + * and return undefined for intersections with the back face. + * @returns {Cartesian3} The intersection point or undefined if there is no intersections. + */ IntersectionTests.lineSegmentTriangle = function(v0, v1, p0, p1, p2, cullBackFacing, result) { var ray = scratchLineSegmentTriangleRay; Cartesian3.clone(v0, ray.origin); @@ -778,382 +785,5 @@ define([ return undefined; }; - IntersectionTests.spherePlane = function(sphere, plane, result) { - //>>includeStart('debug', pragmas.debug); - if (!defined(sphere)) { - throw new DeveloperError('sphere is required.'); - } - if (!defined(plane)) { - throw new DeveloperError('plane is required.'); - } - //>>includeEnd('debug'); - - var sphereCenter = sphere.center; - var sphereRadius = sphere.radius; - - var planeNormal = plane.normal; - var planeD = plane.distance; - - var p = Cartesian3.dot(planeNormal, sphereCenter) + planeD; - var planeNormalMagSqrd = Cartesian3.magnitudeSquared(planeNormal); - var d = Math.abs(p) / Math.sqrt(planeNormalMagSqrd); - - if (sphereRadius < d) { - return undefined; - } - - if (!defined(result)) { - result = {}; - } - - var center = result.center; - if (!defined(result.center)) { - center = result.center = new Cartesian3(); - } - - var c = p / planeNormalMagSqrd; - Cartesian3.multiplyByScalar(planeNormal, c, center); - Cartesian3.subtract(sphereCenter, center, center); - - result.radius = (sphereRadius > d) ? Math.sqrt(sphereRadius * sphereRadius - d * d) : 0.0; - - return result; - }; - - var scratchCircleCircleRoots = { - root0 : 0.0, - root1 : 0.0 - }; - - IntersectionTests.circleCircle = function(c0, c1, result) { - var p0 = c0.center; - var p1 = c1.center; - if (Cartesian2.equalsEpsilon(p0, p1, CesiumMath.EPSILON6)) { - return undefined; - } - - var r0 = c0.radius; - var r1 = c1.radius; - - if (Cartesian2.distance(p0, p1) > r0 + r1) { - return undefined; - } - - var x0 = p0.x; - var y0 = p0.y; - - var x1 = p1.x; - var y1 = p1.y; - - var r0Sqrd = r0 * r0; - var x0Sqrd = x0 * x0; - var y0Sqrd = y0 * y0; - - var q = r0Sqrd - r1 * r1 + y1 * y1 - y0Sqrd + x1 * x1 - x0Sqrd; - - var a; - var b; - var c; - var roots; - - if (!CesiumMath.equalsEpsilon(y1, y0, CesiumMath.EPSILON6)) { - var invYDiff = 1.0 / (y1 - y0); - q *= 0.5 * invYDiff; - var r = (x1 - x0) * invYDiff; - - a = 1.0 + r * r; - b = 2.0 * (r * y0 - q * r - x0); - c = x0Sqrd + q * q - 2.0 * q * y0 + y0Sqrd - r0Sqrd; - - roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); - if (!defined(roots)) { - return undefined; - } - - x0 = roots.root0; - y0 = q - roots.root0 * r; - - x1 = roots.root1; - y1 = q - roots.root1 * r; - } else { - q /= 2.0 * (x1 - x0); - - a = 1.0; - b = -2.0 * y0; - c = y0Sqrd - r0Sqrd + q * q - 2.0 * q * x0 + x0Sqrd; - - roots = solveQuadratic(a, b, c, scratchCircleCircleRoots); - if (!defined(roots)) { - return undefined; - } - - x0 = x1 = q; - y0 = roots.root0; - y1 = roots.root1; - } - - if (!defined(result)) { - result = []; - } - - var cartesian = result[0]; - if (!defined(cartesian)) { - cartesian = result[0] = new Cartesian2(); - } - - cartesian.x = x0; - cartesian.y = y0; - - cartesian = result[1]; - if (!defined(cartesian)) { - cartesian = result[1] = new Cartesian2(); - } - - cartesian.x = x1; - cartesian.y = y1; - - return result; - }; - - var scratchDirection = new Cartesian3(); - var xName = 'x'; - var yName = 'y'; - var zName = 'z'; - - IntersectionTests.planePlane = function(p0, p1, result) { - var p0Normal = p0.normal; - var p1Normal = p1.normal; - - var direction = Cartesian3.cross(p0Normal, p1Normal, scratchDirection); - if (Cartesian3.equalsEpsilon(direction, Cartesian3.ZERO, CesiumMath.EPSILON6)) { - return undefined; - } - - if (!defined(result)) { - result = { - direction : new Cartesian3(), - point : new Cartesian3() - }; - } - - Cartesian3.normalize(direction, result.direction); - - var absX = Math.abs(p0Normal.x); - var absY = Math.abs(p0Normal.y); - var absZ = Math.abs(p0Normal.z); - - var a1; - var a2; - var b1; - var b2; - - var aName; - var bName; - var zeroName; - - if (absX < absY) { - if (absX < absZ) { - zeroName = xName; - aName = yName; - bName = zName; - - a1 = p0Normal.y; - a2 = p1Normal.y; - b1 = p0Normal.z; - b2 = p1Normal.z; - } else { - zeroName = zName; - aName = xName; - bName = yName; - - a1 = p0Normal.x; - a2 = p1Normal.x; - b1 = p0Normal.y; - b2 = p1Normal.y; - } - } else if (absY < absZ) { - zeroName = yName; - aName = xName; - bName = zName; - - a1 = p0Normal.x; - a2 = p1Normal.x; - b1 = p0Normal.z; - b2 = p1Normal.z; - } else { - zeroName = zName; - aName = xName; - bName = yName; - - a1 = p0Normal.x; - a2 = p1Normal.x; - b1 = p0Normal.y; - b2 = p1Normal.y; - } - - var d1 = p0.distance; - var d2 = p1.distance; - - var point = result.point; - point[zeroName] = 0.0; - - if (CesiumMath.equalsEpsilon(a1, 0.0, CesiumMath.EPSILON6)) { - point[aName] = 0.0; - point[bName] = -d1 / b1; - } else if (CesiumMath.equalsEpsilon(b1, 0.0, CesiumMath.EPSILON6)) { - point[bName] = 0.0; - point[aName] = -d1 / a1; - } else { - var ratio = a2 / a1; - point[bName] = (ratio * d1 - d2) / (b2 - ratio * b1); - point[aName] = (-b1 * point[bName] - d1) / b1; - } - - return result; - }; - - var scratchLineCircleDiff = new Cartesian2(); - var scratchLineCircleRoots = { - root0 : 0.0, - root1 : 0.0 - }; - - IntersectionTests.lineCircle = function(line, circle, result) { - var direction = line.direction; - var point = line.point; - - var center = circle.center; - var radius = circle.radius; - - var diff = Cartesian2.subtract(point, center, scratchLineCircleDiff); - - var a = Cartesian2.magnitudeSquared(direction); - var b = 2.0 * Cartesian2.dot(direction, diff); - var c = Cartesian2.magnitudeSquared(diff) - radius * radius; - - var roots = solveQuadratic(a, b, c, scratchLineCircleRoots); - if (!defined(roots)) { - return undefined; - } - - if (!defined(result)) { - result = { - intersection0 : new Cartesian2(), - intersection1 : new Cartesian2() - }; - } - - var pointOnCircle = result.intersection0; - Cartesian2.multiplyByScalar(direction, roots.root0, pointOnCircle); - Cartesian2.add(point, pointOnCircle, pointOnCircle); - - pointOnCircle = result.intersection1; - Cartesian2.multiplyByScalar(direction, roots.root1, pointOnCircle); - Cartesian2.add(point, pointOnCircle, pointOnCircle); - - return result; - }; - - var scratchCartesian1 = new Cartesian3(); - var scratchCartesian2 = new Cartesian3(); - var scratchCartesian3 = new Cartesian3(); - var scratchTrianglePlane = new Plane(new Cartesian3(), 0.0); - var scratchCirclePlane = new Plane(new Cartesian3(), 0.0); - var scratchPlanePlaneIntersection = { - direction : new Cartesian3(), - point : new Cartesian3() - }; - var scratchCircle = { - center : new Cartesian3(), - radius : 0.0 - }; - var scratchLineCircleIntersection = { - intersection0 : new Cartesian2(), - intersection1 : new Cartesian2() - }; - var scratchTransform = new Matrix4(); - var scratchInvTransform = new Matrix4(); - - IntersectionTests.triangleArc = function(p0, p1, p2, center, radius, v0, v1, result) { - Cartesian3.subtract(p1, p0, scratchCartesian1); - Cartesian3.subtract(p2, p0, scratchCartesian2); - var normal = Cartesian3.cross(scratchCartesian1, scratchCartesian2, scratchCartesian3); - - if (Cartesian3.equalsEpsilon(normal, Cartesian3.ZERO, CesiumMath.EPSILON6)) { - return undefined; - } - - Cartesian3.normalize(normal, normal); - var trianglePlane = Plane.fromPointNormal(p0, normal, scratchTrianglePlane); - - normal = Cartesian3.cross(v0, v1, normal); - if (Cartesian3.equalsEpsilon(normal, Cartesian3.ZERO, CesiumMath.EPSILON6)) { - return undefined; - } - - Cartesian3.normalize(normal, normal); - var circlePlane = Plane.fromPointNormal(center, normal, scratchCirclePlane); - - var line = IntersectionTests.planePlane(trianglePlane, circlePlane, scratchPlanePlaneIntersection); - if (!defined(line)) { - return undefined; - } - - var xBasis = Cartesian3.normalize(v0, scratchCartesian1); - var zBasis = circlePlane.normal; - var yBasis = Cartesian3.cross(zBasis, xBasis, scratchCartesian2); - - var transform = scratchTransform; - transform[0] = xBasis.x; - transform[1] = xBasis.y; - transform[2] = xBasis.z; - transform[3] = 0.0; - transform[4] = yBasis.x; - transform[5] = yBasis.y; - transform[6] = yBasis.z; - transform[7] = 0.0; - transform[8] = zBasis.x; - transform[9] = zBasis.y; - transform[10] = zBasis.z; - transform[11] = 0.0; - transform[12] = center.x; - transform[13] = center.y; - transform[14] = center.z; - transform[15] = 0.0; - - Matrix4.multiplyByPoint(transform, line.point, line.point); - Matrix4.multiplyByPointAsVector(transform, line.direction, line.direction); - Matrix4.multiplyByPointAsVector(transform, v1, scratchCartesian1); - - var circle = scratchCircle; - circle.radius = radius; - - var intersections = IntersectionTests.lineCircle(line, circle, scratchLineCircleIntersection); - if (!defined(intersections)) { - return undefined; - } - - var angle = Math.atan2(scratchCartesian1.y, scratchCartesian1.x); - - var intersection = intersections.intersection0; - var intersectionAngle = Math.atan2(intersection.y, intersection.x); - if (((angle > 0 && intersectionAngle >= 0) || (angle < 0 && intersectionAngle <= 0)) && intersectionAngle <= angle) { - intersection = Cartesian2.clone(intersections.intersection0, scratchCartesian1); - } else { - intersection = intersections.intersection1; - intersectionAngle = Math.atan2(intersection.y, intersection.x); - if (((angle > 0 && intersectionAngle >= 0) || (angle < 0 && intersectionAngle <= 0)) && intersectionAngle <= angle) { - intersection = Cartesian2.clone(intersections.intersection1, scratchCartesian1); - } else { - return undefined; - } - } - - intersection.z = 0.0; - - var invTransform = Matrix4.inverseTransformation(transform, scratchInvTransform); - return Matrix4.multiplyByPoint(invTransform, intersection, result); - }; - return IntersectionTests; }); From 4ba687035421ccbb68d19adc1550c12037223c8d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 15:59:16 -0400 Subject: [PATCH 37/74] Clean up and add doc. --- Source/Scene/Globe.js | 33 ++++++++++---- Source/Scene/GlobeSurface.js | 50 ++++++++++++++++----- Source/Scene/ScreenSpaceCameraController.js | 4 +- Source/Scene/Tile.js | 44 +++--------------- 4 files changed, 72 insertions(+), 59 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 9c80b1c4fe34..b773206cd273 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -293,12 +293,27 @@ define([ } }); - Globe.prototype.intersectArc = function(center, radius, v0, v1, frameState, result) { - return this._surface.intersectArc(center, radius, v0, v1, frameState, result); - }; - - Globe.prototype.pick = function(ray, frameState, result) { - return this._surface.pick(ray, frameState, result); + /** + * Find an intersection between rays or line segments and the globe surface. + *

+ * If the rays direction has a magnitude greater than one, then the ray is assumed to be a line + * segment with end points at the ray origin and the ray origin plus the ray direction. + *

+ * + * @memberof Globe + * + * @param {Ray|Ray[]} rays The rays or line segments to test for intersection. + * @param {FrameState} frameState The current frame state. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3|undefined} The first intersection or undefined if none were found. + * + * @example + * // find intersection of ray through a pixel and the globe + * var ray = scene.camera.getPickRay(windowCoordinates); + * var intersection = globe.rayIntersections(ray, scene.frameState); + */ + Globe.prototype.intersect = function(rays, frameState, result) { + return this._surface.intersect(rays, frameState, result); }; /** @@ -308,15 +323,15 @@ define([ * @param {Ray} ray The ray to test for intersection. * @param {FrameState} frameState The current frame state. * @param {Cartesian3} [result] The object onto which to store the result. - * @returns {Cartesian3|undefined} The intersection of undefined if none was found. + * @returns {Cartesian3|undefined} The intersection or undefined if none was found. * * @example * // find intersection of ray through a pixel and the globe * var ray = scene.camera.getPickRay(windowCoordinates); * var intersection = globe.pick(ray, scene.frameState); */ - Globe.prototype.pickRenderedSurface = function(ray, frameState, result) { - return this._surface.pickRenderedSurface(ray, frameState, result); + Globe.prototype.pick = function(ray, frameState, result) { + return this._surface.pick(ray, frameState, result); }; var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 83efa68adacb..6013e6a642ec 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -280,22 +280,46 @@ define([ }; } - var scratchSphereIntersections = []; + var scratchArray1 = []; + var scratchArray2 = []; var scratchSphereIntersectionResult = { start : 0.0, stop : 0.0 }; + var scratchArray = new Array(1); + var scratchRay = new Ray(); - GlobeSurface.prototype.pick = function(rays, frameState, result) { + /** + * Find an intersection between rays or line segments and the globe surface. + *

+ * If the rays direction has a magnitude greater than one, then the ray is assumed to be a line + * segment with end points at the ray origin and the ray origin plus the ray direction. + *

+ * + * @memberof Globe + * + * @param {Ray|Ray[]} rays The rays or line segments to test for intersection. + * @param {FrameState} frameState The current frame state. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3|undefined} The first intersection or undefined if none were found. + * + * @example + * // find intersection of ray through a pixel and the globe + * var ray = scene.camera.getPickRay(windowCoordinates); + * var intersection = globe.rayIntersections(ray, scene.frameState); + */ + GlobeSurface.prototype.intersect = function(rays, frameState, result) { if (!isArray(rays)) { - rays = [rays]; + scratchArray[0] = rays; + rays = scratchArray; } - var stack = []; - var sphereIntersections = scratchSphereIntersections; + var stack = scratchArray1; + stack.length = 0; + var sphereIntersections = scratchArray2; sphereIntersections.length = 0; - var raysLength = rays.length; + var raysLength = rays.length; var tile; var i; @@ -325,11 +349,10 @@ define([ } for (i = 0; i < raysLength; ++i) { - var ray = new Ray(); + var ray = scratchRay; Cartesian3.clone(rays[i].origin, ray.origin); Cartesian3.normalize(rays[i].direction, ray.direction); - //var ray = rays[i]; var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); if (defined(boundingSphereIntersection)) { var children = tile.children; @@ -350,7 +373,10 @@ define([ sphereIntersections.sort(createComparePickTileFunction(rays[0].origin)); var currentTile = sphereIntersections[0]; - var uniqueIntersections = [currentTile]; + var uniqueIntersections = scratchArray1; + uniqueIntersections.length = 0; + uniqueIntersections[0] = [currentTile]; + for (i = 1; i < length; ++i) { tile = sphereIntersections[i]; if (tile !== currentTile) { @@ -386,7 +412,7 @@ define([ * var intersection = surface.pick(ray, scene.frameState); */ GlobeSurface.prototype.pickRenderedSurface = function(ray, frameState, result) { - var sphereIntersections = scratchSphereIntersections; + var sphereIntersections = scratchArray1; sphereIntersections.length = 0; var tilesToRenderByTextureCount = this._tilesToRenderByTextureCount; @@ -420,7 +446,9 @@ define([ sphereIntersections.sort(createComparePickTileFunction(ray.origin)); - var rays = [ray]; + var rays = scratchArray2; + rays.length = 1; + rays[0] = ray; var intersection; length = sphereIntersections.length; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 82847386dbfe..220e12297288 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -454,7 +454,7 @@ define([ Cartesian3.clone(direction, ray.direction); } - var intersection = controller._globe.pick(ray, frameState, adjustForTerrainCartesian3); + var intersection = controller._globe.intersect(ray, frameState, adjustForTerrainCartesian3); if (!defined(intersection)) { return distance; } @@ -489,7 +489,7 @@ define([ start = stop; } - var intersection = globe.pick(rays, frameState, adjustForTerrainCartesian3); + var intersection = globe.intersect(rays, frameState, adjustForTerrainCartesian3); if (!defined(intersection)) { return angle; } diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 40880a794ca6..986cfcdaf237 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -328,6 +328,8 @@ define([ var scratchV0 = new Cartesian3(); var scratchV1 = new Cartesian3(); var scratchV2 = new Cartesian3(); + var scratchCartesian = new Cartesian3(); + var scratchResult = new Cartesian3(); Tile.prototype.pick = function(rays, frameState, result) { var terrain = this.pickTerrain; @@ -359,14 +361,15 @@ define([ var ray = rays[j]; var intersection; if (Cartesian3.magnitudeSquared(ray) > 1.0) { - intersection = IntersectionTests.lineSegmentTriangle(ray.origin, Cartesian3.add(ray.origin, ray.direction), v0, v1, v2, true, result); + var p0 = ray.origin; + var p1 = Cartesian3.add(p0, ray.direction, scratchCartesian); + intersection = IntersectionTests.lineSegmentTriangle(p0, p1, v0, v1, v2, true, scratchResult); } else { - intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, result); + intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, scratchResult); } - //var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, result); if (defined(intersection)) { - return intersection; + return Cartesian3.clone(intersection, result); } } } @@ -374,39 +377,6 @@ define([ return undefined; }; - Tile.prototype.intersectArc = function(center, radius, v0, v1, frameState, result) { - var terrain = this.pickTerrain; - if (!defined(terrain)) { - return undefined; - } - - var mesh = terrain.mesh; - if (!defined(mesh)) { - return undefined; - } - - var vertices = mesh.vertices; - var indices = mesh.indices; - - var length = indices.length; - for (var i = 0; i < length; i += 3) { - var i0 = indices[i]; - var i1 = indices[i + 1]; - var i2 = indices[i + 2]; - - var p0 = getPosition(this, frameState, vertices, i0, scratchV0); - var p1 = getPosition(this, frameState, vertices, i1, scratchV1); - var p2 = getPosition(this, frameState, vertices, i2, scratchV2); - - var intersection = IntersectionTests.triangleArc(p0, p1, p2, center, radius, v0, v1, result); - if (defined(intersection)) { - return intersection; - } - } - - return undefined; - }; - Tile.prototype.freeResources = function() { if (defined(this.waterMaskTexture)) { --this.waterMaskTexture.referenceCount; From faa232c3a36368c602967b6a96d53ad600d81502 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 16:12:30 -0400 Subject: [PATCH 38/74] Fix tests. --- Source/Scene/ScreenSpaceCameraController.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 220e12297288..8c8b891b8f39 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -1126,13 +1126,14 @@ define([ var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid, tilt3DVerticalTransform); + var globeOverride = controller._globe; var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); var constrainedAxis = Cartesian3.UNIT_Z; var restrictedAngle = CesiumMath.PI_OVER_TWO - angle; - rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true, oldGlobe); + rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true, globeOverride); var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); if (Cartesian3.dot(camera.right, tangent) < 0.0) { @@ -1145,11 +1146,11 @@ define([ var oldConstrainedAxis = camera.constrainedAxis; camera.constrainedAxis = undefined; - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, oldGlobe); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, globeOverride); camera.constrainedAxis = oldConstrainedAxis; } else { - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, oldGlobe); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, globeOverride); } controller.globe = oldGlobe; From 05968937822d9da7a993c7f4f51e1e9d357dc933 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 16:40:14 -0400 Subject: [PATCH 39/74] Update doc and add intersection test specs. --- Source/Core/IntersectionTests.js | 12 +++ Source/Scene/Globe.js | 4 + Source/Scene/GlobeSurface.js | 4 + Specs/Core/IntersectionTestsSpec.js | 149 ++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 4d1520d7e276..e870d9bae5fd 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -35,6 +35,7 @@ define([ * * @param {Ray} ray The ray. * @param {Plane} plane The plane. + * @param {Cartesian3} [result] The Cartesian3 onto which to store the result. * @returns {Cartesian3} The intersection point or undefined if there is no intersections. */ IntersectionTests.rayPlane = function(ray, plane, result) { @@ -161,6 +162,7 @@ define([ * @param {Cartesian3} p2 The third vertex of the triangle. * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle * and return undefined for intersections with the back face. + * @param {Cartesian3} [result] The Cartesian3 onto which to store the result. * @returns {Cartesian3} The intersection point or undefined if there is no intersections. */ IntersectionTests.rayTriangle = function(ray, p0, p1, p2, cullBackFacing, result) { @@ -186,9 +188,19 @@ define([ * @param {Cartesian3} p2 The third vertex of the triangle. * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle * and return undefined for intersections with the back face. + * @param {Cartesian3} [result] The Cartesian3 onto which to store the result. * @returns {Cartesian3} The intersection point or undefined if there is no intersections. */ IntersectionTests.lineSegmentTriangle = function(v0, v1, p0, p1, p2, cullBackFacing, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(v0)) { + throw new DeveloperError('v0 is required.'); + } + if (!defined(v1)) { + throw new DeveloperError('v1 is required.'); + } + //>>includeEnd('debug'); + var ray = scratchLineSegmentTriangleRay; Cartesian3.clone(v0, ray.origin); Cartesian3.subtract(v1, v0, ray.direction); diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index b773206cd273..879b94f7f91e 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -318,6 +318,10 @@ define([ /** * Find an intersection between a ray and the globe surface that was rendered. + *

+ * Prefer this method over {@link Globe#intersect} when finding an intersection with the section of + * the globe that is rendered for better performance + *

* @memberof Globe * * @param {Ray} ray The ray to test for intersection. diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 6013e6a642ec..a9814cfa7dd9 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -399,6 +399,10 @@ define([ /** * Find an intersection between a ray and the globe surface that was rendered. + *

+ * Prefer this method over {@link GlobeSurface#intersect} when finding an intersection with the section of + * the globe surface that is rendered for better performance + *

* @memberof GlobeSurface * * @param {Ray} ray The ray to test for intersection. diff --git a/Specs/Core/IntersectionTestsSpec.js b/Specs/Core/IntersectionTestsSpec.js index aafa2cc79d41..64fca635b982 100644 --- a/Specs/Core/IntersectionTestsSpec.js +++ b/Specs/Core/IntersectionTestsSpec.js @@ -158,6 +158,155 @@ defineSuite([ expect(intersection).not.toBeDefined(); }); + it('rayTriangle does not intersect behind the ray origin', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var ray = new Ray(Cartesian3.UNIT_Z, Cartesian3.UNIT_Z); + + var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('lineSegmentTriangle throws without v0', function() { + expect(function() { + IntersectionTests.lineSegmentTriangle(); + }).toThrowDeveloperError(); + }); + + it('lineSegmentTriangle throws without v1', function() { + expect(function() { + IntersectionTests.lineSegmentTriangle(new Cartesian3()); + }).toThrowDeveloperError(); + }); + + it('lineSegmentTriangle throws without p0', function() { + expect(function() { + IntersectionTests.lineSegmentTriangle(new Cartesian3(), new Cartesian3()); + }).toThrowDeveloperError(); + }); + + it('lineSegmentTriangle throws without p1', function() { + expect(function() { + IntersectionTests.lineSegmentTriangle(new Cartesian3(), new Cartesian3(), new Cartesian3()); + }).toThrowDeveloperError(); + }); + + it('lineSegmentTriangle throws without p2', function() { + expect(function() { + IntersectionTests.lineSegmentTriangle(new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()); + }).toThrowDeveloperError(); + }); + + it('lineSegmentTriangle intersects front face', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = Cartesian3.UNIT_Z; + var v1 = Cartesian3.negate(Cartesian3.UNIT_Z); + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).toEqual(Cartesian3.ZERO); + }); + + it('lineSegmentTriangle intersects back face without culling', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = Cartesian3.negate(Cartesian3.UNIT_Z); + var v1 = Cartesian3.UNIT_Z; + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).toEqual(Cartesian3.ZERO); + }); + + it('lineSegmentTriangle does not intersect back face with culling', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = Cartesian3.negate(Cartesian3.UNIT_Z); + var v1 = Cartesian3.UNIT_Z; + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2, true); + expect(intersection).not.toBeDefined(); + }); + + it('lineSegmentTriangle does not intersect outside the 0-1 edge', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = new Cartesian3(0.0, -1.0, 1.0); + var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z)); + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('lineSegmentTriangle does not intersect outside the 1-2 edge', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = new Cartesian3(1.0, 1.0, 1.0); + var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z)); + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('lineSegmentTriangle does not intersect outside the 2-0 edge', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = new Cartesian3(-1.0, 1.0, 1.0); + var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z)); + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('lineSegmentTriangle does not intersect parallel ray and triangle', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = new Cartesian3(-1.0, 0.0, 1.0); + var v1 = Cartesian3.add(v0, Cartesian3.UNIT_X); + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('lineSegmentTriangle does not intersect behind the v0', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = Cartesian3.UNIT_Z; + var v1 = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Z, 2.0); + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + + it('lineSegmentTriangle does not intersect behind the v1', function() { + var p0 = new Cartesian3(-1.0, 0.0, 0.0); + var p1 = new Cartesian3(1.0, 0.0, 0.0); + var p2 = new Cartesian3(0.0, 1.0, 0.0); + + var v0 = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Z, 2.0); + var v1 = Cartesian3.UNIT_Z; + + var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); + expect(intersection).not.toBeDefined(); + }); + it('raySphere throws without ray', function() { expect(function() { IntersectionTests.raySphere(); From a41ea5d4baafbe1459969beb2912f3e5f60b5a5e Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 17:39:30 -0400 Subject: [PATCH 40/74] Some fixes after renames and for 2D/CV. --- Source/Scene/GlobeSurface.js | 8 ++++---- Source/Scene/ScreenSpaceCameraController.js | 8 ++++---- Source/Scene/Tile.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index a9814cfa7dd9..d8168e846746 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -342,7 +342,7 @@ define([ var boundingVolume = tile.pickBoundingSphere; if (frameState.mode !== SceneMode.SCENE3D) { - BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.scene2D.projection, tile.minimumHeight, tile.maximumHeight, boundingVolume); + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, tile.minimumHeight, tile.maximumHeight, boundingVolume); Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); } else { BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); @@ -375,7 +375,7 @@ define([ var currentTile = sphereIntersections[0]; var uniqueIntersections = scratchArray1; uniqueIntersections.length = 0; - uniqueIntersections[0] = [currentTile]; + uniqueIntersections[0] = currentTile; for (i = 1; i < length; ++i) { tile = sphereIntersections[i]; @@ -415,7 +415,7 @@ define([ * var ray = scene.camera.getPickRay(windowCoordinates); * var intersection = surface.pick(ray, scene.frameState); */ - GlobeSurface.prototype.pickRenderedSurface = function(ray, frameState, result) { + GlobeSurface.prototype.pick = function(ray, frameState, result) { var sphereIntersections = scratchArray1; sphereIntersections.length = 0; @@ -435,7 +435,7 @@ define([ var boundingVolume = tile.pickBoundingSphere; if (frameState.mode !== SceneMode.SCENE3D) { - BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.scene2D.projection, tile.minimumHeight, tile.maximumHeight, boundingVolume); + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, tile.minimumHeight, tile.maximumHeight, boundingVolume); Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); } else { BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 8c8b891b8f39..184d23b939d2 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -667,7 +667,7 @@ define([ var startPlanePos; if (defined(controller._globe) && controller._camera.position.z < controller.minimumTerrainHeight) { - startPlanePos = controller._globe.pickRenderedSurface(startRay, frameState, translateCVStartPos); + startPlanePos = controller._globe.pick(startRay, frameState, translateCVStartPos); if (defined(startPlanePos)) { origin.x = startPlanePos.x; } @@ -729,7 +729,7 @@ define([ } else { ray = camera.getPickRay(startPosition, rotateCVWindowRay); if (defined(controller._globe)) { - center = controller._globe.pickRenderedSurface(ray, frameState, rotateCVCenter); + center = controller._globe.pick(ray, frameState, rotateCVCenter); } if (!defined(center)) { @@ -865,7 +865,7 @@ define([ var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; if (defined(controller._globe) && height < controller.minimumTerrainHeight) { var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); - var mousePos = controller._globe.pickRenderedSurface(startRay, frameState, scratchMousePos); + var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); if (!defined(mousePos)) { pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); } else { @@ -1091,7 +1091,7 @@ define([ } else { ray = camera.getPickRay(startPosition, tilt3DRay); if (defined(controller._globe)) { - center = controller._globe.pickRenderedSurface(ray, frameState, tilt3DCenter); + center = controller._globe.pick(ray, frameState, tilt3DCenter); } if (!defined(center)) { diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 986cfcdaf237..94df3dfc3e56 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -315,7 +315,7 @@ define([ Cartesian3.add(tile.center, result, result); if (frameState.mode !== SceneMode.SCENE3D) { - var projection = frameState.scene2D.projection; + var projection = frameState.mapProjection; var ellipsoid = projection.ellipsoid; var positionCart = ellipsoid.cartesianToCartographic(result); projection.project(positionCart, result); From 4d8c4f4ca172243ce86dabe162e3118b993fb791 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 20:54:32 -0400 Subject: [PATCH 41/74] Add camera-terrain collision detection to Columbus view. --- Source/Scene/ScreenSpaceCameraController.js | 30 +++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 402279552162..58a5e1db4d02 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -447,11 +447,15 @@ define([ var camera = controller._camera; var ray = adjustForTerrainRay; - Cartesian3.clone(camera.position, ray.origin); + Cartesian3.clone(camera.positionWC, ray.origin); if (distance < 0.0) { - Cartesian3.negate(direction, ray.direction); + direction = Cartesian3.negate(direction, ray.direction); } else { - Cartesian3.clone(direction, ray.direction); + direction = Cartesian3.clone(direction, ray.direction); + } + + if (frameState.mode !== SceneMode.SCENE3D) { + Cartesian3.fromElements(direction.z, direction.x, direction.y, direction); } var intersection = controller._globe.intersect(ray, frameState, adjustForTerrainCartesian3); @@ -476,9 +480,16 @@ define([ var camera = controller._camera; + if (frameState.mode !== SceneMode.SCENE3D) { + var cart = frameState.mapProjection.ellipsoid.cartesianToCartographic(center); + center = frameState.mapProjection.project(cart); + center = Cartesian3.fromElements(center.z, center.x, center.y); + axis = Cartesian3.fromElements(axis.z, axis.x, axis.y); + } + var numRays = Math.min(Math.ceil(radius * Math.abs(angle) / 1000.0), 1.0); var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle / numRays)); - var start = Cartesian3.subtract(camera.position, center); + var start = Cartesian3.subtract(camera.positionWC, center); var rays = []; for (var i = 0; i < numRays; ++i) { @@ -494,7 +505,7 @@ define([ return angle; } - var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.position, center)); + var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.positionWC, center)); var endDirection = Cartesian3.normalize(Cartesian3.subtract(intersection, center)); var newAngle = Math.acos(Cartesian3.dot(startDirection, endDirection)); return CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance / radius, 0.0, Math.abs(angle)); @@ -698,6 +709,8 @@ define([ var mag = Cartesian3.magnitude(diff); if (mag > CesiumMath.EPSILON6) { Cartesian3.normalize(diff, diff); + + mag = adjustForTerrain(controller, frameState, mag, diff); camera.move(diff, mag); } } @@ -781,11 +794,12 @@ define([ var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, ellipsoid, rotateCVVerticalTransform); + var globeOverride = controller._globe; var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; var constrainedAxis = Cartesian3.UNIT_Z; - rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, undefined, false, true); + rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, undefined, false, true, globeOverride); var tangent = Cartesian3.cross(Cartesian3.UNIT_Z, Cartesian3.normalize(camera.position, rotateCVCartesian3), rotateCVCartesian3); if (Cartesian3.dot(camera.right, tangent) < 0.0) { @@ -797,11 +811,11 @@ define([ var oldConstrainedAxis = camera.constrainedAxis; camera.constrainedAxis = undefined; - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false, globeOverride); camera.constrainedAxis = oldConstrainedAxis; } else { - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false, globeOverride); } controller.globe = oldGlobe; From 31bbebf8c46e49d85085eaaba739ff156a9148d5 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 9 Jun 2014 21:08:00 -0400 Subject: [PATCH 42/74] Clean up garbage. --- Source/Scene/ScreenSpaceCameraController.js | 46 ++++++++++++++------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 58a5e1db4d02..553b2815ccb2 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -472,6 +472,15 @@ define([ return distance * ratio; } + var scratchAdjustForTerrainCart = new Cartographic(); + var scratchAdjustForTerrainCenter = new Cartesian3(); + var scratchAdjustForTerrainAxis = new Cartesian3(); + var scratchQuaternion = new Quaternion(); + var scratchRotation = new Matrix3(); + var scratchAdjustForTerrainStart = new Cartesian3(); + var scratchAdjustForTerrainStop = new Cartesian3(); + var scratchRayArray = []; + function adjustRotateForTerrain(controller, frameState, center, radius, axis, angle, globeOverride) { var globe = defaultValue(globeOverride, controller._globe); if (!defined(globe)) { @@ -481,23 +490,30 @@ define([ var camera = controller._camera; if (frameState.mode !== SceneMode.SCENE3D) { - var cart = frameState.mapProjection.ellipsoid.cartesianToCartographic(center); - center = frameState.mapProjection.project(cart); - center = Cartesian3.fromElements(center.z, center.x, center.y); - axis = Cartesian3.fromElements(axis.z, axis.x, axis.y); + var cart = frameState.mapProjection.ellipsoid.cartesianToCartographic(center, scratchAdjustForTerrainCart); + center = frameState.mapProjection.project(cart, scratchAdjustForTerrainCenter); + center = Cartesian3.fromElements(center.z, center.x, center.y, center); + axis = Cartesian3.fromElements(axis.z, axis.x, axis.y, scratchAdjustForTerrainAxis); } var numRays = Math.min(Math.ceil(radius * Math.abs(angle) / 1000.0), 1.0); - var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle / numRays)); - var start = Cartesian3.subtract(camera.positionWC, center); + var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle / numRays, scratchQuaternion), scratchRotation); + var start = Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart); + + var rays = scratchRayArray; + rays.length = numRays; - var rays = []; for (var i = 0; i < numRays; ++i) { - var stop = Matrix3.multiplyByVector(rotation, start); - var direction = Cartesian3.subtract(stop, start); - var origin = Cartesian3.add(center, start); - rays.push(new Ray(origin, direction)); - start = stop; + var stop = Matrix3.multiplyByVector(rotation, start, scratchAdjustForTerrainStop); + + var ray = rays[i]; + if (!defined(ray)) { + ray = rays[i] = new Ray(); + } + + var direction = Cartesian3.subtract(stop, start, ray.direction); + var origin = Cartesian3.add(center, start, ray.origin); + start = Cartesian3.clone(stop, start); } var intersection = globe.intersect(rays, frameState, adjustForTerrainCartesian3); @@ -505,9 +521,9 @@ define([ return angle; } - var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.positionWC, center)); - var endDirection = Cartesian3.normalize(Cartesian3.subtract(intersection, center)); - var newAngle = Math.acos(Cartesian3.dot(startDirection, endDirection)); + var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart), scratchAdjustForTerrainStart); + var endDirection = Cartesian3.normalize(Cartesian3.subtract(intersection, center, scratchAdjustForTerrainStop), scratchAdjustForTerrainStop); + var newAngle = CesiumMath.acosClamped(Cartesian3.dot(startDirection, endDirection)); return CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance / radius, 0.0, Math.abs(angle)); } From 2a2dbc2073908626fb18a16d0397c5a6966576d2 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 10 Jun 2014 14:23:34 -0400 Subject: [PATCH 43/74] Add minimum camera height for picking terrain and minimum height for collision detetction with terrain. --- Source/Scene/ScreenSpaceCameraController.js | 55 +++++++++++++++++---- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 553b2815ccb2..09fa2f8ad400 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -234,11 +234,17 @@ define([ modifier : KeyboardEventModifier.SHIFT }; /** - * The minimum height the camera must be before testing for intersection with the terrain instead of the ellipsoid. + * The minimum height the camera must be before picking the terrain instead of the ellipsoid. * @type {Number} * @default 150000.0 */ - this.minimumTerrainHeight = 150000.0; + this.minimumPickingTerrainHeight = 150000.0; + /** + * The minimum height the camera must be before tesing for collision with terrain. + * @type {Number} + * @default 10000.0 + */ + this.minimumCollisionTerrainHeight = 10000.0; this._camera = camera; this._canvas = canvas; @@ -440,11 +446,27 @@ define([ var adjustForTerrainCartesian3 = new Cartesian3(); function adjustForTerrain(controller, frameState, distance, direction) { - if (!defined(controller._globe)) { + var mode = frameState.mode; + if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) { + return distance; + } + + var globe = controller._globe; + if (!defined(globe)) { return distance; } var camera = controller._camera; + var height; + if (mode === SceneMode.SCENE3D) { + height = Cartesian3.magnitude(camera.positionWC) - globe.ellipsoid.maximumRadius; + } else { + height = camera.position.z; + } + + if (height > controller.minimumCollisionTerrainHeight) { + return distance; + } var ray = adjustForTerrainRay; Cartesian3.clone(camera.positionWC, ray.origin); @@ -454,11 +476,11 @@ define([ direction = Cartesian3.clone(direction, ray.direction); } - if (frameState.mode !== SceneMode.SCENE3D) { + if (mode !== SceneMode.SCENE3D) { Cartesian3.fromElements(direction.z, direction.x, direction.y, direction); } - var intersection = controller._globe.intersect(ray, frameState, adjustForTerrainCartesian3); + var intersection = globe.intersect(ray, frameState, adjustForTerrainCartesian3); if (!defined(intersection)) { return distance; } @@ -482,14 +504,29 @@ define([ var scratchRayArray = []; function adjustRotateForTerrain(controller, frameState, center, radius, axis, angle, globeOverride) { + var mode = frameState.mode; + if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) { + return angle; + } + var globe = defaultValue(globeOverride, controller._globe); if (!defined(globe)) { return angle; } var camera = controller._camera; + var height; + if (mode === SceneMode.SCENE3D) { + height = Cartesian3.magnitude(camera.positionWC) - globe.ellipsoid.maximumRadius; + } else { + height = camera.position.z; + } - if (frameState.mode !== SceneMode.SCENE3D) { + if (height > controller.minimumCollisionTerrainHeight) { + return angle; + } + + if (mode !== SceneMode.SCENE3D) { var cart = frameState.mapProjection.ellipsoid.cartesianToCartographic(center, scratchAdjustForTerrainCart); center = frameState.mapProjection.project(cart, scratchAdjustForTerrainCenter); center = Cartesian3.fromElements(center.z, center.x, center.y, center); @@ -694,7 +731,7 @@ define([ var startPlanePos; - if (defined(controller._globe) && controller._camera.position.z < controller.minimumTerrainHeight) { + if (defined(controller._globe) && controller._camera.position.z < controller.minimumPickingTerrainHeight) { startPlanePos = controller._globe.pick(startRay, frameState, translateCVStartPos); if (defined(startPlanePos)) { origin.x = startPlanePos.x; @@ -758,7 +795,7 @@ define([ center = Cartesian3.clone(controller._tiltCenter, rotateCVCenter); } else { ray = camera.getPickRay(startPosition, rotateCVWindowRay); - if (defined(controller._globe)) { + if (defined(controller._globe) && camera.position.z < controller.minimumPickingTerrainHeight) { center = controller._globe.pick(ray, frameState, rotateCVCenter); } @@ -896,7 +933,7 @@ define([ function spin3D(controller, startPosition, movement, frameState) { if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; - if (defined(controller._globe) && height < controller.minimumTerrainHeight) { + if (defined(controller._globe) && height < controller.minimumPickingTerrainHeight) { var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); if (!defined(mousePos)) { From 3ff77a7a37fa6e952f66cee33b81f935354689f2 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 10 Jun 2014 15:36:56 -0400 Subject: [PATCH 44/74] Tweak distance moved back from intersection. Use one ray. --- Source/Scene/ScreenSpaceCameraController.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 09fa2f8ad400..bf037f490b91 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -502,6 +502,7 @@ define([ var scratchAdjustForTerrainStart = new Cartesian3(); var scratchAdjustForTerrainStop = new Cartesian3(); var scratchRayArray = []; + var maxRayDistance = 10000.0; function adjustRotateForTerrain(controller, frameState, center, radius, axis, angle, globeOverride) { var mode = frameState.mode; @@ -533,7 +534,8 @@ define([ axis = Cartesian3.fromElements(axis.z, axis.x, axis.y, scratchAdjustForTerrainAxis); } - var numRays = Math.min(Math.ceil(radius * Math.abs(angle) / 1000.0), 1.0); + //var numRays = Math.min(Math.max(Math.ceil(radius * Math.abs(angle) / maxRayDistance), 1.0), 4.0); + var numRays = 1.0; var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle / numRays, scratchQuaternion), scratchRotation); var start = Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart); @@ -561,7 +563,8 @@ define([ var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart), scratchAdjustForTerrainStart); var endDirection = Cartesian3.normalize(Cartesian3.subtract(intersection, center, scratchAdjustForTerrainStop), scratchAdjustForTerrainStop); var newAngle = CesiumMath.acosClamped(Cartesian3.dot(startDirection, endDirection)); - return CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance / radius, 0.0, Math.abs(angle)); + newAngle = CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance * 10.0 / radius, 0.0, Math.abs(angle)); + return newAngle; } function handleZoom(object, startPosition, movement, frameState, zoomFactor, distanceMeasure, unitPositionDotDirection) { From 2be50be12e4ab8d5cf9240d2f32055386019def0 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 10 Jun 2014 16:54:18 -0400 Subject: [PATCH 45/74] Use a single line segment from camera position to end position after rotate for intersection testing. --- Source/Scene/Globe.js | 10 ++--- Source/Scene/GlobeSurface.js | 48 ++++++++------------- Source/Scene/ScreenSpaceCameraController.js | 30 ++++--------- Source/Scene/Tile.js | 27 +++++------- 4 files changed, 41 insertions(+), 74 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 879b94f7f91e..cd6d259741ba 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -294,7 +294,7 @@ define([ }); /** - * Find an intersection between rays or line segments and the globe surface. + * Find an intersection between a ray or a line segment and the globe surface. *

* If the rays direction has a magnitude greater than one, then the ray is assumed to be a line * segment with end points at the ray origin and the ray origin plus the ray direction. @@ -302,18 +302,18 @@ define([ * * @memberof Globe * - * @param {Ray|Ray[]} rays The rays or line segments to test for intersection. + * @param {Ray} ray The ray or line segment to test for intersection. * @param {FrameState} frameState The current frame state. * @param {Cartesian3} [result] The object onto which to store the result. - * @returns {Cartesian3|undefined} The first intersection or undefined if none were found. + * @returns {Cartesian3|undefined} The intersection or undefined if none was found. * * @example * // find intersection of ray through a pixel and the globe * var ray = scene.camera.getPickRay(windowCoordinates); * var intersection = globe.rayIntersections(ray, scene.frameState); */ - Globe.prototype.intersect = function(rays, frameState, result) { - return this._surface.intersect(rays, frameState, result); + Globe.prototype.intersect = function(ray, frameState, result) { + return this._surface.intersect(ray, frameState, result); }; /** diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index d8168e846746..47d0b2654184 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -286,11 +286,10 @@ define([ start : 0.0, stop : 0.0 }; - var scratchArray = new Array(1); var scratchRay = new Ray(); /** - * Find an intersection between rays or line segments and the globe surface. + * Find an intersection between a ray or a line segment and the globe surface. *

* If the rays direction has a magnitude greater than one, then the ray is assumed to be a line * segment with end points at the ray origin and the ray origin plus the ray direction. @@ -298,31 +297,29 @@ define([ * * @memberof Globe * - * @param {Ray|Ray[]} rays The rays or line segments to test for intersection. + * @param {Ray} ray The ray or line segment to test for intersection. * @param {FrameState} frameState The current frame state. * @param {Cartesian3} [result] The object onto which to store the result. - * @returns {Cartesian3|undefined} The first intersection or undefined if none were found. + * @returns {Cartesian3|undefined} The intersection or undefined if none was found. * * @example * // find intersection of ray through a pixel and the globe * var ray = scene.camera.getPickRay(windowCoordinates); * var intersection = globe.rayIntersections(ray, scene.frameState); */ - GlobeSurface.prototype.intersect = function(rays, frameState, result) { - if (!isArray(rays)) { - scratchArray[0] = rays; - rays = scratchArray; - } - + GlobeSurface.prototype.intersect = function(ray, frameState, result) { var stack = scratchArray1; stack.length = 0; var sphereIntersections = scratchArray2; sphereIntersections.length = 0; - var raysLength = rays.length; var tile; var i; + var tempRay = scratchRay; + Cartesian3.clone(ray.origin, tempRay.origin); + Cartesian3.normalize(ray.direction, tempRay.direction); + var levelZeroTiles = this._levelZeroTiles; var length = levelZeroTiles.length; for (i = 0; i < length; ++i) { @@ -348,19 +345,12 @@ define([ BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); } - for (i = 0; i < raysLength; ++i) { - var ray = scratchRay; - Cartesian3.clone(rays[i].origin, ray.origin); - Cartesian3.normalize(rays[i].direction, ray.direction); - - var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); - if (defined(boundingSphereIntersection)) { - var children = tile.children; - var childrenLength = children.length; - for (i = 0; i < childrenLength; ++i) { - stack.push(children[i]); - } - break; + var boundingSphereIntersection = IntersectionTests.raySphere(tempRay, boundingVolume, scratchSphereIntersectionResult); + if (defined(boundingSphereIntersection)) { + var children = tile.children; + var childrenLength = children.length; + for (i = 0; i < childrenLength; ++i) { + stack.push(children[i]); } } } @@ -370,7 +360,7 @@ define([ return undefined; } - sphereIntersections.sort(createComparePickTileFunction(rays[0].origin)); + sphereIntersections.sort(createComparePickTileFunction(ray.origin)); var currentTile = sphereIntersections[0]; var uniqueIntersections = scratchArray1; @@ -388,7 +378,7 @@ define([ var intersection; length = uniqueIntersections.length; for (i = 0; i < length; ++i) { - intersection = uniqueIntersections[i].pick(rays, frameState, result); + intersection = uniqueIntersections[i].pick(ray, frameState, result); if (defined(intersection)) { break; } @@ -450,14 +440,10 @@ define([ sphereIntersections.sort(createComparePickTileFunction(ray.origin)); - var rays = scratchArray2; - rays.length = 1; - rays[0] = ray; - var intersection; length = sphereIntersections.length; for (i = 0; i < length; ++i) { - intersection = sphereIntersections[i].pick(rays, frameState, result); + intersection = sphereIntersections[i].pick(ray, frameState, result); if (defined(intersection)) { break; } diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index bf037f490b91..5fe17e400c28 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -501,8 +501,7 @@ define([ var scratchRotation = new Matrix3(); var scratchAdjustForTerrainStart = new Cartesian3(); var scratchAdjustForTerrainStop = new Cartesian3(); - var scratchRayArray = []; - var maxRayDistance = 10000.0; + var scratchRay = new Ray(); function adjustRotateForTerrain(controller, frameState, center, radius, axis, angle, globeOverride) { var mode = frameState.mode; @@ -534,28 +533,15 @@ define([ axis = Cartesian3.fromElements(axis.z, axis.x, axis.y, scratchAdjustForTerrainAxis); } - //var numRays = Math.min(Math.max(Math.ceil(radius * Math.abs(angle) / maxRayDistance), 1.0), 4.0); - var numRays = 1.0; - var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle / numRays, scratchQuaternion), scratchRotation); + var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle, scratchQuaternion), scratchRotation); var start = Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart); + var stop = Matrix3.multiplyByVector(rotation, start, scratchAdjustForTerrainStop); - var rays = scratchRayArray; - rays.length = numRays; + var ray = scratchRay; + Cartesian3.subtract(stop, start, ray.direction); + Cartesian3.add(center, start, ray.origin); - for (var i = 0; i < numRays; ++i) { - var stop = Matrix3.multiplyByVector(rotation, start, scratchAdjustForTerrainStop); - - var ray = rays[i]; - if (!defined(ray)) { - ray = rays[i] = new Ray(); - } - - var direction = Cartesian3.subtract(stop, start, ray.direction); - var origin = Cartesian3.add(center, start, ray.origin); - start = Cartesian3.clone(stop, start); - } - - var intersection = globe.intersect(rays, frameState, adjustForTerrainCartesian3); + var intersection = globe.intersect(ray, frameState, adjustForTerrainCartesian3); if (!defined(intersection)) { return angle; } @@ -563,7 +549,7 @@ define([ var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart), scratchAdjustForTerrainStart); var endDirection = Cartesian3.normalize(Cartesian3.subtract(intersection, center, scratchAdjustForTerrainStop), scratchAdjustForTerrainStop); var newAngle = CesiumMath.acosClamped(Cartesian3.dot(startDirection, endDirection)); - newAngle = CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance * 10.0 / radius, 0.0, Math.abs(angle)); + newAngle = CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance * 100.0 / radius, 0.0, Math.abs(angle)); return newAngle; } diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 94df3dfc3e56..98836d88ced1 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -331,7 +331,7 @@ define([ var scratchCartesian = new Cartesian3(); var scratchResult = new Cartesian3(); - Tile.prototype.pick = function(rays, frameState, result) { + Tile.prototype.pick = function(ray, frameState, result) { var terrain = this.pickTerrain; if (!defined(terrain)) { return undefined; @@ -346,8 +346,6 @@ define([ var indices = mesh.indices; var length = indices.length; - var raysLength = rays.length; - for (var i = 0; i < length; i += 3) { var i0 = indices[i]; var i1 = indices[i + 1]; @@ -357,20 +355,17 @@ define([ var v1 = getPosition(this, frameState, vertices, i1, scratchV1); var v2 = getPosition(this, frameState, vertices, i2, scratchV2); - for (var j = 0; j < raysLength; ++j) { - var ray = rays[j]; - var intersection; - if (Cartesian3.magnitudeSquared(ray) > 1.0) { - var p0 = ray.origin; - var p1 = Cartesian3.add(p0, ray.direction, scratchCartesian); - intersection = IntersectionTests.lineSegmentTriangle(p0, p1, v0, v1, v2, true, scratchResult); - } else { - intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, scratchResult); - } + var intersection; + if (Cartesian3.magnitude(ray.direction) > 1.0) { + var p0 = ray.origin; + var p1 = Cartesian3.add(p0, ray.direction, scratchCartesian); + intersection = IntersectionTests.lineSegmentTriangle(p0, p1, v0, v1, v2, true, scratchResult); + } else { + intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, scratchResult); + } - if (defined(intersection)) { - return Cartesian3.clone(intersection, result); - } + if (defined(intersection)) { + return Cartesian3.clone(intersection, result); } } From ebe8cd8d855f526c19058f6574c7b061a7621304 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 10 Jun 2014 17:49:44 -0400 Subject: [PATCH 46/74] Add line segment-sphere intersection to reduce the number of terrain tiles. --- Source/Core/IntersectionTests.js | 88 ++++++++++++++++++++++++-------- Source/Scene/GlobeSurface.js | 16 +++--- 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index e870d9bae5fd..7565356319d3 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -225,14 +225,6 @@ define([ var root0 = (-b + disc) * denom; var root1 = (-b - disc) * denom; - if (root0 <= 0.0 && root1 <= 0.0) { - return undefined; - } else if (root0 < 0.0) { - root0 = 0.0; - } else if (root1 < 0.0) { - root1 = 0.0; - } - if (root0 < root1) { result.root0 = root0; result.root1 = root1; @@ -258,6 +250,33 @@ define([ root1 : 0.0 }; + function raySphere(ray, sphere, result) { + if (!defined(result)) { + result = {}; + } + + var origin = ray.origin; + var direction = ray.direction; + + var center = sphere.center; + var radiusSquared = sphere.radius * sphere.radius; + + var diff = Cartesian3.subtract(origin, center, scratchPVec); + + var a = Cartesian3.dot(direction, direction); + var b = 2.0 * Cartesian3.dot(direction, diff); + var c = Cartesian3.magnitudeSquared(diff) - radiusSquared; + + var roots = solveQuadratic(a, b, c, raySphereRoots); + if (!defined(roots)) { + return undefined; + } + + result.start = roots.root0; + result.stop = roots.root1; + return result; + } + /** * Computes the intersection points of a ray with a sphere. * @memberof IntersectionTests @@ -277,29 +296,54 @@ define([ } //>>includeEnd('debug'); - if (!defined(result)) { - result = {}; + result = raySphere(ray, sphere, result); + if (!defined(result) || result.stop < 0.0) { + return undefined; } - var origin = ray.origin; - var direction = ray.direction; + result.start = Math.max(result.start, 0.0); + return result; + }; - var center = sphere.center; - var radiusSquared = sphere.radius * sphere.radius; + var scratchLineSegmentRay = new Ray(); - var diff = Cartesian3.subtract(origin, center, scratchPVec); + /** + * Computes the intersection points of a line segment with a sphere. + * @memberof IntersectionTests + * + * @param {Cartesian3} p0 An end point of the line segment. + * @param {Cartesian3} p1 The other end point of the line segment. + * @param {BoundingSphere} sphere The sphere. + * @param {Object} [result] The result onto which to store the result. + * @returns {Object} An object with the first (start) and the second (stop) intersection scalars for points along the line segment or undefined if there are no intersections. + */ + IntersectionTests.lineSegmentSphere = function(p0, p1, sphere, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(p0)) { + throw new DeveloperError('p0 is required.'); + } + if (!defined(p1)) { + throw new DeveloperError('p1 is required.'); + } + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + //>>includeEnd('debug'); - var a = Cartesian3.dot(direction, direction); - var b = 2.0 * Cartesian3.dot(direction, diff); - var c = Cartesian3.magnitudeSquared(diff) - radiusSquared; + var ray = scratchLineSegmentRay; + var origin = Cartesian3.clone(p0, ray.origin); + var direction = Cartesian3.subtract(p1, p0, ray.direction); - var roots = solveQuadratic(a, b, c, raySphereRoots); - if (!defined(roots)) { + var maxT = Cartesian3.magnitude(direction); + Cartesian3.normalize(direction, direction); + + result = raySphere(ray, sphere, result); + if (!defined(result) || result.stop < 0.0 || result.start > maxT) { return undefined; } - result.start = roots.root0; - result.stop = roots.root1; + result.start = Math.max(result.start, 0.0); + result.stop = Math.min(result.stop, maxT); return result; }; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 47d0b2654184..4278f272f7be 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -286,7 +286,7 @@ define([ start : 0.0, stop : 0.0 }; - var scratchRay = new Ray(); + var scratchCartesian = new Cartesian3(); /** * Find an intersection between a ray or a line segment and the globe surface. @@ -316,10 +316,6 @@ define([ var tile; var i; - var tempRay = scratchRay; - Cartesian3.clone(ray.origin, tempRay.origin); - Cartesian3.normalize(ray.direction, tempRay.direction); - var levelZeroTiles = this._levelZeroTiles; var length = levelZeroTiles.length; for (i = 0; i < length; ++i) { @@ -345,7 +341,15 @@ define([ BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); } - var boundingSphereIntersection = IntersectionTests.raySphere(tempRay, boundingVolume, scratchSphereIntersectionResult); + var boundingSphereIntersection; + if (Cartesian3.magnitude(ray.direction) > 1.0) { + var p0 = ray.origin; + var p1 = Cartesian3.add(p0, ray.direction, scratchCartesian); + boundingSphereIntersection = IntersectionTests.lineSegmentSphere(p0, p1, boundingVolume, scratchSphereIntersectionResult); + } else { + boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); + } + if (defined(boundingSphereIntersection)) { var children = tile.children; var childrenLength = children.length; From 10916d5b11821eac3afa28c738419870a40f0bb5 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 10 Jun 2014 20:03:42 -0400 Subject: [PATCH 47/74] Add a fast vector rotation by a quaternion. --- Source/Core/Quaternion.js | 32 +++++++++++++++++++++ Source/Scene/ScreenSpaceCameraController.js | 5 ++-- Specs/Core/QuaternionSpec.js | 29 +++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/Source/Core/Quaternion.js b/Source/Core/Quaternion.js index 3f1a4d730031..7e3cb5c3818f 100644 --- a/Source/Core/Quaternion.js +++ b/Source/Core/Quaternion.js @@ -99,6 +99,38 @@ define([ return result; }; + var scratchRotateCross = new Cartesian3(); + + /** + * Apply a quaternion rotation to a vector. + * @memberof Quaternion + * + * @param {Quaternion} quaternion The rotation. + * @param {Cartesian3} cartesian The cartesian to rotate. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + */ + Quaternion.rotateVector = function(quaternion, cartesian, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(quaternion)) { + throw new DeveloperError('quaternion is required.'); + } + if (!defined(cartesian)) { + throw new DeveloperError('quaternion is required.'); + } + //>>includeEnd('debug'); + + // t = 2.0 * cross(quaternion.xyz, cartesian) + var t = Cartesian3.cross(quaternion, cartesian, scratchRotateCross); + Cartesian3.multiplyByScalar(t, 2.0, t); + + // return cartesian + quaternion.w * t + cross(quaternion.xyz, t) + result = Cartesian3.cross(quaternion, t, result); + Cartesian3.multiplyByScalar(t, quaternion.w, t); + Cartesian3.add(t, result, result); + return Cartesian3.add(result, cartesian, result); + }; + var fromRotationMatrixNext = [1, 2, 0]; var fromRotationMatrixQuat = new Array(3); /** diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 5fe17e400c28..b4b481a312b6 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -533,13 +533,12 @@ define([ axis = Cartesian3.fromElements(axis.z, axis.x, axis.y, scratchAdjustForTerrainAxis); } - var rotation = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(axis, angle, scratchQuaternion), scratchRotation); var start = Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart); - var stop = Matrix3.multiplyByVector(rotation, start, scratchAdjustForTerrainStop); + var stop = Quaternion.rotateVector(Quaternion.fromAxisAngle(axis, angle, scratchQuaternion), start, scratchAdjustForTerrainStop); var ray = scratchRay; + Cartesian3.clone(camera.positionWC, ray.origin); Cartesian3.subtract(stop, start, ray.direction); - Cartesian3.add(center, start, ray.origin); var intersection = globe.intersect(ray, frameState, adjustForTerrainCartesian3); if (!defined(intersection)) { diff --git a/Specs/Core/QuaternionSpec.js b/Specs/Core/QuaternionSpec.js index c379b7c6a608..3d95c4ead6ba 100644 --- a/Specs/Core/QuaternionSpec.js +++ b/Specs/Core/QuaternionSpec.js @@ -54,6 +54,23 @@ defineSuite([ expect(returnedResult).toEqual(expected); }); + it('rotateVector works without a result parameter', function() { + var axis = Cartesian3.UNIT_Z; + var angle = CesiumMath.PI_OVER_TWO; + var rotation = Quaternion.fromAxisAngle(axis, angle); + expect(Quaternion.rotateVector(rotation, Cartesian3.UNIT_X)).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON14); + }); + + it('rotateVector works with a result parameter', function() { + var axis = Cartesian3.UNIT_Z; + var angle = CesiumMath.PI_OVER_TWO; + var rotation = Quaternion.fromAxisAngle(axis, angle); + var result = new Cartesian3(); + var actual = Quaternion.rotateVector(rotation, Cartesian3.UNIT_X, result); + expect(actual).toBe(result); + expect(actual).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON14); + }); + it('fromRotationMatrix works when m22 is max', function() { var q = Quaternion.fromAxisAngle(Cartesian3.negate(Cartesian3.UNIT_Z), Math.PI); var rotation = new Matrix3(-1.0, 0.0, 0.0, @@ -779,6 +796,18 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('rotateVector throws without quaternion', function() { + expect(function() { + Quaternion.rotateVector(undefined, new Cartesian3()); + }).toThrowDeveloperError(); + }); + + it('rotateVector throws without cartesian', function() { + expect(function() { + Quaternion.rotateVector(new Quaternion(), undefined); + }).toThrowDeveloperError(); + }); + it('fromRotationMatrix throws with undefined matrix', function() { expect(function() { Quaternion.fromRotationMatrix(undefined); From d495ba77dd87d9d4e1060df06806db28f51861be Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 10 Jun 2014 20:10:59 -0400 Subject: [PATCH 48/74] Fix Sandcastle example after merge. --- Apps/Sandcastle/gallery/Camera.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Apps/Sandcastle/gallery/Camera.html b/Apps/Sandcastle/gallery/Camera.html index 8fe6357b5d2f..f4d70e74d519 100644 --- a/Apps/Sandcastle/gallery/Camera.html +++ b/Apps/Sandcastle/gallery/Camera.html @@ -32,12 +32,11 @@ var clock = viewer.clock; function reset() { - var ellipsoid = scene.globe.ellipsoid; scene.primitives.removeAll(); scene.animations.removeAll(); var controller = scene.screenSpaceCameraController; - controller.ellipsoid = ellipsoid; + controller.globe = scene.globe; controller.enableTilt = true; scene.camera.setTransform(Cesium.Matrix4.IDENTITY); From 4c4dda969e83786645c39bd9e70739345b3bea3e Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 10 Jun 2014 20:32:39 -0400 Subject: [PATCH 49/74] Update CHANGES.md. --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 340aef601ae5..dcbf6e4d6970 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -27,6 +27,7 @@ Beta Releases * CZML property references now use a `#` symbol to separate identifier from property path. `objectId.position` should now be `objectId#position`. * `CesiumWidget.showErrorPanel` now takes a `message` parameter in between the previous `title` and `error` parameters. * `Event.removeEventListener` no longer throws `DeveloperError` if the `listener` does not exist; it now returns `false`. + * Renamed `ScreenSpaceCameraController.ellipsoid` to `ScreenSpaceCameraController.globe` whose type can now be `Globe` or `Ellipsoid`. * `DynamicObject.id` can now include period characters. * `ReferenceProperty` can now handle sub-properties, for example, `myObject#billboard.scale`. * Added `Cesium.VERSION` to the combined `Cesium.js` file. @@ -40,6 +41,12 @@ Beta Releases * `Viewer` and `CesiumWidget` now take a new optional parameter, `creditContainer`. * Added `PerformanceWatchdog` widget and `viewerPerformanceWatchdogMixin`. * Fixed a problem that could rarely lead to the camera's `tilt` property being `NaN`. +* Added the following methods to `IntersectionTests`: `rayTriangle`, `lineSegmentTriangle`, `raySphere`, and `lineSegmentSphere`. +* Added `Quaternion.rotateVector` for fast application of a quaternion rotation to a vector. +* Fixed camera rotation near the camera's constrained axis. +* Added `Globe.intersect` and `Globe.pick` for finding intersections of a ray or line segment with the globe. `Globe.pick` only performs a ray intersection test with the part of the surface that was rendered. +* Modified the default camera tilt mouse behavior to tilt about the point clicked. +* Added camera collision detection to the default mouse interaction. * Updated third-party [Tween.js](https://github.com/sole/tween.js/) from r7 to r13. ### b29 - 2014-06-02 From eddce950ba17f87ed165034580e495fae72cb741 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 12 Jun 2014 16:55:35 -0400 Subject: [PATCH 50/74] Fix tests after merge. --- Source/Core/IntersectionTests.js | 12 ++++++++-- Source/Core/Quaternion.js | 6 ++++- Specs/Core/IntersectionTestsSpec.js | 34 ++++++++++++++--------------- Specs/Scene/CameraSpec.js | 8 +++---- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index d291bf0f9a8e..77d109b1b8e6 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -175,7 +175,11 @@ define([ return undefined; } - result = Cartesian3.multiplyByScalar(ray.direction, t, result); + if (!defined(result)) { + result = new Cartesian3(); + } + + Cartesian3.multiplyByScalar(ray.direction, t, result); return Cartesian3.add(ray.origin, result, result); }; @@ -215,7 +219,11 @@ define([ return undefined; } - result = Cartesian3.multiplyByScalar(ray.direction, t, result); + if (!defined(result)) { + result = new Cartesian3(); + } + + Cartesian3.multiplyByScalar(ray.direction, t, result); return Cartesian3.add(ray.origin, result, result); }; diff --git a/Source/Core/Quaternion.js b/Source/Core/Quaternion.js index c460a8be57e6..3b0a0b3e3cf1 100644 --- a/Source/Core/Quaternion.js +++ b/Source/Core/Quaternion.js @@ -120,12 +120,16 @@ define([ } //>>includeEnd('debug'); + if (!defined(result)) { + result = new Cartesian3(); + } + // t = 2.0 * cross(quaternion.xyz, cartesian) var t = Cartesian3.cross(quaternion, cartesian, scratchRotateCross); Cartesian3.multiplyByScalar(t, 2.0, t); // return cartesian + quaternion.w * t + cross(quaternion.xyz, t) - result = Cartesian3.cross(quaternion, t, result); + Cartesian3.cross(quaternion, t, result); Cartesian3.multiplyByScalar(t, quaternion.w, t); Cartesian3.add(t, result, result); return Cartesian3.add(result, cartesian, result); diff --git a/Specs/Core/IntersectionTestsSpec.js b/Specs/Core/IntersectionTestsSpec.js index 70e575d05548..17d30a936e1f 100644 --- a/Specs/Core/IntersectionTestsSpec.js +++ b/Specs/Core/IntersectionTestsSpec.js @@ -86,7 +86,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var ray = new Ray(Cartesian3.UNIT_Z, Cartesian3.negate(Cartesian3.UNIT_Z)); + var ray = new Ray(Cartesian3.UNIT_Z, Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3())); var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); expect(intersection).toEqual(Cartesian3.ZERO); @@ -97,7 +97,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var ray = new Ray(Cartesian3.negate(Cartesian3.UNIT_Z), Cartesian3.UNIT_Z); + var ray = new Ray(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), Cartesian3.UNIT_Z); var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); expect(intersection).toEqual(Cartesian3.ZERO); @@ -108,7 +108,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var ray = new Ray(Cartesian3.negate(Cartesian3.UNIT_Z), Cartesian3.UNIT_Z); + var ray = new Ray(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), Cartesian3.UNIT_Z); var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2, true); expect(intersection).not.toBeDefined(); @@ -119,7 +119,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var ray = new Ray(new Cartesian3(0.0, -1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z)); + var ray = new Ray(new Cartesian3(0.0, -1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3())); var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -130,7 +130,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var ray = new Ray(new Cartesian3(1.0, 1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z)); + var ray = new Ray(new Cartesian3(1.0, 1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3())); var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -141,7 +141,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var ray = new Ray(new Cartesian3(-1.0, 1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z)); + var ray = new Ray(new Cartesian3(-1.0, 1.0, 1.0), Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3())); var intersection = IntersectionTests.rayTriangle(ray, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -205,7 +205,7 @@ defineSuite([ var p2 = new Cartesian3(0.0, 1.0, 0.0); var v0 = Cartesian3.UNIT_Z; - var v1 = Cartesian3.negate(Cartesian3.UNIT_Z); + var v1 = Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()); var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); expect(intersection).toEqual(Cartesian3.ZERO); @@ -216,7 +216,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var v0 = Cartesian3.negate(Cartesian3.UNIT_Z); + var v0 = Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()); var v1 = Cartesian3.UNIT_Z; var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); @@ -228,7 +228,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var v0 = Cartesian3.negate(Cartesian3.UNIT_Z); + var v0 = Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()); var v1 = Cartesian3.UNIT_Z; var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2, true); @@ -241,7 +241,7 @@ defineSuite([ var p2 = new Cartesian3(0.0, 1.0, 0.0); var v0 = new Cartesian3(0.0, -1.0, 1.0); - var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z)); + var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), new Cartesian3()); var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -253,7 +253,7 @@ defineSuite([ var p2 = new Cartesian3(0.0, 1.0, 0.0); var v0 = new Cartesian3(1.0, 1.0, 1.0); - var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z)); + var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), new Cartesian3()); var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -265,7 +265,7 @@ defineSuite([ var p2 = new Cartesian3(0.0, 1.0, 0.0); var v0 = new Cartesian3(-1.0, 1.0, 1.0); - var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z)); + var v1 = Cartesian3.add(v0, Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), new Cartesian3()); var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -277,7 +277,7 @@ defineSuite([ var p2 = new Cartesian3(0.0, 1.0, 0.0); var v0 = new Cartesian3(-1.0, 0.0, 1.0); - var v1 = Cartesian3.add(v0, Cartesian3.UNIT_X); + var v1 = Cartesian3.add(v0, Cartesian3.UNIT_X, new Cartesian3()); var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -289,7 +289,7 @@ defineSuite([ var p2 = new Cartesian3(0.0, 1.0, 0.0); var v0 = Cartesian3.UNIT_Z; - var v1 = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Z, 2.0); + var v1 = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Z, 2.0, new Cartesian3()); var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); expect(intersection).not.toBeDefined(); @@ -300,7 +300,7 @@ defineSuite([ var p1 = new Cartesian3(1.0, 0.0, 0.0); var p2 = new Cartesian3(0.0, 1.0, 0.0); - var v0 = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Z, 2.0); + var v0 = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Z, 2.0, new Cartesian3()); var v1 = Cartesian3.UNIT_Z; var intersection = IntersectionTests.lineSegmentTriangle(v0, v1, p0, p1, p2); @@ -377,7 +377,7 @@ defineSuite([ var sphere = new BoundingSphere(Cartesian3.ZERO, 5000.0); var origin = new Cartesian3(200.0, 0.0, 0.0); - var direction = Cartesian3.negate(Cartesian3.normalize(origin)); + var direction = Cartesian3.negate(Cartesian3.normalize(origin, new Cartesian3()), new Cartesian3()); var ray = new Ray(origin, direction); var expected = { @@ -395,7 +395,7 @@ defineSuite([ var sphere = new BoundingSphere(Cartesian3.ZERO, 5000.0); var origin = new Cartesian3(200.0, 0.0, 0.0); - var direction = Cartesian3.normalize(origin); + var direction = Cartesian3.normalize(origin, new Cartesian3()); var ray = new Ray(origin, direction); var expected = { diff --git a/Specs/Scene/CameraSpec.js b/Specs/Scene/CameraSpec.js index 45714a37740f..95cf5c726f8d 100644 --- a/Specs/Scene/CameraSpec.js +++ b/Specs/Scene/CameraSpec.js @@ -631,10 +631,10 @@ defineSuite([ it('rotate past constrained axis stops at constained axis', function() { camera.constrainedAxis = Cartesian3.UNIT_Y; camera.rotateUp(Math.PI); - expect(camera.up).toEqualEpsilon(Cartesian3.negate(dir, new Cartesian3()), CesiumMath.EPSILON14); - expect(camera.direction).toEqualEpsilon(up, CesiumMath.EPSILON14); - expect(camera.right).toEqualEpsilon(right, CesiumMath.EPSILON14); - expect(camera.position).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Y, new Cartesian3()), CesiumMath.EPSILON14); + expect(camera.up).toEqualEpsilon(Cartesian3.negate(dir, new Cartesian3()), CesiumMath.EPSILON4); + expect(camera.direction).toEqualEpsilon(up, CesiumMath.EPSILON4); + expect(camera.right).toEqualEpsilon(right, CesiumMath.EPSILON4); + expect(camera.position).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Y, new Cartesian3()), CesiumMath.EPSILON4); }); it('zooms out 2D', function() { From a19f47cf7bdb6025fdcb0e4a6d7de518038e96e1 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 12 Jun 2014 19:18:47 -0400 Subject: [PATCH 51/74] Use a new method for camera collision response. --- Source/Scene/Globe.js | 38 ++-- Source/Scene/GlobeSurface.js | 184 ++++++++------------ Source/Scene/ScreenSpaceCameraController.js | 180 ++++++------------- Source/Scene/Tile.js | 14 +- 4 files changed, 141 insertions(+), 275 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 9c11eeb76977..512b59668e3d 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -293,36 +293,8 @@ define([ } }); - /** - * Find an intersection between a ray or a line segment and the globe surface. - *

- * If the rays direction has a magnitude greater than one, then the ray is assumed to be a line - * segment with end points at the ray origin and the ray origin plus the ray direction. - *

- * - * @memberof Globe - * - * @param {Ray} ray The ray or line segment to test for intersection. - * @param {FrameState} frameState The current frame state. - * @param {Cartesian3} [result] The object onto which to store the result. - * @returns {Cartesian3|undefined} The intersection or undefined if none was found. - * - * @example - * // find intersection of ray through a pixel and the globe - * var ray = scene.camera.getPickRay(windowCoordinates); - * var intersection = globe.rayIntersections(ray, scene.frameState); - */ - Globe.prototype.intersect = function(ray, frameState, result) { - return this._surface.intersect(ray, frameState, result); - }; - /** * Find an intersection between a ray and the globe surface that was rendered. - *

- * Prefer this method over {@link Globe#intersect} when finding an intersection with the section of - * the globe that is rendered for better performance - *

- * @memberof Globe * * @param {Ray} ray The ray to test for intersection. * @param {FrameState} frameState The current frame state. @@ -338,6 +310,16 @@ define([ return this._surface.pick(ray, frameState, result); }; + /** + * Get the height of the surface at a given cartographic. + * + * @param {Cartographic} cartographic The cartographic for which to find the height. + * @returns {Number|undefined} The height of the cartographic or undefined if it could not be found. + */ + Globe.prototype.getHeight = function(cartographic) { + return this._surface.getHeight(cartographic); + }; + var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; var scratchCartesian1 = new Cartesian3(); var scratchCartesian2 = new Cartesian3(); diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index a91475a64270..7532513ffd7c 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -280,116 +280,11 @@ define([ }; } - var scratchArray1 = []; - var scratchArray2 = []; + var scratchArray = []; var scratchSphereIntersectionResult = { start : 0.0, stop : 0.0 }; - var scratchCartesian = new Cartesian3(); - - /** - * Find an intersection between a ray or a line segment and the globe surface. - *

- * If the rays direction has a magnitude greater than one, then the ray is assumed to be a line - * segment with end points at the ray origin and the ray origin plus the ray direction. - *

- * - * @memberof Globe - * - * @param {Ray} ray The ray or line segment to test for intersection. - * @param {FrameState} frameState The current frame state. - * @param {Cartesian3} [result] The object onto which to store the result. - * @returns {Cartesian3|undefined} The intersection or undefined if none was found. - * - * @example - * // find intersection of ray through a pixel and the globe - * var ray = scene.camera.getPickRay(windowCoordinates); - * var intersection = globe.rayIntersections(ray, scene.frameState); - */ - GlobeSurface.prototype.intersect = function(ray, frameState, result) { - var stack = scratchArray1; - stack.length = 0; - var sphereIntersections = scratchArray2; - sphereIntersections.length = 0; - - var tile; - var i; - - var levelZeroTiles = this._levelZeroTiles; - var length = levelZeroTiles.length; - for (i = 0; i < length; ++i) { - stack.push(levelZeroTiles[i]); - } - - while (stack.length > 0) { - tile = stack.pop(); - if (tile.state < TileState.READY) { - while (!defined(tile.pickTerrain) && defined(parent)) { - tile = tile.parent; - } - - sphereIntersections.push(tile); - continue; - } - - var boundingVolume = tile.pickBoundingSphere; - if (frameState.mode !== SceneMode.SCENE3D) { - BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, tile.minimumHeight, tile.maximumHeight, boundingVolume); - Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); - } else { - BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); - } - - var boundingSphereIntersection; - if (Cartesian3.magnitude(ray.direction) > 1.0) { - var p0 = ray.origin; - var p1 = Cartesian3.add(p0, ray.direction, scratchCartesian); - boundingSphereIntersection = IntersectionTests.lineSegmentSphere(p0, p1, boundingVolume, scratchSphereIntersectionResult); - } else { - boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); - } - - if (defined(boundingSphereIntersection)) { - var children = tile.children; - var childrenLength = children.length; - for (i = 0; i < childrenLength; ++i) { - stack.push(children[i]); - } - } - } - - length = sphereIntersections.length; - if (length === 0) { - return undefined; - } - - sphereIntersections.sort(createComparePickTileFunction(ray.origin)); - - var currentTile = sphereIntersections[0]; - var uniqueIntersections = scratchArray1; - uniqueIntersections.length = 0; - uniqueIntersections[0] = currentTile; - - for (i = 1; i < length; ++i) { - tile = sphereIntersections[i]; - if (tile !== currentTile) { - uniqueIntersections.push(tile); - currentTile = tile; - } - } - - var intersection; - length = uniqueIntersections.length; - for (i = 0; i < length; ++i) { - intersection = uniqueIntersections[i].pick(ray, frameState, result); - if (defined(intersection)) { - break; - } - } - - return intersection; - }; /** * Find an intersection between a ray and the globe surface that was rendered. @@ -410,7 +305,16 @@ define([ * var intersection = surface.pick(ray, scene.frameState); */ GlobeSurface.prototype.pick = function(ray, frameState, result) { - var sphereIntersections = scratchArray1; + //>>includeStart('debug', pragmas.debug); + if (!defined(ray)) { + throw new DeveloperError('ray is required'); + } + if (!defined(frameState)) { + throw new DeveloperError('frameState is required'); + } + //>>includeEnd('debug'); + + var sphereIntersections = scratchArray; sphereIntersections.length = 0; var tilesToRenderByTextureCount = this._tilesToRenderByTextureCount; @@ -447,7 +351,7 @@ define([ var intersection; length = sphereIntersections.length; for (i = 0; i < length; ++i) { - intersection = sphereIntersections[i].pick(ray, frameState, result); + intersection = sphereIntersections[i].pick(ray, frameState, true, result); if (defined(intersection)) { break; } @@ -456,6 +360,70 @@ define([ return intersection; }; + var scratchGetHeightCartesian = new Cartesian3(); + var scratchGetHeightIntersection = new Cartesian3(); + var scratchGetHeightCartographic = new Cartographic(); + var scratchGetHeightRay = new Ray(); + + /** + * Get the height of the surface at a given cartographic. + * + * @param {Cartographic} cartographic The cartographic for which to find the height. + * @returns {Number|undefined} The height of the cartographic or undefined if it could not be found. + */ + GlobeSurface.prototype.getHeight = function(cartographic) { + //>>includeStart('debug', pragmas.debug); + if (!defined(cartographic)) { + throw new DeveloperError('cartographic is required'); + } + //>>includeEnd('debug'); + + var tile; + var i; + + var levelZeroTiles = this._levelZeroTiles; + var length = levelZeroTiles.length; + for (i = 0; i < length; ++i) { + tile = levelZeroTiles[i]; + if (Rectangle.contains(tile.rectangle, cartographic)) { + break; + } + } + + if (!defined(tile) || !Rectangle.contains(tile.rectangle, cartographic)) { + return undefined; + } + + while(tile.state >= TileState.READY) { + var children = tile.children; + length = children.length; + + for (i = 0; i < length; ++i) { + tile = children[i]; + if (Rectangle.contains(tile.rectangle, cartographic)) { + break; + } + } + } + + while (!defined(tile.pickTerrain) && defined(tile)) { + tile = tile.parent; + } + + var ellipsoid = this._terrainProvider.tilingScheme.ellipsoid; + var cartesian = ellipsoid.cartographicToCartesian(cartographic, scratchGetHeightCartesian); + + var ray = scratchGetHeightRay; + Cartesian3.normalize(cartesian, ray.direction); + + var intersection = tile.pick(ray, undefined, false, scratchGetHeightIntersection); + if (!defined(intersection)) { + return undefined; + } + + return ellipsoid.cartesianToCartographic(intersection, scratchGetHeightCartographic).height; + }; + /** * Returns true if this object was destroyed; otherwise, false. *

diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 1663b03522fd..1d151c61420f 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -442,116 +442,6 @@ define([ } } - var adjustForTerrainRay = new Ray(); - var adjustForTerrainCartesian3 = new Cartesian3(); - - function adjustForTerrain(controller, frameState, distance, direction) { - var mode = frameState.mode; - if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) { - return distance; - } - - var globe = controller._globe; - if (!defined(globe)) { - return distance; - } - - var camera = controller._camera; - var height; - if (mode === SceneMode.SCENE3D) { - height = Cartesian3.magnitude(camera.positionWC) - globe.ellipsoid.maximumRadius; - } else { - height = camera.position.z; - } - - if (height > controller.minimumCollisionTerrainHeight) { - return distance; - } - - var ray = adjustForTerrainRay; - Cartesian3.clone(camera.positionWC, ray.origin); - if (distance < 0.0) { - direction = Cartesian3.negate(direction, ray.direction); - } else { - direction = Cartesian3.clone(direction, ray.direction); - } - - if (mode !== SceneMode.SCENE3D) { - Cartesian3.fromElements(direction.z, direction.x, direction.y, direction); - } - - var intersection = globe.intersect(ray, frameState, adjustForTerrainCartesian3); - if (!defined(intersection)) { - return distance; - } - - var dist = Cartesian3.distance(ray.origin, intersection) - controller.minimumZoomDistance; - if (dist <= 0.0 || distance === 0.0) { - return 0.0; - } - - var ratio = CesiumMath.clamp(dist / Math.abs(distance), 0.0, 1.0); - return distance * ratio; - } - - var scratchAdjustForTerrainCart = new Cartographic(); - var scratchAdjustForTerrainCenter = new Cartesian3(); - var scratchAdjustForTerrainAxis = new Cartesian3(); - var scratchQuaternion = new Quaternion(); - var scratchRotation = new Matrix3(); - var scratchAdjustForTerrainStart = new Cartesian3(); - var scratchAdjustForTerrainStop = new Cartesian3(); - var scratchRay = new Ray(); - - function adjustRotateForTerrain(controller, frameState, center, radius, axis, angle, globeOverride) { - var mode = frameState.mode; - if (mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) { - return angle; - } - - var globe = defaultValue(globeOverride, controller._globe); - if (!defined(globe)) { - return angle; - } - - var camera = controller._camera; - var height; - if (mode === SceneMode.SCENE3D) { - height = Cartesian3.magnitude(camera.positionWC) - globe.ellipsoid.maximumRadius; - } else { - height = camera.position.z; - } - - if (height > controller.minimumCollisionTerrainHeight) { - return angle; - } - - if (mode !== SceneMode.SCENE3D) { - var cart = frameState.mapProjection.ellipsoid.cartesianToCartographic(center, scratchAdjustForTerrainCart); - center = frameState.mapProjection.project(cart, scratchAdjustForTerrainCenter); - center = Cartesian3.fromElements(center.z, center.x, center.y, center); - axis = Cartesian3.fromElements(axis.z, axis.x, axis.y, scratchAdjustForTerrainAxis); - } - - var start = Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart); - var stop = Quaternion.rotateVector(Quaternion.fromAxisAngle(axis, angle, scratchQuaternion), start, scratchAdjustForTerrainStop); - - var ray = scratchRay; - Cartesian3.clone(camera.positionWC, ray.origin); - Cartesian3.subtract(stop, start, ray.direction); - - var intersection = globe.intersect(ray, frameState, adjustForTerrainCartesian3); - if (!defined(intersection)) { - return angle; - } - - var startDirection = Cartesian3.normalize(Cartesian3.subtract(camera.positionWC, center, scratchAdjustForTerrainStart), scratchAdjustForTerrainStart); - var endDirection = Cartesian3.normalize(Cartesian3.subtract(intersection, center, scratchAdjustForTerrainStop), scratchAdjustForTerrainStop); - var newAngle = CesiumMath.acosClamped(Cartesian3.dot(startDirection, endDirection)); - newAngle = CesiumMath.sign(angle) * CesiumMath.clamp(newAngle - controller.minimumZoomDistance * 100.0 / radius, 0.0, Math.abs(angle)); - return newAngle; - } - function handleZoom(object, startPosition, movement, frameState, zoomFactor, distanceMeasure, unitPositionDotDirection) { var percentage = 1.0; if (defined(unitPositionDotDirection)) { @@ -586,7 +476,6 @@ define([ distance = distanceMeasure - maxHeight; } - distance = adjustForTerrain(object, frameState, distance, object._camera.direction); object._camera.zoomIn(distance); } @@ -750,8 +639,6 @@ define([ var mag = Cartesian3.magnitude(diff); if (mag > CesiumMath.EPSILON6) { Cartesian3.normalize(diff, diff); - - mag = adjustForTerrain(controller, frameState, mag, diff); camera.move(diff, mag); } } @@ -835,12 +722,11 @@ define([ var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, ellipsoid, rotateCVVerticalTransform); - var globeOverride = controller._globe; var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; var constrainedAxis = Cartesian3.UNIT_Z; - rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, undefined, false, true, globeOverride); + rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, undefined, false, true); var tangent = Cartesian3.cross(Cartesian3.UNIT_Z, Cartesian3.normalize(camera.position, rotateCVCartesian3), rotateCVCartesian3); if (Cartesian3.dot(camera.right, tangent) < 0.0) { @@ -852,11 +738,11 @@ define([ var oldConstrainedAxis = camera.constrainedAxis; camera.constrainedAxis = undefined; - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false, globeOverride); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); camera.constrainedAxis = oldConstrainedAxis; } else { - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false, globeOverride); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); } controller.globe = oldGlobe; @@ -946,7 +832,7 @@ define([ var rotate3DNegateScratch = new Cartesian3(); var rotate3DInverseMatrixScratch = new Matrix4(); - function rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, rotateOnlyVertical, rotateOnlyHorizontal, globeOverride) { + function rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, rotateOnlyVertical, rotateOnlyHorizontal) { rotateOnlyVertical = defaultValue(rotateOnlyVertical, false); rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, false); @@ -979,12 +865,10 @@ define([ var radius = Cartesian3.distance(camera.position, center); if (!rotateOnlyVertical) { - deltaPhi = adjustRotateForTerrain(controller, frameState, center, radius, camera.up, deltaPhi, globeOverride); camera.rotateRight(deltaPhi, transform); } if (!rotateOnlyHorizontal) { - deltaTheta = adjustRotateForTerrain(controller, frameState, center, radius, camera.right, deltaTheta, globeOverride); camera.rotateUp(deltaTheta, transform); } @@ -1034,7 +918,6 @@ define([ if (dot < 1.0 && !Cartesian3.equalsEpsilon(axis, Cartesian3.ZERO, CesiumMath.EPSILON14)) { // dot is in [0, 1] var angle = Math.acos(dot); - angle = adjustRotateForTerrain(controller, frameState, Cartesian3.ZERO, cameraPosMag, axis, angle); camera.rotate(axis, angle); } } else { @@ -1094,10 +977,7 @@ define([ deltaTheta = startTheta - endTheta; } - deltaPhi = adjustRotateForTerrain(controller, frameState, Cartesian3.ZERO, cameraPosMag, camera.up, deltaPhi); camera.rotateRight(deltaPhi); - - deltaTheta = adjustRotateForTerrain(controller, frameState, Cartesian3.ZERO, cameraPosMag, camera.right, deltaTheta); camera.rotateUp(deltaTheta); } } @@ -1185,14 +1065,13 @@ define([ var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid, tilt3DVerticalTransform); - var globeOverride = controller._globe; var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); var constrainedAxis = Cartesian3.UNIT_Z; var restrictedAngle = CesiumMath.PI_OVER_TWO - angle; - rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true, globeOverride); + rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true); var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); if (Cartesian3.dot(camera.right, tangent) < 0.0) { @@ -1205,11 +1084,11 @@ define([ var oldConstrainedAxis = camera.constrainedAxis; camera.constrainedAxis = undefined; - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, globeOverride); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); camera.constrainedAxis = oldConstrainedAxis; } else { - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false, globeOverride); + rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); } controller.globe = oldGlobe; @@ -1267,6 +1146,49 @@ define([ reactToInput(controller, frameState, controller.enableLook, controller.lookEventTypes, look3D); } + var scratchAdjustHeightCartographic = new Cartographic(); + + function adjustHeightForTerrain(controller, frameState) { + var mode = frameState.mode; + var globe = controller._globe; + + if (!defined(globe) || mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) { + return; + } + + var camera = controller._camera; + var ellipsoid = controller._ellipsoid; + var projection = frameState.mapProjection; + + var cartographic = scratchAdjustHeightCartographic; + if (mode === SceneMode.SCENE3D) { + ellipsoid.cartesianToCartographic(camera.position, cartographic); + } else { + projection.unproject(camera.position, cartographic); + } + + if (cartographic.height > controller.minimumCollisionTerrainHeight) { + return; + } + + var height = globe.getHeight(cartographic, frameState); + if (!defined(height)) { + return; + } + + height += controller.minimumZoomDistance; + if (cartographic.height >= height) { + return; + } + + cartographic.height = height; + if (mode === SceneMode.SCENE3D) { + ellipsoid.cartographicToCartesian(cartographic, camera.position); + } else { + projection.project(cartographic, camera.position); + } + } + /** * @private */ @@ -1282,6 +1204,8 @@ define([ update3D(this, frameState); } + adjustHeightForTerrain(this, frameState); + this._aggregator.reset(); }; diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index 98836d88ced1..e1568baf684b 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -314,7 +314,7 @@ define([ Cartesian3.unpack(vertices, index * 6, result); Cartesian3.add(tile.center, result, result); - if (frameState.mode !== SceneMode.SCENE3D) { + if (defined(frameState) && frameState.mode !== SceneMode.SCENE3D) { var projection = frameState.mapProjection; var ellipsoid = projection.ellipsoid; var positionCart = ellipsoid.cartesianToCartographic(result); @@ -331,7 +331,7 @@ define([ var scratchCartesian = new Cartesian3(); var scratchResult = new Cartesian3(); - Tile.prototype.pick = function(ray, frameState, result) { + Tile.prototype.pick = function(ray, frameState, cullBackFaces, result) { var terrain = this.pickTerrain; if (!defined(terrain)) { return undefined; @@ -355,15 +355,7 @@ define([ var v1 = getPosition(this, frameState, vertices, i1, scratchV1); var v2 = getPosition(this, frameState, vertices, i2, scratchV2); - var intersection; - if (Cartesian3.magnitude(ray.direction) > 1.0) { - var p0 = ray.origin; - var p1 = Cartesian3.add(p0, ray.direction, scratchCartesian); - intersection = IntersectionTests.lineSegmentTriangle(p0, p1, v0, v1, v2, true, scratchResult); - } else { - intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, true, scratchResult); - } - + var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, cullBackFaces, scratchResult); if (defined(intersection)) { return Cartesian3.clone(intersection, result); } From 493d57a642d6b6aa2504601e762b3a68c4539142 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 13 Jun 2014 16:25:10 -0400 Subject: [PATCH 52/74] Fix crash when tilting. Allow tilting below the point clicked. Remove unneeded code. --- Source/Scene/Camera.js | 60 ++++------------- Source/Scene/ScreenSpaceCameraController.js | 75 ++++++++------------- Specs/Scene/GeometryRenderingSpec.js | 72 +++++++++++++------- 3 files changed, 90 insertions(+), 117 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 48d2c696c9ae..aecc520119a0 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -1086,23 +1086,6 @@ define([ this.look(this.direction, -amount); }; - var appendTransformMatrix = new Matrix4(); - - function appendTransform(camera, transform) { - var oldTransform; - if (defined(transform)) { - oldTransform = Matrix4.clone(camera.transform, appendTransformMatrix); - camera.setTransform(transform); - } - return oldTransform; - } - - function revertTransform(camera, transform) { - if (defined(transform)) { - camera.setTransform(transform); - } - } - var rotateScratchQuaternion = new Quaternion(); var rotateScratchMatrix = new Matrix3(); /** @@ -1111,20 +1094,13 @@ define([ * * @param {Cartesian3} axis The axis to rotate around given in world coordinates. * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to defaultRotateAmount. - * @param {Matrix4} [transform] A transform to append to the camera transform before the rotation. Does not alter the camera's transform. * * @see Camera#rotateUp * @see Camera#rotateDown * @see Camera#rotateLeft * @see Camera#rotateRight - * - * @example - * // Rotate about a point on the earth. - * var center = ellipsoid.cartographicToCartesian(cartographic); - * var transform = Cesium.Matrix4.fromTranslation(center); - * camera.rotate(axis, angle, transform); */ - Camera.prototype.rotate = function(axis, angle, transform) { + Camera.prototype.rotate = function(axis, angle) { //>>includeStart('debug', pragmas.debug); if (!defined(axis)) { throw new DeveloperError('axis is required.'); @@ -1134,50 +1110,44 @@ define([ var turnAngle = defaultValue(angle, this.defaultRotateAmount); var quaternion = Quaternion.fromAxisAngle(axis, -turnAngle, rotateScratchQuaternion); var rotation = Matrix3.fromQuaternion(quaternion, rotateScratchMatrix); - var oldTransform = appendTransform(this, transform); Matrix3.multiplyByVector(rotation, this.position, this.position); Matrix3.multiplyByVector(rotation, this.direction, this.direction); Matrix3.multiplyByVector(rotation, this.up, this.up); Cartesian3.cross(this.direction, this.up, this.right); Cartesian3.cross(this.right, this.direction, this.up); - revertTransform(this, oldTransform); }; /** * Rotates the camera around the center of the camera's reference frame by angle downwards. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to defaultRotateAmount. - * @param {Matrix4} [transform] A transform to append to the camera transform before the rotation. Does not alter the camera's transform. * * @see Camera#rotateUp * @see Camera#rotate */ - Camera.prototype.rotateDown = function(angle, transform) { + Camera.prototype.rotateDown = function(angle) { angle = defaultValue(angle, this.defaultRotateAmount); - rotateVertical(this, angle, transform); + rotateVertical(this, angle); }; /** * Rotates the camera around the center of the camera's reference frame by angle upwards. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to defaultRotateAmount. - * @param {Matrix4} [transform] A transform to append to the camera transform before the rotation. Does not alter the camera's transform. * * @see Camera#rotateDown * @see Camera#rotate */ - Camera.prototype.rotateUp = function(angle, transform) { + Camera.prototype.rotateUp = function(angle) { angle = defaultValue(angle, this.defaultRotateAmount); - rotateVertical(this, -angle, transform); + rotateVertical(this, -angle); }; var rotateVertScratchP = new Cartesian3(); var rotateVertScratchA = new Cartesian3(); var rotateVertScratchTan = new Cartesian3(); var rotateVertScratchNegate = new Cartesian3(); - function rotateVertical(camera, angle, transform) { - var oldTransform = appendTransform(camera, transform); - + function rotateVertical(camera, angle) { var position = camera.position; var p = Cartesian3.normalize(position, rotateVertScratchP); if (defined(camera.constrainedAxis)) { @@ -1206,43 +1176,39 @@ define([ } else { camera.rotate(camera.right, angle); } - - revertTransform(camera, oldTransform); } /** * Rotates the camera around the center of the camera's reference frame by angle to the right. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to defaultRotateAmount. - * @param {Matrix4} [transform] A transform to append to the camera transform before the rotation. Does not alter the camera's transform. * * @see Camera#rotateLeft * @see Camera#rotate */ - Camera.prototype.rotateRight = function(angle, transform) { + Camera.prototype.rotateRight = function(angle) { angle = defaultValue(angle, this.defaultRotateAmount); - rotateHorizontal(this, -angle, transform); + rotateHorizontal(this, -angle); }; /** * Rotates the camera around the center of the camera's reference frame by angle to the left. * * @param {Number} [angle] The angle, in radians, to rotate by. Defaults to defaultRotateAmount. - * @param {Matrix4} [transform] A transform to append to the camera transform before the rotation. Does not alter the camera's transform. * * @see Camera#rotateRight * @see Camera#rotate */ - Camera.prototype.rotateLeft = function(angle, transform) { + Camera.prototype.rotateLeft = function(angle) { angle = defaultValue(angle, this.defaultRotateAmount); - rotateHorizontal(this, angle, transform); + rotateHorizontal(this, angle); }; - function rotateHorizontal(camera, angle, transform) { + function rotateHorizontal(camera, angle) { if (defined(camera.constrainedAxis)) { - camera.rotate(camera.constrainedAxis, angle, transform); + camera.rotate(camera.constrainedAxis, angle); } else { - camera.rotate(camera.up, angle, transform); + camera.rotate(camera.up, angle); } } diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 1d151c61420f..b3a34134398b 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -653,6 +653,7 @@ define([ var rotateCVPlane = new Plane(Cartesian3.ZERO, 0.0); var rotateCVCartesian3 = new Cartesian3(); var rotateCVCart = new Cartographic(); + var rotateCVOldTransform = new Matrix4(); function rotateCV(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { @@ -724,13 +725,17 @@ define([ var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; - var constrainedAxis = Cartesian3.UNIT_Z; - rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, undefined, false, true); var tangent = Cartesian3.cross(Cartesian3.UNIT_Z, Cartesian3.normalize(camera.position, rotateCVCartesian3), rotateCVCartesian3); - if (Cartesian3.dot(camera.right, tangent) < 0.0) { + var dot = Cartesian3.dot(camera.right, tangent); + + var oldTransform = Matrix4.clone(camera.transform, rotateCVOldTransform); + camera.setTransform(transform); + rotate3D(controller, startPosition, movement, frameState, constrainedAxis, false, true); + camera.setTransform(verticalTransform); + if (dot < 0.0) { if (movement.startPosition.y > movement.endPosition.y) { constrainedAxis = undefined; } @@ -738,13 +743,14 @@ define([ var oldConstrainedAxis = camera.constrainedAxis; camera.constrainedAxis = undefined; - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); + rotate3D(controller, startPosition, movement, frameState, constrainedAxis, true, false); camera.constrainedAxis = oldConstrainedAxis; } else { - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, undefined, true, false); + rotate3D(controller, startPosition, movement, frameState, constrainedAxis, true, false); } + camera.setTransform(oldTransform); controller.globe = oldGlobe; } @@ -827,12 +833,7 @@ define([ } } - var rotate3DRestrictedDirection = Cartesian3.clone(Cartesian3.ZERO); - var rotate3DScratchCartesian3 = new Cartesian3(); - var rotate3DNegateScratch = new Cartesian3(); - var rotate3DInverseMatrixScratch = new Matrix4(); - - function rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, rotateOnlyVertical, rotateOnlyHorizontal) { + function rotate3D(controller, startPosition, movement, frameState, constrainedAxis, rotateOnlyVertical, rotateOnlyHorizontal) { rotateOnlyVertical = defaultValue(rotateOnlyVertical, false); rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, false); @@ -861,28 +862,12 @@ define([ var deltaPhi = rotateRate * phiWindowRatio * Math.PI * 2.0; var deltaTheta = rotateRate * thetaWindowRatio * Math.PI; - var center = !defined(transform) ? Cartesian3.ZERO : Matrix4.getColumn(transform, 3); - var radius = Cartesian3.distance(camera.position, center); - if (!rotateOnlyVertical) { - camera.rotateRight(deltaPhi, transform); + camera.rotateRight(deltaPhi); } if (!rotateOnlyHorizontal) { - camera.rotateUp(deltaTheta, transform); - } - - if (defined(restrictedAngle)) { - var direction = Cartesian3.clone(camera.directionWC, rotate3DRestrictedDirection); - var invTransform = Matrix4.inverseTransformation(transform, rotate3DInverseMatrixScratch); - direction = Matrix4.multiplyByPointAsVector(invTransform, direction, direction); - - var dot = -Cartesian3.dot(direction, constrainedAxis); - var angle = Math.acos(dot); - if (angle > restrictedAngle) { - angle -= restrictedAngle; - camera.rotateUp(-angle, transform); - } + camera.rotateUp(deltaTheta); } camera.constrainedAxis = oldAxis; @@ -1005,6 +990,7 @@ define([ var tilt3DVerticalTransform = new Matrix4(); var tilt3DNormal = new Cartesian3(); var tilt3DCartesian3 = new Cartesian3(); + var tilt3DOldTransform = new Matrix4(); function tilt3D(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { @@ -1012,14 +998,7 @@ define([ } var camera = controller._camera; - var ellipsoid = controller._ellipsoid; - var minHeight = controller.minimumZoomDistance * 0.25; - var height = ellipsoid.cartesianToCartographic(camera.positionWC).height; - if (height - minHeight - 1.0 < CesiumMath.EPSILON3 && - movement.endPosition.y - movement.startPosition.y < 0) { - return; - } var center; var ray; @@ -1045,7 +1024,6 @@ define([ Cartesian3.clone(center, controller._tiltCenter); } - var verticalCenter; var windowPosition = tilt3DWindowPos; windowPosition.x = controller._canvas.clientWidth / 2; @@ -1060,37 +1038,42 @@ define([ if (!defined(intersection)) { return; } - verticalCenter = Ray.getPoint(ray, intersection.start, tilt3DVerticalCenter); + + var t = Cartesian3.magnitude(ray.origin) > mag ? intersection.start : intersection.stop; + var verticalCenter = Ray.getPoint(ray, t, tilt3DVerticalCenter); var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid, tilt3DVerticalTransform); var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; - - var angle = (minHeight * 0.25) / Cartesian3.distance(center, camera.position); var constrainedAxis = Cartesian3.UNIT_Z; - var restrictedAngle = CesiumMath.PI_OVER_TWO - angle; - rotate3D(controller, startPosition, movement, frameState, transform, constrainedAxis, restrictedAngle, false, true); var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); - if (Cartesian3.dot(camera.right, tangent) < 0.0) { + var dot = Cartesian3.dot(camera.right, tangent); + + var oldTransform = Matrix4.clone(camera.transform, tilt3DOldTransform); + camera.setTransform(transform); + rotate3D(controller, startPosition, movement, frameState, constrainedAxis, false, true); + + camera.setTransform(verticalTransform); + if (dot < 0.0) { if (movement.startPosition.y > movement.endPosition.y) { constrainedAxis = undefined; - restrictedAngle = undefined; } var oldConstrainedAxis = camera.constrainedAxis; camera.constrainedAxis = undefined; - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); + rotate3D(controller, startPosition, movement, frameState, constrainedAxis, true, false); camera.constrainedAxis = oldConstrainedAxis; } else { - rotate3D(controller, startPosition, movement, frameState, verticalTransform, constrainedAxis, restrictedAngle, true, false); + rotate3D(controller, startPosition, movement, frameState, constrainedAxis, true, false); } + camera.setTransform(oldTransform); controller.globe = oldGlobe; } diff --git a/Specs/Scene/GeometryRenderingSpec.js b/Specs/Scene/GeometryRenderingSpec.js index d8103b7435b5..1ef670bf612a 100644 --- a/Specs/Scene/GeometryRenderingSpec.js +++ b/Specs/Scene/GeometryRenderingSpec.js @@ -576,7 +576,8 @@ defineSuite([ var transform = Matrix4.multiplyByTranslation( Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center), new Cartesian3(0.0, 0.0, height)); - frameState.camera.rotateDown(CesiumMath.PI, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI); }; render3D(instance, afterView); }); @@ -584,7 +585,8 @@ defineSuite([ it('renders wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -797,7 +799,8 @@ defineSuite([ it('renders bottom', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(CesiumMath.PI, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI); }; render3D(instance, afterView); }); @@ -805,7 +808,8 @@ defineSuite([ it('renders north wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -813,7 +817,8 @@ defineSuite([ it('renders south wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -821,7 +826,8 @@ defineSuite([ it('renders west wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -829,7 +835,8 @@ defineSuite([ it('renders east wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -991,7 +998,8 @@ defineSuite([ var transform = Matrix4.multiplyByTranslation( Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center), new Cartesian3(0.0, 0.0, height)); - frameState.camera.rotateDown(CesiumMath.PI, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI); }; render3D(instance, afterView); }); @@ -999,7 +1007,8 @@ defineSuite([ it('renders wall 1', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateUp(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateUp(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1007,7 +1016,8 @@ defineSuite([ it('renders wall 2', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1015,7 +1025,8 @@ defineSuite([ it('renders wall 3', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1023,7 +1034,8 @@ defineSuite([ it('renders wall 4', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1060,7 +1072,8 @@ defineSuite([ viewSphere3D(frameState.camera, primitive._boundingSphere, primitive.modelMatrix); var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO); frameState.camera.moveForward(primitive._boundingSphere.radius * 0.75); context.uniformState.update(context, frameState); @@ -1100,7 +1113,8 @@ defineSuite([ afterView3D = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO); frameState.camera.zoomIn(primitive._boundingSphere.radius * 0.99); }; @@ -1251,7 +1265,8 @@ defineSuite([ var transform = Matrix4.multiplyByTranslation( Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center), new Cartesian3(0.0, 0.0, height)); - frameState.camera.rotateDown(CesiumMath.PI, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI); }; render3D(instance, afterView); }); @@ -1259,7 +1274,8 @@ defineSuite([ it('renders north wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1267,7 +1283,8 @@ defineSuite([ it('renders south wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1275,7 +1292,8 @@ defineSuite([ it('renders west wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1283,7 +1301,8 @@ defineSuite([ it('renders east wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1343,7 +1362,8 @@ defineSuite([ var transform = Matrix4.multiplyByTranslation( Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center), new Cartesian3(0.0, 0.0, height)); - frameState.camera.rotateDown(CesiumMath.PI, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI); }; render3D(instance, afterView); }); @@ -1351,7 +1371,8 @@ defineSuite([ it('renders north wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1359,7 +1380,8 @@ defineSuite([ it('renders south wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateDown(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1367,7 +1389,8 @@ defineSuite([ it('renders west wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(-CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); @@ -1375,7 +1398,8 @@ defineSuite([ it('renders east wall', function() { var afterView = function(frameState, primitive) { var transform = Transforms.eastNorthUpToFixedFrame(primitive._boundingSphere.center); - frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO, transform); + frameState.camera.setTransform(transform); + frameState.camera.rotateRight(CesiumMath.PI_OVER_TWO); }; render3D(instance, afterView); }); From 3c5549e526e2e899a35e2d76e9cc8a8d166e3662 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 13 Jun 2014 17:15:25 -0400 Subject: [PATCH 53/74] Adjust camera orientation after collision with terrain when tilting. --- Source/Scene/ScreenSpaceCameraController.js | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index b3a34134398b..68d812eb8270 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -991,6 +991,8 @@ define([ var tilt3DNormal = new Cartesian3(); var tilt3DCartesian3 = new Cartesian3(); var tilt3DOldTransform = new Matrix4(); + var tilt3DQuaternion = new Quaternion(); + var tilt3DMatrix = new Matrix3(); function tilt3D(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { @@ -1075,6 +1077,28 @@ define([ camera.setTransform(oldTransform); controller.globe = oldGlobe; + + var originalPosition = Cartesian3.clone(camera.position, tilt3DCartesian3); + adjustHeightForTerrain(controller, frameState); + + if (!Cartesian3.equals(camera.position, originalPosition)) { + camera.setTransform(verticalTransform); + var invTransform = Matrix4.inverseTransformation(verticalTransform, tilt3DTransform); + Matrix4.multiplyByPoint(invTransform, originalPosition, originalPosition); + + var angle = Cartesian3.angleBetween(originalPosition, camera.position); + var axis = Cartesian3.cross(originalPosition, camera.position, originalPosition); + Cartesian3.normalize(axis, axis); + + var quaternion = Quaternion.fromAxisAngle(axis, angle, tilt3DQuaternion); + var rotation = Matrix3.fromQuaternion(quaternion, tilt3DMatrix); + Matrix3.multiplyByVector(rotation, camera.direction, camera.direction); + Matrix3.multiplyByVector(rotation, camera.up, camera.up); + Cartesian3.cross(camera.direction, camera.up, camera.right); + Cartesian3.cross(camera.right, camera.direction, camera.up); + + camera.setTransform(oldTransform); + } } var look3DStartPos = new Cartesian2(); From 5f08530dbddccb4f06d552e988e251e980eea561 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Fri, 13 Jun 2014 17:49:36 -0400 Subject: [PATCH 54/74] Adjust camera orientation after collision with terrain when tilting in Columbus view. --- Source/Scene/ScreenSpaceCameraController.js | 28 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 68d812eb8270..98f635f4b35c 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -752,6 +752,27 @@ define([ camera.setTransform(oldTransform); controller.globe = oldGlobe; + + var originalPosition = Cartesian3.clone(camera.positionWC, tilt3DCartesian3); + adjustHeightForTerrain(controller, frameState); + + if (!Cartesian3.equals(camera.positionWC, originalPosition)) { + camera.setTransform(verticalTransform); + camera.worldToCameraCoordinatesPoint(originalPosition, originalPosition); + + var angle = Cartesian3.angleBetween(originalPosition, camera.position); + var axis = Cartesian3.cross(originalPosition, camera.position, originalPosition); + Cartesian3.normalize(axis, axis); + + var quaternion = Quaternion.fromAxisAngle(axis, angle, tilt3DQuaternion); + var rotation = Matrix3.fromQuaternion(quaternion, tilt3DMatrix); + Matrix3.multiplyByVector(rotation, camera.direction, camera.direction); + Matrix3.multiplyByVector(rotation, camera.up, camera.up); + Cartesian3.cross(camera.direction, camera.up, camera.right); + Cartesian3.cross(camera.right, camera.direction, camera.up); + + camera.setTransform(oldTransform); + } } var zoomCVWindowPos = new Cartesian2(); @@ -1078,13 +1099,12 @@ define([ camera.setTransform(oldTransform); controller.globe = oldGlobe; - var originalPosition = Cartesian3.clone(camera.position, tilt3DCartesian3); + var originalPosition = Cartesian3.clone(camera.positionWC, tilt3DCartesian3); adjustHeightForTerrain(controller, frameState); - if (!Cartesian3.equals(camera.position, originalPosition)) { + if (!Cartesian3.equals(camera.positionWC, originalPosition)) { camera.setTransform(verticalTransform); - var invTransform = Matrix4.inverseTransformation(verticalTransform, tilt3DTransform); - Matrix4.multiplyByPoint(invTransform, originalPosition, originalPosition); + camera.worldToCameraCoordinatesPoint(originalPosition, originalPosition); var angle = Cartesian3.angleBetween(originalPosition, camera.position); var axis = Cartesian3.cross(originalPosition, camera.position, originalPosition); From 37aee14ba7acfccfc7825e55fd84c71223f4370e Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 16 Jun 2014 15:16:39 -0400 Subject: [PATCH 55/74] Adjust camera orientation after tilting without a contrained axis (the camera is already over it for example mouse clicks near the bottom of the screen). Fix a couple of crashes from undefined values. --- Source/Scene/GlobeSurface.js | 6 +- Source/Scene/ScreenSpaceCameraController.js | 89 +++++++++++++++++++-- 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index 7532513ffd7c..b219edb3e010 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -378,10 +378,14 @@ define([ } //>>includeEnd('debug'); + var levelZeroTiles = this._levelZeroTiles; + if (!defined(levelZeroTiles)) { + return; + } + var tile; var i; - var levelZeroTiles = this._levelZeroTiles; var length = levelZeroTiles.length; for (i = 0; i < length; ++i) { tile = levelZeroTiles[i]; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 98f635f4b35c..ab72f7a663f8 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -654,6 +654,8 @@ define([ var rotateCVCartesian3 = new Cartesian3(); var rotateCVCart = new Cartographic(); var rotateCVOldTransform = new Matrix4(); + var rotateCVQuaternion = new Quaternion(); + var rotateCVMatrix = new Matrix3(); function rotateCV(controller, startPosition, movement, frameState) { if (defined(movement.angleAndHeight)) { @@ -717,11 +719,16 @@ define([ var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, rotateCVTransform); - Cartesian3.fromElements(verticalCenter.y, verticalCenter.z, verticalCenter.x, verticalCenter); - cart = projection.unproject(verticalCenter, rotateCVCart); - ellipsoid.cartographicToCartesian(cart, verticalCenter); + var verticalTransform; + if (defined(verticalCenter)) { + Cartesian3.fromElements(verticalCenter.y, verticalCenter.z, verticalCenter.x, verticalCenter); + cart = projection.unproject(verticalCenter, rotateCVCart); + ellipsoid.cartographicToCartesian(cart, verticalCenter); - var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, ellipsoid, rotateCVVerticalTransform); + verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, ellipsoid, rotateCVVerticalTransform); + } else { + verticalTransform = transform; + } var oldGlobe = controller.globe; controller.globe = Ellipsoid.UNIT_SPHERE; @@ -750,10 +757,44 @@ define([ rotate3D(controller, startPosition, movement, frameState, constrainedAxis, true, false); } + if (defined(camera.constrainedAxis)) { + var right = Cartesian3.cross(camera.direction, camera.constrainedAxis, tilt3DCartesian3); + if (!Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)) { + if (Cartesian3.dot(right, camera.right) < 0.0) { + Cartesian3.negate(right, right); + } + + Cartesian3.cross(right, camera.direction, camera.up); + Cartesian3.cross(camera.direction, camera.up, camera.right); + } else { + var mostOrthogonalAxis = Cartesian3.mostOrthogonalAxis(camera.constrainedAxis, tilt3DCartesian3); + if (Math.abs(Cartesian3.dot(mostOrthogonalAxis, camera.up) < 0.5)) { + Cartesian3.cross(camera.direction, mostOrthogonalAxis, right); + if (Cartesian3.dot(right, camera.right) < 0.0) { + Cartesian3.negate(right, right); + } + + Cartesian3.cross(right, camera.direction, camera.up); + Cartesian3.cross(camera.direction, camera.up, camera.right); + } else { + var up = Cartesian3.cross(camera.direction, mostOrthogonalAxis, tilt3DCartesian3); + if (Cartesian3.dot(up, camera.up) < 0.0) { + Cartesian3.negate(up, up); + } + + Cartesian3.cross(camera.direction, up, camera.right); + Cartesian3.cross(camera.right, camera.direction, camera.up); + } + } + + Cartesian3.normalize(camera.up, camera.up); + Cartesian3.normalize(camera.right, camera.right); + } + camera.setTransform(oldTransform); controller.globe = oldGlobe; - var originalPosition = Cartesian3.clone(camera.positionWC, tilt3DCartesian3); + var originalPosition = Cartesian3.clone(camera.positionWC, rotateCVCartesian3); adjustHeightForTerrain(controller, frameState); if (!Cartesian3.equals(camera.positionWC, originalPosition)) { @@ -764,8 +805,8 @@ define([ var axis = Cartesian3.cross(originalPosition, camera.position, originalPosition); Cartesian3.normalize(axis, axis); - var quaternion = Quaternion.fromAxisAngle(axis, angle, tilt3DQuaternion); - var rotation = Matrix3.fromQuaternion(quaternion, tilt3DMatrix); + var quaternion = Quaternion.fromAxisAngle(axis, angle, rotateCVQuaternion); + var rotation = Matrix3.fromQuaternion(quaternion, rotateCVMatrix); Matrix3.multiplyByVector(rotation, camera.direction, camera.direction); Matrix3.multiplyByVector(rotation, camera.up, camera.up); Cartesian3.cross(camera.direction, camera.up, camera.right); @@ -1096,6 +1137,40 @@ define([ rotate3D(controller, startPosition, movement, frameState, constrainedAxis, true, false); } + if (defined(camera.constrainedAxis)) { + var right = Cartesian3.cross(camera.direction, camera.constrainedAxis, tilt3DCartesian3); + if (!Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)) { + if (Cartesian3.dot(right, camera.right) < 0.0) { + Cartesian3.negate(right, right); + } + + Cartesian3.cross(right, camera.direction, camera.up); + Cartesian3.cross(camera.direction, camera.up, camera.right); + } else { + var mostOrthogonalAxis = Cartesian3.mostOrthogonalAxis(camera.constrainedAxis, tilt3DCartesian3); + if (Math.abs(Cartesian3.dot(mostOrthogonalAxis, camera.up) < 0.5)) { + Cartesian3.cross(camera.direction, mostOrthogonalAxis, right); + if (Cartesian3.dot(right, camera.right) < 0.0) { + Cartesian3.negate(right, right); + } + + Cartesian3.cross(right, camera.direction, camera.up); + Cartesian3.cross(camera.direction, camera.up, camera.right); + } else { + var up = Cartesian3.cross(camera.direction, mostOrthogonalAxis, tilt3DCartesian3); + if (Cartesian3.dot(up, camera.up) < 0.0) { + Cartesian3.negate(up, up); + } + + Cartesian3.cross(camera.direction, up, camera.right); + Cartesian3.cross(camera.right, camera.direction, camera.up); + } + } + + Cartesian3.normalize(camera.up, camera.up); + Cartesian3.normalize(camera.right, camera.right); + } + camera.setTransform(oldTransform); controller.globe = oldGlobe; From 74facc1e7820ae5492256051c25d0cc9004a55b8 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 16 Jun 2014 15:22:31 -0400 Subject: [PATCH 56/74] Update CHANGES.md and remove unused code. --- CHANGES.md | 5 ++--- Source/Core/Quaternion.js | 36 ------------------------------------ Specs/Core/QuaternionSpec.js | 29 ----------------------------- 3 files changed, 2 insertions(+), 68 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index db364fe59571..667eefc5da8b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -63,11 +63,10 @@ Beta Releases * Added `PerformanceWatchdog` widget and `viewerPerformanceWatchdogMixin`. * Fixed a problem that could rarely lead to the camera's `tilt` property being `NaN`. * Added the following methods to `IntersectionTests`: `rayTriangle`, `lineSegmentTriangle`, `raySphere`, and `lineSegmentSphere`. -* Added `Quaternion.rotateVector` for fast application of a quaternion rotation to a vector. * Fixed camera rotation near the camera's constrained axis. -* Added `Globe.intersect` and `Globe.pick` for finding intersections of a ray or line segment with the globe. `Globe.pick` only performs a ray intersection test with the part of the surface that was rendered. +* Added `Globe.getHeight` and `Globe.pick` for finding the terrain height at a given Cartographic coordinate and picking the terrain with a ray. * Modified the default camera tilt mouse behavior to tilt about the point clicked. -* Added camera collision detection to the default mouse interaction. +* Added camera collision detection with terrain to the default mouse interaction. * Updated third-party [Tween.js](https://github.com/sole/tween.js/) from r7 to r13. * `Viewer` can now optionally be constructed with a `DataSourceCollection`. Previously, it always created one itself internally. * `GeoJsonDataSource` no longer uses the `name` or `title` property of the feature as the dynamic object's name if the value of the property is null. diff --git a/Source/Core/Quaternion.js b/Source/Core/Quaternion.js index 3b0a0b3e3cf1..fdc057c7bd15 100644 --- a/Source/Core/Quaternion.js +++ b/Source/Core/Quaternion.js @@ -99,42 +99,6 @@ define([ return result; }; - var scratchRotateCross = new Cartesian3(); - - /** - * Apply a quaternion rotation to a vector. - * @memberof Quaternion - * - * @param {Quaternion} quaternion The rotation. - * @param {Cartesian3} cartesian The cartesian to rotate. - * @param {Cartesian3} [result] The object onto which to store the result. - * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. - */ - Quaternion.rotateVector = function(quaternion, cartesian, result) { - //>>includeStart('debug', pragmas.debug); - if (!defined(quaternion)) { - throw new DeveloperError('quaternion is required.'); - } - if (!defined(cartesian)) { - throw new DeveloperError('quaternion is required.'); - } - //>>includeEnd('debug'); - - if (!defined(result)) { - result = new Cartesian3(); - } - - // t = 2.0 * cross(quaternion.xyz, cartesian) - var t = Cartesian3.cross(quaternion, cartesian, scratchRotateCross); - Cartesian3.multiplyByScalar(t, 2.0, t); - - // return cartesian + quaternion.w * t + cross(quaternion.xyz, t) - Cartesian3.cross(quaternion, t, result); - Cartesian3.multiplyByScalar(t, quaternion.w, t); - Cartesian3.add(t, result, result); - return Cartesian3.add(result, cartesian, result); - }; - var fromRotationMatrixNext = [1, 2, 0]; var fromRotationMatrixQuat = new Array(3); /** diff --git a/Specs/Core/QuaternionSpec.js b/Specs/Core/QuaternionSpec.js index 332b2f9dc79b..d95dc2fae585 100644 --- a/Specs/Core/QuaternionSpec.js +++ b/Specs/Core/QuaternionSpec.js @@ -54,23 +54,6 @@ defineSuite([ expect(returnedResult).toEqual(expected); }); - it('rotateVector works without a result parameter', function() { - var axis = Cartesian3.UNIT_Z; - var angle = CesiumMath.PI_OVER_TWO; - var rotation = Quaternion.fromAxisAngle(axis, angle); - expect(Quaternion.rotateVector(rotation, Cartesian3.UNIT_X)).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON14); - }); - - it('rotateVector works with a result parameter', function() { - var axis = Cartesian3.UNIT_Z; - var angle = CesiumMath.PI_OVER_TWO; - var rotation = Quaternion.fromAxisAngle(axis, angle); - var result = new Cartesian3(); - var actual = Quaternion.rotateVector(rotation, Cartesian3.UNIT_X, result); - expect(actual).toBe(result); - expect(actual).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON14); - }); - it('fromRotationMatrix works when m22 is max', function() { var q = Quaternion.fromAxisAngle(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), Math.PI); var rotation = new Matrix3(-1.0, 0.0, 0.0, @@ -796,18 +779,6 @@ defineSuite([ }).toThrowDeveloperError(); }); - it('rotateVector throws without quaternion', function() { - expect(function() { - Quaternion.rotateVector(undefined, new Cartesian3()); - }).toThrowDeveloperError(); - }); - - it('rotateVector throws without cartesian', function() { - expect(function() { - Quaternion.rotateVector(new Quaternion(), undefined); - }).toThrowDeveloperError(); - }); - it('fromRotationMatrix throws with undefined matrix', function() { expect(function() { Quaternion.fromRotationMatrix(undefined); From 097a74cf082f3d9419580c1e1ae44cabb630db2d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 17 Jun 2014 15:16:00 -0400 Subject: [PATCH 57/74] Fix panning when looking up at terrain. --- Source/Scene/Camera.js | 3 +- Source/Scene/ScreenSpaceCameraController.js | 48 ++++++++++++--------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index aecc520119a0..2b3af4167c87 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -1652,7 +1652,8 @@ define([ return undefined; } - return Ray.getPoint(ray, intersection.start, result); + var t = (Cartesian3.magnitude(ray.origin) < ellipsoid.maximumRadius) ? intersection.stop : intersection.start; + return Ray.getPoint(ray, t, result); } var pickEllipsoid2DRay = new Ray(); diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index ab72f7a663f8..7570bb705e8c 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -873,23 +873,21 @@ define([ var scratchEllipsoid = new Ellipsoid(); function spin3D(controller, startPosition, movement, frameState) { - if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { - var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; - if (defined(controller._globe) && height < controller.minimumPickingTerrainHeight) { - var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); - var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); - if (!defined(mousePos)) { - pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); - } else { - var magnitude = Cartesian3.magnitude(mousePos); - var radii = scratchRadii; - radii.x = radii.y = radii.z = magnitude; - var ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); - pan3D(controller, startPosition, movement, frameState, ellipsoid); - } + var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; + if (defined(controller._globe) && height < controller.minimumPickingTerrainHeight) { + var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); + var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); + if (!defined(mousePos)) { + rotate3D(controller, startPosition, movement, frameState); } else { - pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); + var magnitude = Cartesian3.magnitude(mousePos); + var radii = scratchRadii; + radii.x = radii.y = radii.z = magnitude; + var ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); + pan3D(controller, startPosition, movement, frameState, ellipsoid); } + } else if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { + pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); } else { rotate3D(controller, startPosition, movement, frameState); } @@ -941,12 +939,22 @@ define([ var pan3DTemp1 = new Cartesian3(); var pan3DTemp2 = new Cartesian3(); var pan3DTemp3 = new Cartesian3(); - var basis1Scratch = new Cartesian3(); + var pan3DStartMousePosition = new Cartesian2(); + var pan3DEndMousePosition = new Cartesian2(); function pan3D(controller, startPosition, movement, frameState, ellipsoid) { var camera = controller._camera; - var p0 = camera.pickEllipsoid(movement.startPosition, ellipsoid, pan3DP0); - var p1 = camera.pickEllipsoid(movement.endPosition, ellipsoid, pan3DP1); + var cameraPosMag = Cartesian3.magnitude(camera.position); + + var startMousePosition = Cartesian2.clone(movement.startPosition, pan3DStartMousePosition); + var endMousePosition = Cartesian2.clone(movement.endPosition, pan3DEndMousePosition); + if (cameraPosMag < ellipsoid.maximumRadius) { + startMousePosition.y = endMousePosition.y; + endMousePosition.y = movement.startPosition.y; + } + + var p0 = camera.pickEllipsoid(startMousePosition, ellipsoid, pan3DP0); + var p1 = camera.pickEllipsoid(endMousePosition, ellipsoid, pan3DP1); if (!defined(p0) || !defined(p1)) { return; @@ -955,8 +963,6 @@ define([ p0 = camera.worldToCameraCoordinates(p0, p0); p1 = camera.worldToCameraCoordinates(p1, p1); - var cameraPosMag = Cartesian3.magnitude(camera.position); - if (!defined(camera.constrainedAxis)) { Cartesian3.normalize(p0, p0); Cartesian3.normalize(p1, p1); @@ -969,7 +975,7 @@ define([ } } else { var basis0 = camera.constrainedAxis; - var basis1 = Cartesian3.mostOrthogonalAxis(basis0, pan3DTemp0, basis1Scratch); + var basis1 = Cartesian3.mostOrthogonalAxis(basis0, pan3DTemp0); Cartesian3.cross(basis1, basis0, basis1); Cartesian3.normalize(basis1, basis1); var basis2 = Cartesian3.cross(basis0, basis1, pan3DTemp1); From 9833ca3def28303f7734957865ca987086e4b716 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 17 Jun 2014 15:28:33 -0400 Subject: [PATCH 58/74] Fix abrupt orientation change from tiltting when the camera direction and constrained axis are parallel. --- Source/Scene/ScreenSpaceCameraController.js | 46 ++------------------- 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 7570bb705e8c..472a21188e1a 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -766,29 +766,10 @@ define([ Cartesian3.cross(right, camera.direction, camera.up); Cartesian3.cross(camera.direction, camera.up, camera.right); - } else { - var mostOrthogonalAxis = Cartesian3.mostOrthogonalAxis(camera.constrainedAxis, tilt3DCartesian3); - if (Math.abs(Cartesian3.dot(mostOrthogonalAxis, camera.up) < 0.5)) { - Cartesian3.cross(camera.direction, mostOrthogonalAxis, right); - if (Cartesian3.dot(right, camera.right) < 0.0) { - Cartesian3.negate(right, right); - } - - Cartesian3.cross(right, camera.direction, camera.up); - Cartesian3.cross(camera.direction, camera.up, camera.right); - } else { - var up = Cartesian3.cross(camera.direction, mostOrthogonalAxis, tilt3DCartesian3); - if (Cartesian3.dot(up, camera.up) < 0.0) { - Cartesian3.negate(up, up); - } - Cartesian3.cross(camera.direction, up, camera.right); - Cartesian3.cross(camera.right, camera.direction, camera.up); - } + Cartesian3.normalize(camera.up, camera.up); + Cartesian3.normalize(camera.right, camera.right); } - - Cartesian3.normalize(camera.up, camera.up); - Cartesian3.normalize(camera.right, camera.right); } camera.setTransform(oldTransform); @@ -1152,29 +1133,10 @@ define([ Cartesian3.cross(right, camera.direction, camera.up); Cartesian3.cross(camera.direction, camera.up, camera.right); - } else { - var mostOrthogonalAxis = Cartesian3.mostOrthogonalAxis(camera.constrainedAxis, tilt3DCartesian3); - if (Math.abs(Cartesian3.dot(mostOrthogonalAxis, camera.up) < 0.5)) { - Cartesian3.cross(camera.direction, mostOrthogonalAxis, right); - if (Cartesian3.dot(right, camera.right) < 0.0) { - Cartesian3.negate(right, right); - } - - Cartesian3.cross(right, camera.direction, camera.up); - Cartesian3.cross(camera.direction, camera.up, camera.right); - } else { - var up = Cartesian3.cross(camera.direction, mostOrthogonalAxis, tilt3DCartesian3); - if (Cartesian3.dot(up, camera.up) < 0.0) { - Cartesian3.negate(up, up); - } - Cartesian3.cross(camera.direction, up, camera.right); - Cartesian3.cross(camera.right, camera.direction, camera.up); - } + Cartesian3.normalize(camera.up, camera.up); + Cartesian3.normalize(camera.right, camera.right); } - - Cartesian3.normalize(camera.up, camera.up); - Cartesian3.normalize(camera.right, camera.right); } camera.setTransform(oldTransform); From 9f94ee713da00f555894cd1b3ade9b6fbbe09292 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 23 Jun 2014 17:14:28 -0400 Subject: [PATCH 59/74] Remove ScreenSpaceCameraController.globe. Fix panning when mouse is over the sky. Fix crash. --- Apps/Sandcastle/gallery/3D Models.html | 3 - Apps/Sandcastle/gallery/Camera.html | 8 - CHANGES.md | 2 +- Source/DynamicScene/DynamicObjectView.js | 8 - Source/Scene/GlobeSurface.js | 2 +- Source/Scene/Scene.js | 2 +- Source/Scene/ScreenSpaceCameraController.js | 232 ++++++++---------- Source/Widgets/CesiumWidget/CesiumWidget.js | 1 - Source/Widgets/Geocoder/GeocoderViewModel.js | 4 - .../Widgets/HomeButton/HomeButtonViewModel.js | 3 - Specs/Scene/ModelSpec.js | 4 - .../Scene/ScreenSpaceCameraControllerSpec.js | 83 ++----- 12 files changed, 123 insertions(+), 229 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index d5842a769a9b..1a31b111203c 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -55,9 +55,6 @@ var camera = scene.camera; camera.transform = transform; camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z; - var controller = scene.screenSpaceCameraController; - controller.globe = Cesium.Ellipsoid.UNIT_SPHERE; - controller.enableTilt = false; var r = 1.25 * Math.max(model.boundingSphere.radius, camera.frustum.near); controller.minimumZoomDistance = r * 0.25; camera.lookAt(new Cesium.Cartesian3(r, r, r), Cesium.Cartesian3.ZERO, Cesium.Cartesian3.UNIT_Z); diff --git a/Apps/Sandcastle/gallery/Camera.html b/Apps/Sandcastle/gallery/Camera.html index f4d70e74d519..24cb028fa863 100644 --- a/Apps/Sandcastle/gallery/Camera.html +++ b/Apps/Sandcastle/gallery/Camera.html @@ -35,10 +35,6 @@ scene.primitives.removeAll(); scene.animations.removeAll(); - var controller = scene.screenSpaceCameraController; - controller.globe = scene.globe; - controller.enableTilt = true; - scene.camera.setTransform(Cesium.Matrix4.IDENTITY); clock.multiplier = 1.0; @@ -130,10 +126,6 @@ Cesium.Matrix4.clone(transform, camera.transform); camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z; - var controller = scene.screenSpaceCameraController; - controller.globe = Cesium.Ellipsoid.UNIT_SPHERE; - controller.enableTilt = false; - // Zoom in camera.lookAt( new Cesium.Cartesian3(-120000.0, -120000.0, 120000.0), diff --git a/CHANGES.md b/CHANGES.md index bd5f193175a9..8f8db61344b2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -48,7 +48,7 @@ Beta Releases * `date.lessThanOrEquals (right)` -> `JulianDate.lessThanOrEquals (left, right)` * `date.greaterThan (right)` -> `JulianDate.greaterThan (left, right)` * `date.greaterThanOrEquals (right)` -> `JulianDate.greaterThanOrEquals (left, right)` - * Renamed `ScreenSpaceCameraController.ellipsoid` to `ScreenSpaceCameraController.globe` whose type can now be `Globe` or `Ellipsoid`. + * Removed `ScreenSpaceCameraController.ellipsoid`. The behavior that depended on the ellipsoid is now determined based on the scene state. * `DynamicObject.id` can now include period characters. * `ReferenceProperty` can now handle sub-properties, for example, `myObject#billboard.scale`. * Added `Cesium.VERSION` to the combined `Cesium.js` file. diff --git a/Source/DynamicScene/DynamicObjectView.js b/Source/DynamicScene/DynamicObjectView.js index 6d4ab004b8ae..0e5ac8f91e16 100644 --- a/Source/DynamicScene/DynamicObjectView.js +++ b/Source/DynamicScene/DynamicObjectView.js @@ -144,8 +144,6 @@ define([ // Stationary or slow-moving, low-altitude objects use East-North-Up. Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, camera.transform); } - - that._screenSpaceCameraController.globe = Ellipsoid.UNIT_SPHERE; } updateController(that, camera, objectChanged); @@ -194,7 +192,6 @@ define([ * @type {Scene} */ this.scene = scene; - this._lastScene = undefined; /** * The ellipsoid to use for orienting the camera. @@ -246,11 +243,6 @@ define([ } //>>includeEnd('debug'); - if (scene !== this._lastScene) { - this._lastScene = scene; - this._screenSpaceCameraController = scene.screenSpaceCameraController; - } - var positionProperty = dynamicObject.position; var objectChanged = dynamicObject !== this._lastDynamicObject; this._lastDynamicObject = dynamicObject; diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index b219edb3e010..ee4d98860938 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -410,7 +410,7 @@ define([ } } - while (!defined(tile.pickTerrain) && defined(tile)) { + while (defined(tile) && !defined(tile.pickTerrain)) { tile = tile.parent; } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 871e775bb659..9e5d1b526aff 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -441,7 +441,7 @@ define([ var camera = new Camera(this); this._camera = camera; - this._screenSpaceCameraController = new ScreenSpaceCameraController(canvas, camera); + this._screenSpaceCameraController = new ScreenSpaceCameraController(this); // initial guess at frustums. var near = camera.frustum.near; diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 472a21188e1a..e0b54139ae6c 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -58,17 +58,12 @@ define([ * @alias ScreenSpaceCameraController * @constructor * - * @param {Canvas} canvas The canvas to listen for events. - * @param {Camera} camera The camera. - * @param {Globe|Ellipsoid} [globeOrEllipsoid=Ellipsoid.WGS84] The globe or ellipsoid to use to determine camera movement direction and speed. + * @param {Scene} scene The scene. */ - var ScreenSpaceCameraController = function(canvas, camera, globeOrEllipsoid) { + var ScreenSpaceCameraController = function(scene) { //>>includeStart('debug', pragmas.debug); - if (!defined(canvas)) { - throw new DeveloperError('canvas is required.'); - } - if (!defined(camera)) { - throw new DeveloperError('camera is required.'); + if (!defined(scene)) { + throw new DeveloperError('scene is required.'); } //>>includeEnd('debug'); @@ -246,24 +241,11 @@ define([ */ this.minimumCollisionTerrainHeight = 10000.0; - this._camera = camera; - this._canvas = canvas; - - var globe; - var ellipsoid; + this._scene = scene; + this._globe = undefined; + this._ellipsoid = undefined; - globeOrEllipsoid = defaultValue(globeOrEllipsoid, Ellipsoid.WGS84); - if (defined(globeOrEllipsoid.ellipsoid)) { - globe = globeOrEllipsoid; - ellipsoid = globe.ellipsoid; - } else { - ellipsoid = globeOrEllipsoid; - } - - this._globe = globe; - this._ellipsoid = ellipsoid; - - this._aggregator = new CameraEventAggregator(this._canvas); + this._aggregator = new CameraEventAggregator(scene.canvas); this._lastInertiaSpinMovement = undefined; this._lastInertiaZoomMovement = undefined; @@ -280,10 +262,9 @@ define([ this._tiltCenter = new Cartesian3(); // Constants, Make any of these public? - var radius = this._ellipsoid.maximumRadius; this._zoomFactor = 5.0; - this._rotateFactor = 1.0 / radius; - this._rotateRateRangeAdjustment = radius; + this._rotateFactor = undefined; + this._rotateRateRangeAdjustment = undefined; this._maximumRotateRate = 1.77; this._minimumRotateRate = 1.0 / 5000.0; this._translateFactor = 1.0; @@ -291,44 +272,6 @@ define([ this._maximumZoomRate = FAR; }; - defineProperties(ScreenSpaceCameraController.prototype, { - /** - * Gets and sets the globe. The globe is used to determine the size of the map in 2D and Columbus view - * as well as how fast to rotate the camera based on the distance to its surface. - * @memberof ScreenSpaceCameraController.prototype - * @type {Globe|Ellipsoid} - */ - globe : { - get : function() { - if (defined(this._globe)) { - return this._globe; - } - - return this._ellipsoid; - }, - set : function(globe) { - //>>includeStart('debug', pragmas.debug); - if (!defined(globe)) { - throw new DeveloperError('globe is required'); - } - //>>includeEnd('debug'); - - var ellipsoid = globe.ellipsoid; - if (!defined(ellipsoid)) { - ellipsoid = globe; - globe = undefined; - } - - this._globe = globe; - this._ellipsoid = ellipsoid; - - var radius = ellipsoid.maximumRadius; - this._rotateFactor = 1.0 / radius; - this._rotateRateRangeAdjustment = radius; - } - } - }); - function decay(time, coefficient) { if (time < 0) { return 0.0; @@ -458,7 +401,7 @@ define([ zoomRate = CesiumMath.clamp(zoomRate, object._minimumZoomRate, object._maximumZoomRate); var diff = movement.endPosition.y - movement.startPosition.y; - var rangeWindowRatio = diff / object._canvas.clientHeight; + var rangeWindowRatio = diff / object._scene.canvas.clientHeight; rangeWindowRatio = Math.min(rangeWindowRatio, object.maximumMovementRatio); var distance = zoomRate * rangeWindowRatio; @@ -476,7 +419,7 @@ define([ distance = distanceMeasure - maxHeight; } - object._camera.zoomIn(distance); + object._scene.camera.zoomIn(distance); } var translate2DStart = new Ray(); @@ -485,7 +428,7 @@ define([ var scratchTranslateP1 = new Cartesian3(); function translate2D(controller, startPosition, movement, frameState) { - var camera = controller._camera; + var camera = controller._scene.camera; var start = camera.getPickRay(movement.startPosition, translate2DStart).origin; var end = camera.getPickRay(movement.endPosition, translate2DEnd).origin; @@ -506,7 +449,7 @@ define([ movement = movement.distance; } - handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, controller._camera.getMagnitude()); + handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, controller._scene.camera.getMagnitude()); } var twist2DStart = new Cartesian2(); @@ -517,8 +460,9 @@ define([ return; } - var width = controller._canvas.clientWidth; - var height = controller._canvas.clientHeight; + var canvas = controller._scene.canvas; + var width = canvas.clientWidth; + var height = canvas.clientHeight; var start = twist2DStart; start.x = (2.0 / width) * movement.startPosition.x - 1.0; @@ -540,7 +484,7 @@ define([ } var theta = endTheta - startTheta; - controller._camera.twistRight(theta); + controller._scene.camera.twistRight(theta); } function singleAxisTwist2D(controller, startPosition, movement, frameState) { @@ -554,12 +498,12 @@ define([ rotateRate = controller._minimumRotateRate; } - var phiWindowRatio = (movement.endPosition.x - movement.startPosition.x) / controller._canvas.clientWidth; + var phiWindowRatio = (movement.endPosition.x - movement.startPosition.x) / controller._scene.canvas.clientWidth; phiWindowRatio = Math.min(phiWindowRatio, controller.maximumMovementRatio); var deltaPhi = rotateRate * phiWindowRatio * Math.PI * 4.0; - controller._camera.twistRight(deltaPhi); + controller._scene.camera.twistRight(deltaPhi); } function update2D(controller, frameState) { @@ -568,7 +512,9 @@ define([ animations.removeAll(); } - if (!Matrix4.equals(Matrix4.IDENTITY, controller._camera.transform)) { + var camera = controller._scene.camera; + + if (!Matrix4.equals(Matrix4.IDENTITY, camera.transform)) { reactToInput(controller, frameState, controller.enableRotate, controller.translateEventTypes, twist2D, controller.inertiaSpin, '_lastInertiaSpinMovement'); reactToInput(controller, frameState, controller.enableZoom, controller.zoomEventTypes, zoom3D, controller.inertiaZoom, '_lastInertiaZoomMovement'); } else { @@ -581,7 +527,7 @@ define([ (!defined(controller._lastInertiaZoomMovement) || !controller._lastInertiaZoomMovement.active) && (!defined(controller._lastInertiaTranslateMovement) || !controller._lastInertiaTranslateMovement.active) && !animations.contains(controller._animation)) { - var animation = controller._camera.createCorrectPositionAnimation(controller.bounceAnimationTime); + var animation = camera.createCorrectPositionAnimation(controller.bounceAnimationTime); if (defined(animation)) { controller._animation = animations.add(animation); } @@ -599,7 +545,7 @@ define([ var translateCVPlane = new Plane(Cartesian3.ZERO, 0.0); function translateCV(controller, startPosition, movement, frameState) { - var camera = controller._camera; + var camera = controller._scene.camera; var startRay = camera.getPickRay(movement.startPosition, translateCVStartRay); var endRay = camera.getPickRay(movement.endPosition, translateCVEndRay); @@ -608,7 +554,7 @@ define([ var startPlanePos; - if (defined(controller._globe) && controller._camera.position.z < controller.minimumPickingTerrainHeight) { + if (defined(controller._globe) && camera.position.z < controller.minimumPickingTerrainHeight) { startPlanePos = controller._globe.pick(startRay, frameState, translateCVStartPos); if (defined(startPlanePos)) { origin.x = startPlanePos.x; @@ -663,11 +609,12 @@ define([ } var ellipsoid = controller._ellipsoid; - var camera = controller._camera; + var camera = controller._scene.camera; var center; var ray; var intersection; + var normal = Cartesian3.UNIT_X; if (Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) { center = Cartesian3.clone(controller._tiltCenter, rotateCVCenter); @@ -678,29 +625,19 @@ define([ } if (!defined(center)) { - intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); - if (!defined(intersection)) { - return; - } - center = Ray.getPoint(ray, intersection.start, rotateCVCenter); + var position = ray.origin; + var direction = ray.direction; + var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); + center = Cartesian3.multiplyByScalar(direction, scalar, rotateCVCenter); + Cartesian3.add(position, center, center); } Cartesian2.clone(startPosition, controller._tiltCenterMousePosition); Cartesian3.clone(center, controller._tiltCenter); } - var normal = Cartesian3.UNIT_X; - - if (!defined(center)) { - var position = ray.origin; - var direction = ray.direction; - var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); - center = Cartesian3.multiplyByScalar(direction, scalar, rotateCVCenter); - Cartesian3.add(position, center, center); - } - var windowPosition = rotateCVWindowPos; - windowPosition.x = controller._canvas.clientWidth / 2; + windowPosition.x = controller._scene.canvas.clientWidth / 2; windowPosition.y = controller._tiltCenterMousePosition.y; ray = camera.getPickRay(windowPosition, rotateCVWindowRay); @@ -710,7 +647,7 @@ define([ var plane = Plane.fromPointNormal(origin, normal, rotateCVPlane); var verticalCenter = IntersectionTests.rayPlane(ray, plane, rotateCVVerticalCenter); - var projection = controller._camera._projection; + var projection = controller._scene.camera._projection; ellipsoid = projection.ellipsoid; Cartesian3.fromElements(center.y, center.z, center.x, center); @@ -730,8 +667,13 @@ define([ verticalTransform = transform; } - var oldGlobe = controller.globe; - controller.globe = Ellipsoid.UNIT_SPHERE; + var oldGlobe = controller._globe; + var oldEllipsoid = controller._ellipsoid; + controller._globe = undefined; + controller._ellipsoid = Ellipsoid.UNIT_SPHERE; + controller._rotateFactor = 1.0; + controller._rotateRateRangeAdjustment = 1.0; + var constrainedAxis = Cartesian3.UNIT_Z; var tangent = Cartesian3.cross(Cartesian3.UNIT_Z, Cartesian3.normalize(camera.position, rotateCVCartesian3), rotateCVCartesian3); @@ -773,7 +715,12 @@ define([ } camera.setTransform(oldTransform); - controller.globe = oldGlobe; + controller._globe = oldGlobe; + controller._ellipsoid = oldEllipsoid; + + var radius = oldEllipsoid.maximumRadius; + controller._rotateFactor = 1.0 / radius; + controller._rotateRateRangeAdjustment = radius; var originalPosition = Cartesian3.clone(camera.positionWC, rotateCVCartesian3); adjustHeightForTerrain(controller, frameState); @@ -804,10 +751,12 @@ define([ movement = movement.distance; } + var canvas = controller._scene.canvas; + var windowPosition = zoomCVWindowPos; - windowPosition.x = controller._canvas.clientWidth / 2; - windowPosition.y = controller._canvas.clientHeight / 2; - var ray = controller._camera.getPickRay(windowPosition, zoomCVWindowRay); + windowPosition.x = canvas.clientWidth / 2; + windowPosition.y = canvas.clientHeight / 2; + var ray = controller._scene.camera.getPickRay(windowPosition, zoomCVWindowRay); var normal = Cartesian3.UNIT_X; var position = ray.origin; @@ -818,7 +767,9 @@ define([ } function updateCV(controller, frameState) { - if (!Matrix4.equals(Matrix4.IDENTITY, controller._camera.transform)) { + var camera = controller._scene.camera; + + if (!Matrix4.equals(Matrix4.IDENTITY, camera.transform)) { reactToInput(controller, frameState, controller.enableRotate, controller.rotateEventTypes, rotate3D, controller.inertiaSpin, '_lastInertiaSpinMovement'); reactToInput(controller, frameState, controller.enableZoom, controller.zoomEventTypes, zoom3D, controller.inertiaZoom, '_lastInertiaZoomMovement'); } else { @@ -836,7 +787,7 @@ define([ if (!controller._aggregator.anyButtonDown() && (!defined(controller._lastInertiaZoomMovement) || !controller._lastInertiaZoomMovement.active) && (!defined(controller._lastInertiaTranslateMovement) || !controller._lastInertiaTranslateMovement.active) && !animations.contains(controller._animation)) { - var animation = controller._camera.createCorrectPositionAnimation(controller.bounceAnimationTime); + var animation = camera.createCorrectPositionAnimation(controller.bounceAnimationTime); if (defined(animation)) { controller._animation = animations.add(animation); } @@ -854,23 +805,25 @@ define([ var scratchEllipsoid = new Ellipsoid(); function spin3D(controller, startPosition, movement, frameState) { - var height = controller._ellipsoid.cartesianToCartographic(controller._camera.positionWC, scratchCartographic).height; + var camera = controller._scene.camera; + if (!Matrix4.equals(camera.transform, Matrix4.IDENTITY)) { + rotate3D(controller, startPosition, movement, frameState); + return; + } + + var height = controller._ellipsoid.cartesianToCartographic(camera.positionWC, scratchCartographic).height; if (defined(controller._globe) && height < controller.minimumPickingTerrainHeight) { - var startRay = controller._camera.getPickRay(movement.startPosition, scratchStartRay); + var startRay = camera.getPickRay(movement.startPosition, scratchStartRay); var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); - if (!defined(mousePos)) { - rotate3D(controller, startPosition, movement, frameState); - } else { + if (defined(mousePos)) { var magnitude = Cartesian3.magnitude(mousePos); var radii = scratchRadii; radii.x = radii.y = radii.z = magnitude; var ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); pan3D(controller, startPosition, movement, frameState, ellipsoid); } - } else if (defined(controller._camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { + } else if (defined(camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); - } else { - rotate3D(controller, startPosition, movement, frameState); } } @@ -878,7 +831,7 @@ define([ rotateOnlyVertical = defaultValue(rotateOnlyVertical, false); rotateOnlyHorizontal = defaultValue(rotateOnlyHorizontal, false); - var camera = controller._camera; + var camera = controller._scene.camera; var oldAxis = camera.constrainedAxis; if (defined(constrainedAxis)) { camera.constrainedAxis = constrainedAxis; @@ -895,8 +848,9 @@ define([ rotateRate = controller._minimumRotateRate; } - var phiWindowRatio = (movement.startPosition.x - movement.endPosition.x) / controller._canvas.clientWidth; - var thetaWindowRatio = (movement.startPosition.y - movement.endPosition.y) / controller._canvas.clientHeight; + var canvas = controller._scene.canvas; + var phiWindowRatio = (movement.startPosition.x - movement.endPosition.x) / canvas.clientWidth; + var thetaWindowRatio = (movement.startPosition.y - movement.endPosition.y) / canvas.clientHeight; phiWindowRatio = Math.min(phiWindowRatio, controller.maximumMovementRatio); thetaWindowRatio = Math.min(thetaWindowRatio, controller.maximumMovementRatio); @@ -924,7 +878,7 @@ define([ var pan3DEndMousePosition = new Cartesian2(); function pan3D(controller, startPosition, movement, frameState, ellipsoid) { - var camera = controller._camera; + var camera = controller._scene.camera; var cameraPosMag = Cartesian3.magnitude(camera.position); var startMousePosition = Cartesian2.clone(movement.startPosition, pan3DStartMousePosition); @@ -1022,7 +976,7 @@ define([ movement = movement.distance; } - var camera = controller._camera; + var camera = controller._scene.camera; var ellipsoid = controller._ellipsoid; var height = ellipsoid.cartesianToCartographic(camera.position).height; @@ -1044,11 +998,15 @@ define([ var tilt3DMatrix = new Matrix3(); function tilt3D(controller, startPosition, movement, frameState) { + if (!Matrix4.equals(controller._scene.camera.transform, Matrix4.IDENTITY)) { + return; + } + if (defined(movement.angleAndHeight)) { movement = movement.angleAndHeight; } - var camera = controller._camera; + var camera = controller._scene.camera; var ellipsoid = controller._ellipsoid; var center; @@ -1077,7 +1035,7 @@ define([ var windowPosition = tilt3DWindowPos; - windowPosition.x = controller._canvas.clientWidth / 2; + windowPosition.x = controller._scene.canvas.clientWidth / 2; windowPosition.y = controller._tiltCenterMousePosition.y; ray = camera.getPickRay(windowPosition, tilt3DRay); @@ -1096,8 +1054,13 @@ define([ var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); var verticalTransform = Transforms.eastNorthUpToFixedFrame(verticalCenter, newEllipsoid, tilt3DVerticalTransform); - var oldGlobe = controller.globe; - controller.globe = Ellipsoid.UNIT_SPHERE; + var oldGlobe = controller._globe; + var oldEllipsoid = controller._ellipsoid; + controller._globe = undefined; + controller._ellipsoid = Ellipsoid.UNIT_SPHERE; + controller._rotateFactor = 1.0; + controller._rotateRateRangeAdjustment = 1.0; + var constrainedAxis = Cartesian3.UNIT_Z; var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); @@ -1140,7 +1103,12 @@ define([ } camera.setTransform(oldTransform); - controller.globe = oldGlobe; + controller._globe = oldGlobe; + controller._ellipsoid = oldEllipsoid; + + var radius = oldEllipsoid.maximumRadius; + controller._rotateFactor = 1.0 / radius; + controller._rotateRateRangeAdjustment = radius; var originalPosition = Cartesian3.clone(camera.positionWC, tilt3DCartesian3); adjustHeightForTerrain(controller, frameState); @@ -1169,7 +1137,7 @@ define([ var look3DStartRay = new Ray(); var look3DEndRay = new Ray(); function look3D(controller, startPosition, movement, frameState) { - var camera = controller._camera; + var camera = controller._scene.camera; var startPos = look3DStartPos; startPos.x = movement.startPosition.x; @@ -1226,7 +1194,7 @@ define([ return; } - var camera = controller._camera; + var camera = controller._scene.camera; var ellipsoid = controller._ellipsoid; var projection = frameState.mapProjection; @@ -1263,6 +1231,18 @@ define([ * @private */ ScreenSpaceCameraController.prototype.update = function(frameState) { + if (!Matrix4.equals(this._scene.camera.transform, Matrix4.IDENTITY)) { + this._globe = undefined; + this._ellipsoid = Ellipsoid.UNIT_SPHERE; + } else { + this._globe = this._scene.globe; + this._ellipsoid = defined(this._globe) ? this._globe.ellipsoid : this._scene.mapProjection.ellipsoid; + } + + var radius = this._ellipsoid.maximumRadius; + this._rotateFactor = 1.0 / radius; + this._rotateRateRangeAdjustment = radius; + var mode = frameState.mode; if (mode === SceneMode.SCENE2D) { update2D(this, frameState); diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.js b/Source/Widgets/CesiumWidget/CesiumWidget.js index afda9db01e52..59189109355a 100644 --- a/Source/Widgets/CesiumWidget/CesiumWidget.js +++ b/Source/Widgets/CesiumWidget/CesiumWidget.js @@ -194,7 +194,6 @@ define([ var globe = new Globe(ellipsoid); scene.globe = globe; - scene.screenSpaceCameraController.globe = globe; var skyBox = options.skyBox; if (!defined(skyBox)) { diff --git a/Source/Widgets/Geocoder/GeocoderViewModel.js b/Source/Widgets/Geocoder/GeocoderViewModel.js index dd56094d0b39..260a7be2df53 100644 --- a/Source/Widgets/Geocoder/GeocoderViewModel.js +++ b/Source/Widgets/Geocoder/GeocoderViewModel.js @@ -240,10 +240,6 @@ define([ viewModel._scene.camera.flyTo({ destination : position, duration : viewModel._flightDuration, - onComplete : function() { - var screenSpaceCameraController = viewModel._scene.screenSpaceCameraController; - screenSpaceCameraController.globe = viewModel._scene.globe; - }, endTransform : Matrix4.IDENTITY, convert : false }); diff --git a/Source/Widgets/HomeButton/HomeButtonViewModel.js b/Source/Widgets/HomeButton/HomeButtonViewModel.js index be999aa03a92..86af0031e098 100644 --- a/Source/Widgets/HomeButton/HomeButtonViewModel.js +++ b/Source/Widgets/HomeButton/HomeButtonViewModel.js @@ -27,9 +27,6 @@ define([ function viewHome(scene, duration) { var mode = scene.mode; - var controller = scene.screenSpaceCameraController; - - controller.globe = scene.globe; if (defined(scene) && mode === SceneMode.MORPHING) { scene.completeMorph(); diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index ada428e6e76c..cfbec146afd9 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -86,10 +86,6 @@ defineSuite([ camera.transform = transform; camera.constrainedAxis = Cartesian3.UNIT_Z; - var controller = scene.screenSpaceCameraController; - controller.ellipsoid = Ellipsoid.UNIT_SPHERE; - controller.enableTilt = false; - // Zoom in var r = Math.max(model.boundingSphere.radius, camera.frustum.near); camera.lookAt( diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 79f75074ed57..74951e6710b5 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -40,12 +40,22 @@ defineSuite([ "use strict"; /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ + var scene; var canvas; var camera; var controller; var MouseButtons = MockCanvas.MouseButtons; + var MockScene = function(canvas, camera, ellipsoid) { + this.canvas = canvas; + this.camera = camera; + this.globe = undefined; + this.mapProjection = { + ellipsoid : ellipsoid + }; + }; + beforeEach(function() { // create a mock canvas object to add events to so they are callable. canvas = new MockCanvas(); @@ -64,31 +74,21 @@ defineSuite([ near : 1.0, far : 500000000.0 }); - controller = new ScreenSpaceCameraController(canvas, camera); + + scene = new MockScene(canvas, camera, Ellipsoid.WGS84); + controller = new ScreenSpaceCameraController(scene); }); afterEach(function() { controller = controller && !controller.isDestroyed() && controller.destroy(); }); - it('constructor throws without a canvas', function() { + it('constructor throws without a scene', function() { expect(function() { return new ScreenSpaceCameraController(); }).toThrowDeveloperError(); }); - it('constructor throws without a camera', function() { - expect(function() { - return new ScreenSpaceCameraController(new MockCanvas()); - }).toThrowDeveloperError(); - }); - - it('get/set ellipsoid', function() { - expect(controller.globe).toEqual(Ellipsoid.WGS84); - controller.globe = Ellipsoid.UNIT_SPHERE; - expect(controller.globe).toEqual(Ellipsoid.UNIT_SPHERE); - }); - function updateController(frameState) { camera.update(frameState.mode); controller.update(frameState); @@ -671,21 +671,6 @@ defineSuite([ expect(Cartesian3.cross(camera.right, camera.direction, new Cartesian3())).toEqualEpsilon(camera.up, CesiumMath.EPSILON14); }); - it('rotates in 3D', function() { - var frameState = setUp3D(); - var position = Cartesian3.clone(camera.position); - var startPosition = new Cartesian2(0, 0); - var endPosition = new Cartesian2(canvas.clientWidth / 4, canvas.clientHeight / 4); - - MockCanvas.moveMouse(canvas, MouseButtons.LEFT, startPosition, endPosition); - updateController(frameState); - - expect(camera.position).not.toEqual(position); - expect(camera.direction).toEqualEpsilon(Cartesian3.normalize(Cartesian3.negate(camera.position, new Cartesian3()), new Cartesian3()), CesiumMath.EPSILON15); - expect(Cartesian3.cross(camera.direction, camera.up, new Cartesian3())).toEqualEpsilon(camera.right, CesiumMath.EPSILON15); - expect(Cartesian3.cross(camera.right, camera.direction, new Cartesian3())).toEqualEpsilon(camera.up, CesiumMath.EPSILON15); - }); - it('zoom in 3D', function() { var frameState = setUp3D(); var position = Cartesian3.clone(camera.position); @@ -838,24 +823,6 @@ defineSuite([ expect(camera.up).toEqualEpsilon(Cartesian3.cross(camera.right, camera.direction, new Cartesian3()), CesiumMath.EPSILON14); }); - it('rotates with constrained axis', function() { - var frameState = setUp3D(); - - var axis = Cartesian3.clone(Cartesian3.UNIT_Z); - camera.constrainedAxis = axis; - - var startPosition = new Cartesian2(0.0, 0.0); - var endPosition = new Cartesian2(0.0, canvas.clientHeight); - - MockCanvas.moveMouse(canvas, MouseButtons.LEFT, startPosition, endPosition); - updateController(frameState); - - expect(Cartesian3.normalize(camera.position, new Cartesian3())).toEqualEpsilon(axis, CesiumMath.EPSILON4); - expect(camera.direction).toEqualEpsilon(Cartesian3.negate(axis, new Cartesian3()), CesiumMath.EPSILON4); - expect(Cartesian3.dot(camera.up, axis)).toBeLessThan(CesiumMath.EPSILON2); - expect(camera.right).toEqualEpsilon(Cartesian3.cross(camera.direction, camera.up, new Cartesian3()), CesiumMath.EPSILON4); - }); - it('pans with constrained axis and is tilted', function() { var frameState = setUp3D(); camera.position = new Cartesian3(0.0, 2.0 * Ellipsoid.WGS84.maximumRadius, 0.0); @@ -878,28 +845,6 @@ defineSuite([ expect(camera.up).toEqualEpsilon(Cartesian3.cross(camera.right, camera.direction, new Cartesian3()), CesiumMath.EPSILON14); }); - it('rotates with constrained axis and is tilted', function() { - var frameState = setUp3D(); - camera.position = new Cartesian3(0.0, 2.0 * Ellipsoid.WGS84.maximumRadius, 0.0); - camera.direction = Cartesian3.negate(Cartesian3.normalize(camera.position, new Cartesian3()), new Cartesian3()); - camera.up = Cartesian3.clone(Cartesian3.UNIT_X); - camera.right = Cartesian3.cross(camera.direction, camera.up, new Cartesian3()); - - var axis = Cartesian3.clone(Cartesian3.UNIT_Z); - camera.constrainedAxis = axis; - - var startPosition = new Cartesian2(0.0, 0.0); - var endPosition = new Cartesian2(0.0, canvas.clientHeight); - - MockCanvas.moveMouse(canvas, MouseButtons.LEFT, startPosition, endPosition); - updateController(frameState); - - expect(Cartesian3.normalize(camera.position, new Cartesian3())).toEqualEpsilon(Cartesian3.UNIT_Z, CesiumMath.EPSILON4); - expect(camera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON4); - expect(camera.right).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Y, new Cartesian3()), CesiumMath.EPSILON4); - expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_X, CesiumMath.EPSILON4); - }); - it('controller does not modify the camera after re-enabling motion', function() { var frameState = setUp3D(); var position = Cartesian3.clone(camera.position); From b78d2ead2984c1415bb554ee74413384c3e7384a Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 23 Jun 2014 19:15:22 -0400 Subject: [PATCH 60/74] Fix Columbus view issue tilting in the wrong direction. --- Source/Scene/ScreenSpaceCameraController.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index e0b54139ae6c..a0d74ed2146a 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -676,11 +676,12 @@ define([ var constrainedAxis = Cartesian3.UNIT_Z; + var oldTransform = Matrix4.clone(camera.transform, rotateCVOldTransform); + camera.setTransform(transform); + var tangent = Cartesian3.cross(Cartesian3.UNIT_Z, Cartesian3.normalize(camera.position, rotateCVCartesian3), rotateCVCartesian3); var dot = Cartesian3.dot(camera.right, tangent); - var oldTransform = Matrix4.clone(camera.transform, rotateCVOldTransform); - camera.setTransform(transform); rotate3D(controller, startPosition, movement, frameState, constrainedAxis, false, true); camera.setTransform(verticalTransform); @@ -1063,11 +1064,12 @@ define([ var constrainedAxis = Cartesian3.UNIT_Z; + var oldTransform = Matrix4.clone(camera.transform, tilt3DOldTransform); + camera.setTransform(transform); + var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); var dot = Cartesian3.dot(camera.right, tangent); - var oldTransform = Matrix4.clone(camera.transform, tilt3DOldTransform); - camera.setTransform(transform); rotate3D(controller, startPosition, movement, frameState, constrainedAxis, false, true); camera.setTransform(verticalTransform); From d29c9d2301d3b247fe7bc9ad516a878292c498ba Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 23 Jun 2014 19:36:10 -0400 Subject: [PATCH 61/74] Only roll up the terrain for horizontal tilts, not vertical. --- Source/Scene/ScreenSpaceCameraController.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index a0d74ed2146a..2b0cebc16d13 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -730,6 +730,12 @@ define([ camera.setTransform(verticalTransform); camera.worldToCameraCoordinatesPoint(originalPosition, originalPosition); + var magSqrd = Cartesian3.magnitudeSquared(originalPosition); + if (Cartesian3.magnitudeSquared(camera.position) > magSqrd) { + Cartesian3.normalize(camera.position, camera.position); + Cartesian3.multiplyByScalar(camera.position, Math.sqrt(magSqrd), camera.position); + } + var angle = Cartesian3.angleBetween(originalPosition, camera.position); var axis = Cartesian3.cross(originalPosition, camera.position, originalPosition); Cartesian3.normalize(axis, axis); @@ -1119,6 +1125,12 @@ define([ camera.setTransform(verticalTransform); camera.worldToCameraCoordinatesPoint(originalPosition, originalPosition); + var magSqrd = Cartesian3.magnitudeSquared(originalPosition); + if (Cartesian3.magnitudeSquared(camera.position) > magSqrd) { + Cartesian3.normalize(camera.position, camera.position); + Cartesian3.multiplyByScalar(camera.position, Math.sqrt(magSqrd), camera.position); + } + var angle = Cartesian3.angleBetween(originalPosition, camera.position); var axis = Cartesian3.cross(originalPosition, camera.position, originalPosition); Cartesian3.normalize(axis, axis); From 5adc1fed6468cb82ee69ae431eb9353b60ddb376 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 23 Jun 2014 20:23:55 -0400 Subject: [PATCH 62/74] Adjust zoom speed to account for terrain. Fix pan direction in Columbus view when camera height is under the height of the terrain clicked. --- Source/Scene/ScreenSpaceCameraController.js | 77 +++++++++++++++------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 2b0cebc16d13..d5f373eb36e7 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -543,34 +543,37 @@ define([ var translatCVDifference = new Cartesian3(); var translateCVOrigin = new Cartesian3(); var translateCVPlane = new Plane(Cartesian3.ZERO, 0.0); + var translateCVStartMouse = new Cartesian2(); + var translateCVEndMouse = new Cartesian2(); function translateCV(controller, startPosition, movement, frameState) { var camera = controller._scene.camera; - var startRay = camera.getPickRay(movement.startPosition, translateCVStartRay); - var endRay = camera.getPickRay(movement.endPosition, translateCVEndRay); + var startMouse = Cartesian3.clone(movement.startPosition, translateCVStartMouse); + var endMouse = Cartesian3.clone(movement.endPosition, translateCVEndMouse); + var startRay = camera.getPickRay(startMouse, translateCVStartRay); var origin = Cartesian3.clone(Cartesian3.ZERO, translateCVOrigin); var normal = Cartesian3.UNIT_X; - var startPlanePos; - if (defined(controller._globe) && camera.position.z < controller.minimumPickingTerrainHeight) { - startPlanePos = controller._globe.pick(startRay, frameState, translateCVStartPos); - if (defined(startPlanePos)) { - origin.x = startPlanePos.x; + var intersection = controller._globe.pick(startRay, frameState, translateCVStartPos); + if (defined(intersection)) { + origin.x = intersection.x; } } - if (!defined(startPlanePos)) { - var position = startRay.origin; - var direction = startRay.direction; - - var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); - startPlanePos = Cartesian3.multiplyByScalar(direction, scalar, translateCVStartPos); - Cartesian3.add(position, startPlanePos, startPlanePos); + if (origin.x > camera.position.z) { + var tempY = startMouse.y; + startMouse.y = endMouse.y; + endMouse.y = tempY; } var plane = Plane.fromPointNormal(origin, normal, translateCVPlane); + + startRay = camera.getPickRay(startMouse, translateCVStartRay); + var startPlanePos = IntersectionTests.rayPlane(startRay, plane, translateCVStartPos); + + var endRay = camera.getPickRay(endMouse, translateCVEndRay); var endPlanePos = IntersectionTests.rayPlane(endRay, plane, translateCVEndPos); if (!defined(startPlanePos) || !defined(endPlanePos)) { @@ -753,24 +756,37 @@ define([ var zoomCVWindowPos = new Cartesian2(); var zoomCVWindowRay = new Ray(); + var zoomCVIntersection = new Cartesian3(); + function zoomCV(controller, startPosition, movement, frameState) { if (defined(movement.distance)) { movement = movement.distance; } var canvas = controller._scene.canvas; + var camera = controller._scene.camera; var windowPosition = zoomCVWindowPos; windowPosition.x = canvas.clientWidth / 2; windowPosition.y = canvas.clientHeight / 2; - var ray = controller._scene.camera.getPickRay(windowPosition, zoomCVWindowRay); - var normal = Cartesian3.UNIT_X; + var ray = camera.getPickRay(windowPosition, zoomCVWindowRay); - var position = ray.origin; - var direction = ray.direction; - var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); + var intersection; + if (defined(controller._globe) && camera.position.z < controller.minimumPickingTerrainHeight) { + intersection = controller._globe.pick(ray, frameState, zoomCVIntersection); + } + + var distance; + if (defined(intersection)) { + distance = Cartesian3.distance(ray.origin, intersection); + } else { + var normal = Cartesian3.UNIT_X; + var position = ray.origin; + var direction = ray.direction; + distance = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); + } - handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, scalar); + handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, distance); } function updateCV(controller, frameState) { @@ -985,11 +1001,28 @@ define([ var camera = controller._scene.camera; var ellipsoid = controller._ellipsoid; + var canvas = controller._scene.canvas; + var windowPosition = zoomCVWindowPos; + windowPosition.x = canvas.clientWidth / 2; + windowPosition.y = canvas.clientHeight / 2; + var ray = camera.getPickRay(windowPosition, zoomCVWindowRay); + + var intersection; var height = ellipsoid.cartesianToCartographic(camera.position).height; - var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition); + if (defined(controller._globe) && height < controller.minimumPickingTerrainHeight) { + intersection = controller._globe.pick(ray, frameState, zoomCVIntersection); + } + + var distance; + if (defined(intersection)) { + distance = Cartesian3.distance(ray.origin, intersection); + } else { + distance = height; + } - handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, height, Cartesian3.dot(unitPosition, camera.direction)); + var unitPosition = Cartesian3.normalize(camera.position, zoom3DUnitPosition); + handleZoom(controller, startPosition, movement, frameState, controller._zoomFactor, distance, Cartesian3.dot(unitPosition, camera.direction)); } var tilt3DWindowPos = new Cartesian2(); From 56da73ce1bd212cc9cd893d9d8e18b7c2978da65 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 24 Jun 2014 14:25:23 -0400 Subject: [PATCH 63/74] Fix jsHint error and probably the 3D model Sandcastle example. --- Apps/Sandcastle/gallery/3D Models.html | 1 + 1 file changed, 1 insertion(+) diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index 1a31b111203c..a8e8665b68a2 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -56,6 +56,7 @@ camera.transform = transform; camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z; var r = 1.25 * Math.max(model.boundingSphere.radius, camera.frustum.near); + var controller = scene.screenSpaceCameraController; controller.minimumZoomDistance = r * 0.25; camera.lookAt(new Cesium.Cartesian3(r, r, r), Cesium.Cartesian3.ZERO, Cesium.Cartesian3.UNIT_Z); }); From e113629f702d80764691f7358339b3e40983c56d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 25 Jun 2014 14:53:18 -0400 Subject: [PATCH 64/74] Do not start a pan when clicking the sky, but continue to pan when terrain was clicked and mouse moves over the sky. --- Source/Scene/ScreenSpaceCameraController.js | 27 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 286fdde4da71..69b5972aab1c 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -260,6 +260,8 @@ define([ this._tiltCenterMousePosition = new Cartesian2(); this._tiltCenter = new Cartesian3(); + this._rotateMousePosition = new Cartesian2(); + this._rotateStartPosition = new Cartesian3(); // Constants, Make any of these public? this._zoomFactor = 5.0; @@ -834,19 +836,38 @@ define([ return; } + var magnitude; + var radii; + var ellipsoid; + + if (Cartesian2.equals(startPosition, controller._rotateMousePosition)) { + magnitude = Cartesian3.magnitude(controller._rotateStartPosition); + radii = scratchRadii; + radii.x = radii.y = radii.z = magnitude; + ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); + pan3D(controller, startPosition, movement, frameState, ellipsoid); + return; + } + var height = controller._ellipsoid.cartesianToCartographic(camera.positionWC, scratchCartographic).height; if (defined(controller._globe) && height < controller.minimumPickingTerrainHeight) { var startRay = camera.getPickRay(movement.startPosition, scratchStartRay); var mousePos = controller._globe.pick(startRay, frameState, scratchMousePos); if (defined(mousePos)) { - var magnitude = Cartesian3.magnitude(mousePos); - var radii = scratchRadii; + magnitude = Cartesian3.magnitude(mousePos); + radii = scratchRadii; radii.x = radii.y = radii.z = magnitude; - var ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); + ellipsoid = Ellipsoid.fromCartesian3(radii, scratchEllipsoid); pan3D(controller, startPosition, movement, frameState, ellipsoid); + + Cartesian2.clone(startPosition, controller._rotateMousePosition); + Cartesian3.clone(mousePos, controller._rotateStartPosition); } } else if (defined(camera.pickEllipsoid(movement.startPosition, controller._ellipsoid, spin3DPick))) { pan3D(controller, startPosition, movement, frameState, controller._ellipsoid); + + Cartesian2.clone(startPosition, controller._rotateMousePosition); + Cartesian3.clone(spin3DPick, controller._rotateStartPosition); } } From 61c2564702daebade7ece64801d75015120b415d Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 1 Jul 2014 16:56:10 -0400 Subject: [PATCH 65/74] Better panning when camera height is below terrain clicked. --- Source/Scene/ScreenSpaceCameraController.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 60503ea290f9..3e2d7bb31010 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -918,22 +918,13 @@ define([ var pan3DTemp1 = new Cartesian3(); var pan3DTemp2 = new Cartesian3(); var pan3DTemp3 = new Cartesian3(); - var pan3DStartMousePosition = new Cartesian2(); - var pan3DEndMousePosition = new Cartesian2(); function pan3D(controller, startPosition, movement, frameState, ellipsoid) { var camera = controller._scene.camera; var cameraPosMag = Cartesian3.magnitude(camera.position); - var startMousePosition = Cartesian2.clone(movement.startPosition, pan3DStartMousePosition); - var endMousePosition = Cartesian2.clone(movement.endPosition, pan3DEndMousePosition); - if (cameraPosMag < ellipsoid.maximumRadius) { - startMousePosition.y = endMousePosition.y; - endMousePosition.y = movement.startPosition.y; - } - - var p0 = camera.pickEllipsoid(startMousePosition, ellipsoid, pan3DP0); - var p1 = camera.pickEllipsoid(endMousePosition, ellipsoid, pan3DP1); + var p0 = camera.pickEllipsoid(movement.startPosition, ellipsoid, pan3DP0); + var p1 = camera.pickEllipsoid(movement.endPosition, ellipsoid, pan3DP1); if (!defined(p0) || !defined(p1)) { return; @@ -942,6 +933,11 @@ define([ p0 = camera.worldToCameraCoordinates(p0, p0); p1 = camera.worldToCameraCoordinates(p1, p1); + if (cameraPosMag < ellipsoid.maximumRadius) { + p0.y = -p0.y; + p1.y = -p1.y; + } + if (!defined(camera.constrainedAxis)) { Cartesian3.normalize(p0, p0); Cartesian3.normalize(p1, p1); From 38e96fa2811fd8cf348041aa32feb183a0cf21dc Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 1 Jul 2014 17:58:33 -0400 Subject: [PATCH 66/74] Code updates based on review. --- Source/Core/IntersectionTests.js | 18 +++---- Source/Scene/CameraEventAggregator.js | 52 ++++++++++++--------- Source/Scene/Globe.js | 10 ++-- Source/Scene/GlobeSurface.js | 20 ++++---- Source/Scene/ScreenSpaceCameraController.js | 8 ++-- Source/Scene/Tile.js | 14 +++--- Specs/Scene/CameraEventAggregatorSpec.js | 10 ++-- 7 files changed, 70 insertions(+), 62 deletions(-) diff --git a/Source/Core/IntersectionTests.js b/Source/Core/IntersectionTests.js index 77d109b1b8e6..19cdd18831f2 100644 --- a/Source/Core/IntersectionTests.js +++ b/Source/Core/IntersectionTests.js @@ -78,7 +78,7 @@ define([ var scratchTVec = new Cartesian3(); var scratchQVec = new Cartesian3(); - function rayTriangle(ray, p0, p1, p2, cullBackFacing) { + function rayTriangle(ray, p0, p1, p2, cullBackFaces) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError('ray is required.'); @@ -94,7 +94,7 @@ define([ } //>>includeEnd('debug'); - cullBackFacing = defaultValue(cullBackFacing, false); + cullBackFaces = defaultValue(cullBackFaces, false); var origin = ray.origin; var direction = ray.direction; @@ -112,7 +112,7 @@ define([ var v; var t; - if (cullBackFacing) { + if (cullBackFaces) { if (det < CesiumMath.EPSILON6) { return undefined; } @@ -164,13 +164,13 @@ define([ * @param {Cartesian3} p0 The first vertex of the triangle. * @param {Cartesian3} p1 The second vertex of the triangle. * @param {Cartesian3} p2 The third vertex of the triangle. - * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle + * @param {Boolean} [cullBackFaces=false] If true, will only compute an intersection with the front face of the triangle * and return undefined for intersections with the back face. * @param {Cartesian3} [result] The Cartesian3 onto which to store the result. * @returns {Cartesian3} The intersection point or undefined if there is no intersections. */ - IntersectionTests.rayTriangle = function(ray, p0, p1, p2, cullBackFacing, result) { - var t = rayTriangle(ray, p0, p1, p2, cullBackFacing); + IntersectionTests.rayTriangle = function(ray, p0, p1, p2, cullBackFaces, result) { + var t = rayTriangle(ray, p0, p1, p2, cullBackFaces); if (!defined(t) || t < 0.0) { return undefined; } @@ -194,12 +194,12 @@ define([ * @param {Cartesian3} p0 The first vertex of the triangle. * @param {Cartesian3} p1 The second vertex of the triangle. * @param {Cartesian3} p2 The third vertex of the triangle. - * @param {Boolean} [cullBackFacing=false] If true, will only compute an intersection with the front face of the triangle + * @param {Boolean} [cullBackFaces=false] If true, will only compute an intersection with the front face of the triangle * and return undefined for intersections with the back face. * @param {Cartesian3} [result] The Cartesian3 onto which to store the result. * @returns {Cartesian3} The intersection point or undefined if there is no intersections. */ - IntersectionTests.lineSegmentTriangle = function(v0, v1, p0, p1, p2, cullBackFacing, result) { + IntersectionTests.lineSegmentTriangle = function(v0, v1, p0, p1, p2, cullBackFaces, result) { //>>includeStart('debug', pragmas.debug); if (!defined(v0)) { throw new DeveloperError('v0 is required.'); @@ -214,7 +214,7 @@ define([ Cartesian3.subtract(v1, v0, ray.direction); Cartesian3.normalize(ray.direction, ray.direction); - var t = rayTriangle(ray, p0, p1, p2, cullBackFacing); + var t = rayTriangle(ray, p0, p1, p2, cullBackFaces); if (!defined(t) || t < 0.0 || t > Cartesian3.distance(v0, v1)) { return undefined; } diff --git a/Source/Scene/CameraEventAggregator.js b/Source/Scene/CameraEventAggregator.js index d70fc5d14330..63c4c01abe81 100644 --- a/Source/Scene/CameraEventAggregator.js +++ b/Source/Scene/CameraEventAggregator.js @@ -2,6 +2,7 @@ define([ '../Core/Cartesian2', '../Core/defined', + '../Core/defineProperties', '../Core/destroyObject', '../Core/DeveloperError', '../Core/KeyboardEventModifier', @@ -12,6 +13,7 @@ define([ ], function( Cartesian2, defined, + defineProperties, destroyObject, DeveloperError, KeyboardEventModifier, @@ -296,15 +298,33 @@ define([ } }; - /** - * Gets the current mouse position. - * @memberof CameraEventAggregator - * - * @returns {Cartesian2} The current mouse position. - */ - CameraEventAggregator.prototype.getCurrentMousePosition = function() { - return this._currentMousePosition; - }; + defineProperties(CameraEventAggregator.prototype, { + /** + * Gets the current mouse position. + * @memberof CameraEventAggregator.prototype + * @type {Cartesian2} + */ + currentMousePosition : { + get : function() { + return this._currentMousePosition; + } + }, + + /** + * Gets whether any mouse button is down, a touch has started, or the wheel has been moved. + * @memberof CameraEventAggregator.prototype + * @type {Boolean} + */ + anyButtonDown : { + get : function() { + var wheelMoved = !this._update[getKey(CameraEventType.WHEEL)] || + !this._update[getKey(CameraEventType.WHEEL, KeyboardEventModifier.SHIFT)] || + !this._update[getKey(CameraEventType.WHEEL, KeyboardEventModifier.CTRL)] || + !this._update[getKey(CameraEventType.WHEEL, KeyboardEventModifier.ALT)]; + return this._buttonsDown > 0 || wheelMoved; + } + } + }); /** * Gets if a mouse button down or touch has started and has been moved. @@ -386,7 +406,6 @@ define([ /** * Gets the mouse position that started the aggregation. - * @memberof CameraEventAggregator * * @param {CameraEventType} type The camera event type. * @param {KeyboardEventModifier} [modifier] The keyboard modifier. @@ -407,19 +426,6 @@ define([ return this._eventStartPosition[key]; }; - /** - * Gets whether any mouse button is down, a touch has started, or the wheel has been moved. - * - * @returns {Boolean} Whether any mouse button is down, a touch has started, or the wheel has been moved. - */ - CameraEventAggregator.prototype.anyButtonDown = function() { - var wheelMoved = !this._update[getKey(CameraEventType.WHEEL)] || - !this._update[getKey(CameraEventType.WHEEL, KeyboardEventModifier.SHIFT)] || - !this._update[getKey(CameraEventType.WHEEL, KeyboardEventModifier.CTRL)] || - !this._update[getKey(CameraEventType.WHEEL, KeyboardEventModifier.ALT)]; - return this._buttonsDown > 0 || wheelMoved; - }; - /** * Gets the time the button was pressed or the touch was started. * diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 422e74a97642..e8248fd4565e 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -294,20 +294,20 @@ define([ }); /** - * Find an intersection between a ray and the globe surface that was rendered. + * Find an intersection between a ray and the globe surface that was rendered. The ray must be given in world coordinates. * * @param {Ray} ray The ray to test for intersection. - * @param {FrameState} frameState The current frame state. + * @param {Scene} scene The scene. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3|undefined} The intersection or undefined if none was found. * * @example * // find intersection of ray through a pixel and the globe * var ray = scene.camera.getPickRay(windowCoordinates); - * var intersection = globe.pick(ray, scene.frameState); + * var intersection = globe.pick(ray, scene); */ - Globe.prototype.pick = function(ray, frameState, result) { - return this._surface.pick(ray, frameState, result); + Globe.prototype.pick = function(ray, scene, result) { + return this._surface.pick(ray, scene, result); }; /** diff --git a/Source/Scene/GlobeSurface.js b/Source/Scene/GlobeSurface.js index ee4d98860938..f8221caf3153 100644 --- a/Source/Scene/GlobeSurface.js +++ b/Source/Scene/GlobeSurface.js @@ -292,28 +292,30 @@ define([ * Prefer this method over {@link GlobeSurface#intersect} when finding an intersection with the section of * the globe surface that is rendered for better performance *

- * @memberof GlobeSurface * * @param {Ray} ray The ray to test for intersection. - * @param {FrameState} frameState The current frame state. + * @param {Scene} scene The scene. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3|undefined} The intersection of undefined if none was found. * * @example * // find intersection of ray through a pixel and the globe * var ray = scene.camera.getPickRay(windowCoordinates); - * var intersection = surface.pick(ray, scene.frameState); + * var intersection = surface.pick(ray, scene); */ - GlobeSurface.prototype.pick = function(ray, frameState, result) { + GlobeSurface.prototype.pick = function(ray, scene, result) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError('ray is required'); } - if (!defined(frameState)) { - throw new DeveloperError('frameState is required'); + if (!defined(scene)) { + throw new DeveloperError('scene is required'); } //>>includeEnd('debug'); + var mode = scene.mode; + var projection = scene.mapProjection; + var sphereIntersections = scratchArray; sphereIntersections.length = 0; @@ -332,8 +334,8 @@ define([ tile = tileSet[j]; var boundingVolume = tile.pickBoundingSphere; - if (frameState.mode !== SceneMode.SCENE3D) { - BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, frameState.mapProjection, tile.minimumHeight, tile.maximumHeight, boundingVolume); + if (mode !== SceneMode.SCENE3D) { + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, projection, tile.minimumHeight, tile.maximumHeight, boundingVolume); Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); } else { BoundingSphere.clone(tile.boundingSphere3D, boundingVolume); @@ -351,7 +353,7 @@ define([ var intersection; length = sphereIntersections.length; for (i = 0; i < length; ++i) { - intersection = sphereIntersections[i].pick(ray, frameState, true, result); + intersection = sphereIntersections[i].pick(ray, scene, true, result); if (defined(intersection)) { break; } diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 3e2d7bb31010..dafac794c6d6 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -510,7 +510,7 @@ define([ function update2D(controller, frameState) { var tweens = controller._tweens; - if (controller._aggregator.anyButtonDown()) { + if (controller._aggregator.anyButtonDown) { tweens.removeAll(); } @@ -525,7 +525,7 @@ define([ reactToInput(controller, frameState, controller.enableRotate, controller.tiltEventTypes, twist2D, controller.inertiaSpin, '_lastInertiaTiltMovement'); } - if (!controller._aggregator.anyButtonDown() && + if (!controller._aggregator.anyButtonDown && (!defined(controller._lastInertiaZoomMovement) || !controller._lastInertiaZoomMovement.active) && (!defined(controller._lastInertiaTranslateMovement) || !controller._lastInertiaTranslateMovement.active) && !tweens.contains(controller._tween)) { @@ -800,7 +800,7 @@ define([ } else { var tweens = controller._tweens; - if (controller._aggregator.anyButtonDown()) { + if (controller._aggregator.anyButtonDown) { tweens.removeAll(); } @@ -809,7 +809,7 @@ define([ reactToInput(controller, frameState, controller.enableZoom, controller.zoomEventTypes, zoomCV, controller.inertiaZoom, '_lastInertiaZoomMovement'); reactToInput(controller, frameState, controller.enableLook, controller.lookEventTypes, look3D); - if (!controller._aggregator.anyButtonDown() && (!defined(controller._lastInertiaZoomMovement) || !controller._lastInertiaZoomMovement.active) && + if (!controller._aggregator.anyButtonDown && (!defined(controller._lastInertiaZoomMovement) || !controller._lastInertiaZoomMovement.active) && (!defined(controller._lastInertiaTranslateMovement) || !controller._lastInertiaTranslateMovement.active) && !tweens.contains(controller._tween)) { var tween = camera.createCorrectPositionTween(controller.bounceAnimationTime); diff --git a/Source/Scene/Tile.js b/Source/Scene/Tile.js index e1568baf684b..7ed5ddf0e529 100644 --- a/Source/Scene/Tile.js +++ b/Source/Scene/Tile.js @@ -310,12 +310,12 @@ define([ } }); - function getPosition(tile, frameState, vertices, index, result) { + function getPosition(tile, scene, vertices, index, result) { Cartesian3.unpack(vertices, index * 6, result); Cartesian3.add(tile.center, result, result); - if (defined(frameState) && frameState.mode !== SceneMode.SCENE3D) { - var projection = frameState.mapProjection; + if (defined(scene) && scene.mode !== SceneMode.SCENE3D) { + var projection = scene.mapProjection; var ellipsoid = projection.ellipsoid; var positionCart = ellipsoid.cartesianToCartographic(result); projection.project(positionCart, result); @@ -331,7 +331,7 @@ define([ var scratchCartesian = new Cartesian3(); var scratchResult = new Cartesian3(); - Tile.prototype.pick = function(ray, frameState, cullBackFaces, result) { + Tile.prototype.pick = function(ray, scene, cullBackFaces, result) { var terrain = this.pickTerrain; if (!defined(terrain)) { return undefined; @@ -351,9 +351,9 @@ define([ var i1 = indices[i + 1]; var i2 = indices[i + 2]; - var v0 = getPosition(this, frameState, vertices, i0, scratchV0); - var v1 = getPosition(this, frameState, vertices, i1, scratchV1); - var v2 = getPosition(this, frameState, vertices, i2, scratchV2); + var v0 = getPosition(this, scene, vertices, i0, scratchV0); + var v1 = getPosition(this, scene, vertices, i1, scratchV1); + var v2 = getPosition(this, scene, vertices, i2, scratchV2); var intersection = IntersectionTests.rayTriangle(ray, v0, v1, v2, cullBackFaces, scratchResult); if (defined(intersection)) { diff --git a/Specs/Scene/CameraEventAggregatorSpec.js b/Specs/Scene/CameraEventAggregatorSpec.js index 9fc63f0fea6e..12efde53c63c 100644 --- a/Specs/Scene/CameraEventAggregatorSpec.js +++ b/Specs/Scene/CameraEventAggregatorSpec.js @@ -137,7 +137,7 @@ defineSuite([ }); it('anyButtonDown', function() { - expect(handler.anyButtonDown(CameraEventType.LEFT_DRAG)).toEqual(false); + expect(handler.anyButtonDown).toEqual(false); var args = { button : MouseButtons.LEFT, @@ -145,18 +145,18 @@ defineSuite([ clientY : 0 }; canvas.fireEvents('mousedown', args); - expect(handler.anyButtonDown(CameraEventType.LEFT_DRAG)).toEqual(true); + expect(handler.anyButtonDown).toEqual(true); args.button = MouseButtons.RIGHT; canvas.fireEvents('mousedown', args); - expect(handler.anyButtonDown(CameraEventType.LEFT_DRAG)).toEqual(true); + expect(handler.anyButtonDown).toEqual(true); canvas.fireEvents('mouseup', args); - expect(handler.anyButtonDown(CameraEventType.LEFT_DRAG)).toEqual(true); + expect(handler.anyButtonDown).toEqual(true); args.button = MouseButtons.LEFT; canvas.fireEvents('mouseup', args); - expect(handler.anyButtonDown(CameraEventType.LEFT_DRAG)).toEqual(false); + expect(handler.anyButtonDown).toEqual(false); }); it('getButtonPressTime', function() { From f5f9bf1bf8fe943380c0e5488b5bd621d747655f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Tue, 1 Jul 2014 20:06:17 -0400 Subject: [PATCH 67/74] Slightly better panning when camera height is less that the terrain clicked. --- Source/Scene/ScreenSpaceCameraController.js | 23 ++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index dafac794c6d6..dad360e90e9b 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -918,13 +918,27 @@ define([ var pan3DTemp1 = new Cartesian3(); var pan3DTemp2 = new Cartesian3(); var pan3DTemp3 = new Cartesian3(); + var pan3DStartMousePosition = new Cartesian2(); + var pan3DEndMousePosition = new Cartesian2(); function pan3D(controller, startPosition, movement, frameState, ellipsoid) { var camera = controller._scene.camera; var cameraPosMag = Cartesian3.magnitude(camera.position); - var p0 = camera.pickEllipsoid(movement.startPosition, ellipsoid, pan3DP0); - var p1 = camera.pickEllipsoid(movement.endPosition, ellipsoid, pan3DP1); + var startMousePosition = Cartesian2.clone(movement.startPosition, pan3DStartMousePosition); + var endMousePosition = Cartesian2.clone(movement.endPosition, pan3DEndMousePosition); + if (cameraPosMag < ellipsoid.maximumRadius) { + startMousePosition.y = endMousePosition.y; + endMousePosition.y = movement.startPosition.y; + + var magnitude = cameraPosMag + (ellipsoid.maximumRadius - cameraPosMag) * 2.0; + var radii = scratchRadii; + radii.x = radii.y = radii.z = magnitude; + ellipsoid = Ellipsoid.fromCartesian3(radii, ellipsoid); + } + + var p0 = camera.pickEllipsoid(startMousePosition, ellipsoid, pan3DP0); + var p1 = camera.pickEllipsoid(endMousePosition, ellipsoid, pan3DP1); if (!defined(p0) || !defined(p1)) { return; @@ -933,11 +947,6 @@ define([ p0 = camera.worldToCameraCoordinates(p0, p0); p1 = camera.worldToCameraCoordinates(p1, p1); - if (cameraPosMag < ellipsoid.maximumRadius) { - p0.y = -p0.y; - p1.y = -p1.y; - } - if (!defined(camera.constrainedAxis)) { Cartesian3.normalize(p0, p0); Cartesian3.normalize(p1, p1); From 4d78093f6fb0c34943d75eff540e1aab4648e6a5 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Wed, 2 Jul 2014 13:14:43 -0400 Subject: [PATCH 68/74] Tweak CHANGES.md --- CHANGES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3c0361dceaca..689b74f52567 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,6 @@ Change Log ========== -Beta Releases -------------- ### 1.0 - 2014-08-01 * Breaking changes @@ -12,6 +10,9 @@ Beta Releases * Modified the default camera tilt mouse behavior to tilt about the point clicked. * Added camera collision detection with terrain to the default mouse interaction. +Beta Releases +------------- + ### b30 - 2014-07-01 * Breaking changes ([why so many?](https://groups.google.com/forum/#!topic/cesium-dev/Y_mG11IZD9k)) From 6eadfaf55602ee3b7210310600984f3f0d49a362 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Mon, 14 Jul 2014 18:04:19 -0400 Subject: [PATCH 69/74] Fixes after merge. --- Source/Scene/GlobeSurfaceTile.js | 3 - Source/Scene/QuadtreePrimitive.js | 164 ++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 3 deletions(-) diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 7ae90a4d4b19..edbf184a0d15 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -745,8 +745,5 @@ define([ surfaceTile.waterMaskTranslationAndScale.w = scaleY; } - return Tile; -}); - return Tile; return GlobeSurfaceTile; }); diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 52ebb567bff5..4aee334f66e2 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -1,5 +1,6 @@ /*global define*/ define([ + '../Core/BoundingSphere', '../Core/Cartesian3', '../Core/Cartographic', '../Core/defaultValue', @@ -8,13 +9,18 @@ define([ '../Core/DeveloperError', '../Core/EllipsoidalOccluder', '../Core/getTimestamp', + '../Core/IntersectionTests', '../Core/Queue', + '../Core/Ray', + '../Core/Rectangle', '../Core/Visibility', './QuadtreeOccluders', './QuadtreeTile', + './QuadtreeTileLoadState', './SceneMode', './TileReplacementQueue' ], function( + BoundingSphere, Cartesian3, Cartographic, defaultValue, @@ -23,10 +29,14 @@ define([ DeveloperError, EllipsoidalOccluder, getTimestamp, + IntersectionTests, Queue, + Ray, + Rectangle, Visibility, QuadtreeOccluders, QuadtreeTile, + QuadtreeTileLoadState, SceneMode, TileReplacementQueue) { "use strict"; @@ -154,6 +164,160 @@ define([ this._levelZeroTiles = undefined; }; + function createComparePickTileFunction(rayOrigin) { + return function(a, b) { + var aDist = BoundingSphere.distanceSquaredTo(a.pickBoundingSphere, rayOrigin); + var bDist = BoundingSphere.distanceSquaredTo(b.pickBoundingSphere, rayOrigin); + + return aDist - bDist; + }; + } + + var scratchArray = []; + var scratchSphereIntersectionResult = { + start : 0.0, + stop : 0.0 + }; + + /** + * Find an intersection between a ray and the quadtree that was rendered. + * + * @param {Ray} ray The ray to test for intersection. + * @param {Scene} scene The scene. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3|undefined} The intersection of undefined if none was found. + * + * @example + * // find intersection of ray through a pixel and the quadtree + * var ray = scene.camera.getPickRay(windowCoordinates); + * var intersection = quadtree.pick(ray, scene); + */ + QuadtreePrimitive.prototype.pick = function(ray, scene, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(ray)) { + throw new DeveloperError('ray is required'); + } + if (!defined(scene)) { + throw new DeveloperError('scene is required'); + } + //>>includeEnd('debug'); + + var mode = scene.mode; + var projection = scene.mapProjection; + + var sphereIntersections = scratchArray; + sphereIntersections.length = 0; + + var tilesToRender = this._tilesToRender; + var length = tilesToRender.length; + + var tile; + var i; + + for (i = 0; i < length; ++i) { + tile = tilesToRender[i]; + var tileData = tile.data; + + if (!defined(tileData)) { + continue; + } + + var boundingVolume = tileData.pickBoundingSphere; + if (mode !== SceneMode.SCENE3D) { + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, projection, tileData.minimumHeight, tileData.maximumHeight, boundingVolume); + Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); + } else { + BoundingSphere.clone(tileData.boundingSphere3D, boundingVolume); + } + + var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); + if (defined(boundingSphereIntersection)) { + sphereIntersections.push(tileData); + } + } + + sphereIntersections.sort(createComparePickTileFunction(ray.origin)); + + var intersection; + length = sphereIntersections.length; + for (i = 0; i < length; ++i) { + intersection = sphereIntersections[i].pick(ray, scene, true, result); + if (defined(intersection)) { + break; + } + } + + return intersection; + }; + + var scratchGetHeightCartesian = new Cartesian3(); + var scratchGetHeightIntersection = new Cartesian3(); + var scratchGetHeightCartographic = new Cartographic(); + var scratchGetHeightRay = new Ray(); + + /** + * Get the height of the quadtree at a given cartographic. + * + * @param {Cartographic} cartographic The cartographic for which to find the height. + * @returns {Number|undefined} The height of the cartographic or undefined if it could not be found. + */ + QuadtreePrimitive.prototype.getHeight = function(cartographic) { + //>>includeStart('debug', pragmas.debug); + if (!defined(cartographic)) { + throw new DeveloperError('cartographic is required'); + } + //>>includeEnd('debug'); + + var levelZeroTiles = this._levelZeroTiles; + if (!defined(levelZeroTiles)) { + return; + } + + var tile; + var i; + + var length = levelZeroTiles.length; + for (i = 0; i < length; ++i) { + tile = levelZeroTiles[i]; + if (Rectangle.contains(tile.rectangle, cartographic)) { + break; + } + } + + if (!defined(tile) || !Rectangle.contains(tile.rectangle, cartographic)) { + return undefined; + } + + while(tile.state === QuadtreeTileLoadState.DONE) { + var children = tile.children; + length = children.length; + + for (i = 0; i < length; ++i) { + tile = children[i]; + if (Rectangle.contains(tile.rectangle, cartographic)) { + break; + } + } + } + + while (defined(tile) && (!defined(tile.data) || !defined(tile.data.pickTerrain))) { + tile = tile.parent; + } + + var ellipsoid = this._tileProvider.tilingScheme.ellipsoid; + var cartesian = ellipsoid.cartographicToCartesian(cartographic, scratchGetHeightCartesian); + + var ray = scratchGetHeightRay; + Cartesian3.normalize(cartesian, ray.direction); + + var intersection = tile.data.pick(ray, undefined, false, scratchGetHeightIntersection); + if (!defined(intersection)) { + return undefined; + } + + return ellipsoid.cartesianToCartographic(intersection, scratchGetHeightCartographic).height; + }; + /** * Invokes a specified function for each {@link QuadtreeTile} that is partially * or completely loaded. From f448b38c5512a00f97cb606fc5194fe5aa4d8132 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Wed, 16 Jul 2014 16:49:17 -0400 Subject: [PATCH 70/74] Revert tilt behavior in columbus view to tilt about the center of the screen until the camera height is low enough to pick terrain. --- Source/Scene/ScreenSpaceCameraController.js | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 8203a8d9bc15..18cd56947485 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -260,6 +260,10 @@ define([ this._tiltCenter = new Cartesian3(); this._rotateMousePosition = new Cartesian2(); this._rotateStartPosition = new Cartesian3(); + this._tiltCVOffMap = false; + + var projection = scene.mapProjection; + this._maxCoord = projection.project(new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO)); // Constants, Make any of these public? this._zoomFactor = 5.0; @@ -611,6 +615,69 @@ define([ movement = movement.angleAndHeight; } + if (!Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) { + controller._tiltCVOffMap = false; + } + + var camera = controller._scene.camera; + var maxCoord = controller._maxCoord; + var onMap = Math.abs(camera.position.x) - maxCoord.x < 0 && Math.abs(camera.position.y) - maxCoord.y < 0; + + if (controller._tiltCVOffMap || !onMap || camera.position.z > controller.minimumPickingTerrainHeight) { + controller._tiltCVOffMap = true; + rotateCVOnPlane(controller, startPosition, movement, frameState); + } else { + rotateCVOnTerrain(controller, startPosition, movement, frameState); + } + } + + function rotateCVOnPlane(controller, startPosition, movement, frameState) { + var camera = controller._scene.camera; + var canvas = controller._scene.canvas; + + var windowPosition = rotateCVWindowPos; + windowPosition.x = canvas.clientWidth / 2; + windowPosition.y = canvas.clientHeight / 2; + var ray = camera.getPickRay(windowPosition, rotateCVWindowRay); + var normal = Cartesian3.UNIT_X; + + var position = ray.origin; + var direction = ray.direction; + var scalar = -Cartesian3.dot(normal, position) / Cartesian3.dot(normal, direction); + var center = Cartesian3.multiplyByScalar(direction, scalar, rotateCVCenter); + Cartesian3.add(position, center, center); + + var projection = controller._scene.mapProjection; + var ellipsoid = projection.ellipsoid; + + Cartesian3.fromElements(center.y, center.z, center.x, center); + var cart = projection.unproject(center, rotateCVCart); + ellipsoid.cartographicToCartesian(cart, center); + + var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, rotateCVTransform); + + var oldGlobe = controller._globe; + var oldEllipsoid = controller._ellipsoid; + controller._globe = undefined; + controller._ellipsoid = Ellipsoid.UNIT_SPHERE; + controller._rotateFactor = 1.0; + controller._rotateRateRangeAdjustment = 1.0; + + var oldTransform = Matrix4.clone(camera.transform, rotateCVOldTransform); + camera.setTransform(transform); + + rotate3D(controller, startPosition, movement, frameState, Cartesian3.UNIT_Z); + + camera.setTransform(oldTransform); + controller._globe = oldGlobe; + controller._ellipsoid = oldEllipsoid; + + var radius = oldEllipsoid.maximumRadius; + controller._rotateFactor = 1.0 / radius; + controller._rotateRateRangeAdjustment = radius; + } + + function rotateCVOnTerrain(controller, startPosition, movement, frameState) { var ellipsoid = controller._ellipsoid; var camera = controller._scene.camera; From 340af876649ac46661bac58ea555ae929a12a4a7 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 17 Jul 2014 16:45:53 -0400 Subject: [PATCH 71/74] Move terrain picking from the quadtree to the globe. --- Source/Scene/Globe.js | 142 +++++++++++++++++++++++++- Source/Scene/QuadtreePrimitive.js | 164 ------------------------------ 2 files changed, 140 insertions(+), 166 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 1e8d1a75b2f5..24d3acaeaa4f 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -11,6 +11,7 @@ define([ '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', + '../Core/DeveloperError', '../Core/destroyObject', '../Core/Ellipsoid', '../Core/EllipsoidTerrainProvider', @@ -19,11 +20,13 @@ define([ '../Core/Geometry', '../Core/GeometryAttribute', '../Core/Intersect', + '../Core/IntersectionTests', '../Core/loadImage', '../Core/Math', '../Core/Matrix4', '../Core/Occluder', '../Core/PrimitiveType', + '../Core/Ray', '../Core/Rectangle', '../Core/TerrainProvider', '../Core/Transforms', @@ -58,6 +61,7 @@ define([ defaultValue, defined, defineProperties, + DeveloperError, destroyObject, Ellipsoid, EllipsoidTerrainProvider, @@ -66,11 +70,13 @@ define([ Geometry, GeometryAttribute, Intersect, + IntersectionTests, loadImage, CesiumMath, Matrix4, Occluder, PrimitiveType, + Ray, Rectangle, TerrainProvider, Transforms, @@ -299,6 +305,21 @@ define([ } }); + function createComparePickTileFunction(rayOrigin) { + return function(a, b) { + var aDist = BoundingSphere.distanceSquaredTo(a.pickBoundingSphere, rayOrigin); + var bDist = BoundingSphere.distanceSquaredTo(b.pickBoundingSphere, rayOrigin); + + return aDist - bDist; + }; + } + + var scratchArray = []; + var scratchSphereIntersectionResult = { + start : 0.0, + stop : 0.0 + }; + /** * Find an intersection between a ray and the globe surface that was rendered. The ray must be given in world coordinates. * @@ -313,9 +334,68 @@ define([ * var intersection = globe.pick(ray, scene); */ Globe.prototype.pick = function(ray, scene, result) { - return this._surface.pick(ray, scene, result); + //>>includeStart('debug', pragmas.debug); + if (!defined(ray)) { + throw new DeveloperError('ray is required'); + } + if (!defined(scene)) { + throw new DeveloperError('scene is required'); + } + //>>includeEnd('debug'); + + var mode = scene.mode; + var projection = scene.mapProjection; + + var sphereIntersections = scratchArray; + sphereIntersections.length = 0; + + var tilesToRender = this._surface._tilesToRender; + var length = tilesToRender.length; + + var tile; + var i; + + for (i = 0; i < length; ++i) { + tile = tilesToRender[i]; + var tileData = tile.data; + + if (!defined(tileData)) { + continue; + } + + var boundingVolume = tileData.pickBoundingSphere; + if (mode !== SceneMode.SCENE3D) { + BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, projection, tileData.minimumHeight, tileData.maximumHeight, boundingVolume); + Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); + } else { + BoundingSphere.clone(tileData.boundingSphere3D, boundingVolume); + } + + var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); + if (defined(boundingSphereIntersection)) { + sphereIntersections.push(tileData); + } + } + + sphereIntersections.sort(createComparePickTileFunction(ray.origin)); + + var intersection; + length = sphereIntersections.length; + for (i = 0; i < length; ++i) { + intersection = sphereIntersections[i].pick(ray, scene, true, result); + if (defined(intersection)) { + break; + } + } + + return intersection; }; + var scratchGetHeightCartesian = new Cartesian3(); + var scratchGetHeightIntersection = new Cartesian3(); + var scratchGetHeightCartographic = new Cartographic(); + var scratchGetHeightRay = new Ray(); + /** * Get the height of the surface at a given cartographic. * @@ -323,7 +403,65 @@ define([ * @returns {Number|undefined} The height of the cartographic or undefined if it could not be found. */ Globe.prototype.getHeight = function(cartographic) { - return this._surface.getHeight(cartographic); + //>>includeStart('debug', pragmas.debug); + if (!defined(cartographic)) { + throw new DeveloperError('cartographic is required'); + } + //>>includeEnd('debug'); + + var levelZeroTiles = this._surface._levelZeroTiles; + if (!defined(levelZeroTiles)) { + return; + } + + var tile; + var i; + + var length = levelZeroTiles.length; + for (i = 0; i < length; ++i) { + tile = levelZeroTiles[i]; + if (Rectangle.contains(tile.rectangle, cartographic)) { + break; + } + } + + if (!defined(tile) || !Rectangle.contains(tile.rectangle, cartographic)) { + return undefined; + } + + //while(tile.state === QuadtreeTileLoadState.DONE) { + while (tile.renderable) { + var children = tile.children; + length = children.length; + + for (i = 0; i < length; ++i) { + tile = children[i]; + if (Rectangle.contains(tile.rectangle, cartographic)) { + break; + } + } + } + + while (defined(tile) && (!defined(tile.data) || !defined(tile.data.pickTerrain))) { + tile = tile.parent; + } + + if (!defined(tile)) { + return undefined; + } + + var ellipsoid = this._surface._tileProvider.tilingScheme.ellipsoid; + var cartesian = ellipsoid.cartographicToCartesian(cartographic, scratchGetHeightCartesian); + + var ray = scratchGetHeightRay; + Cartesian3.normalize(cartesian, ray.direction); + + var intersection = tile.data.pick(ray, undefined, false, scratchGetHeightIntersection); + if (!defined(intersection)) { + return undefined; + } + + return ellipsoid.cartesianToCartographic(intersection, scratchGetHeightCartographic).height; }; var depthQuadScratch = FeatureDetection.supportsTypedArrays() ? new Float32Array(12) : []; diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 4aee334f66e2..52ebb567bff5 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -1,6 +1,5 @@ /*global define*/ define([ - '../Core/BoundingSphere', '../Core/Cartesian3', '../Core/Cartographic', '../Core/defaultValue', @@ -9,18 +8,13 @@ define([ '../Core/DeveloperError', '../Core/EllipsoidalOccluder', '../Core/getTimestamp', - '../Core/IntersectionTests', '../Core/Queue', - '../Core/Ray', - '../Core/Rectangle', '../Core/Visibility', './QuadtreeOccluders', './QuadtreeTile', - './QuadtreeTileLoadState', './SceneMode', './TileReplacementQueue' ], function( - BoundingSphere, Cartesian3, Cartographic, defaultValue, @@ -29,14 +23,10 @@ define([ DeveloperError, EllipsoidalOccluder, getTimestamp, - IntersectionTests, Queue, - Ray, - Rectangle, Visibility, QuadtreeOccluders, QuadtreeTile, - QuadtreeTileLoadState, SceneMode, TileReplacementQueue) { "use strict"; @@ -164,160 +154,6 @@ define([ this._levelZeroTiles = undefined; }; - function createComparePickTileFunction(rayOrigin) { - return function(a, b) { - var aDist = BoundingSphere.distanceSquaredTo(a.pickBoundingSphere, rayOrigin); - var bDist = BoundingSphere.distanceSquaredTo(b.pickBoundingSphere, rayOrigin); - - return aDist - bDist; - }; - } - - var scratchArray = []; - var scratchSphereIntersectionResult = { - start : 0.0, - stop : 0.0 - }; - - /** - * Find an intersection between a ray and the quadtree that was rendered. - * - * @param {Ray} ray The ray to test for intersection. - * @param {Scene} scene The scene. - * @param {Cartesian3} [result] The object onto which to store the result. - * @returns {Cartesian3|undefined} The intersection of undefined if none was found. - * - * @example - * // find intersection of ray through a pixel and the quadtree - * var ray = scene.camera.getPickRay(windowCoordinates); - * var intersection = quadtree.pick(ray, scene); - */ - QuadtreePrimitive.prototype.pick = function(ray, scene, result) { - //>>includeStart('debug', pragmas.debug); - if (!defined(ray)) { - throw new DeveloperError('ray is required'); - } - if (!defined(scene)) { - throw new DeveloperError('scene is required'); - } - //>>includeEnd('debug'); - - var mode = scene.mode; - var projection = scene.mapProjection; - - var sphereIntersections = scratchArray; - sphereIntersections.length = 0; - - var tilesToRender = this._tilesToRender; - var length = tilesToRender.length; - - var tile; - var i; - - for (i = 0; i < length; ++i) { - tile = tilesToRender[i]; - var tileData = tile.data; - - if (!defined(tileData)) { - continue; - } - - var boundingVolume = tileData.pickBoundingSphere; - if (mode !== SceneMode.SCENE3D) { - BoundingSphere.fromRectangleWithHeights2D(tile.rectangle, projection, tileData.minimumHeight, tileData.maximumHeight, boundingVolume); - Cartesian3.fromElements(boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center); - } else { - BoundingSphere.clone(tileData.boundingSphere3D, boundingVolume); - } - - var boundingSphereIntersection = IntersectionTests.raySphere(ray, boundingVolume, scratchSphereIntersectionResult); - if (defined(boundingSphereIntersection)) { - sphereIntersections.push(tileData); - } - } - - sphereIntersections.sort(createComparePickTileFunction(ray.origin)); - - var intersection; - length = sphereIntersections.length; - for (i = 0; i < length; ++i) { - intersection = sphereIntersections[i].pick(ray, scene, true, result); - if (defined(intersection)) { - break; - } - } - - return intersection; - }; - - var scratchGetHeightCartesian = new Cartesian3(); - var scratchGetHeightIntersection = new Cartesian3(); - var scratchGetHeightCartographic = new Cartographic(); - var scratchGetHeightRay = new Ray(); - - /** - * Get the height of the quadtree at a given cartographic. - * - * @param {Cartographic} cartographic The cartographic for which to find the height. - * @returns {Number|undefined} The height of the cartographic or undefined if it could not be found. - */ - QuadtreePrimitive.prototype.getHeight = function(cartographic) { - //>>includeStart('debug', pragmas.debug); - if (!defined(cartographic)) { - throw new DeveloperError('cartographic is required'); - } - //>>includeEnd('debug'); - - var levelZeroTiles = this._levelZeroTiles; - if (!defined(levelZeroTiles)) { - return; - } - - var tile; - var i; - - var length = levelZeroTiles.length; - for (i = 0; i < length; ++i) { - tile = levelZeroTiles[i]; - if (Rectangle.contains(tile.rectangle, cartographic)) { - break; - } - } - - if (!defined(tile) || !Rectangle.contains(tile.rectangle, cartographic)) { - return undefined; - } - - while(tile.state === QuadtreeTileLoadState.DONE) { - var children = tile.children; - length = children.length; - - for (i = 0; i < length; ++i) { - tile = children[i]; - if (Rectangle.contains(tile.rectangle, cartographic)) { - break; - } - } - } - - while (defined(tile) && (!defined(tile.data) || !defined(tile.data.pickTerrain))) { - tile = tile.parent; - } - - var ellipsoid = this._tileProvider.tilingScheme.ellipsoid; - var cartesian = ellipsoid.cartographicToCartesian(cartographic, scratchGetHeightCartesian); - - var ray = scratchGetHeightRay; - Cartesian3.normalize(cartesian, ray.direction); - - var intersection = tile.data.pick(ray, undefined, false, scratchGetHeightIntersection); - if (!defined(intersection)) { - return undefined; - } - - return ellipsoid.cartesianToCartographic(intersection, scratchGetHeightCartographic).height; - }; - /** * Invokes a specified function for each {@link QuadtreeTile} that is partially * or completely loaded. From 0d3062976c41a81eefe079d58b6f79bcc214f04f Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 17 Jul 2014 19:58:25 -0400 Subject: [PATCH 72/74] Fix tilting wrong direction in 3D by using world coordinates. --- Source/Scene/ScreenSpaceCameraController.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 18cd56947485..1deba756c6b3 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -1197,8 +1197,8 @@ define([ var oldTransform = Matrix4.clone(camera.transform, tilt3DOldTransform); camera.setTransform(transform); - var tangent = Cartesian3.cross(Matrix4.getColumn(verticalTransform, 2, tilt3DNormal), Cartesian3.normalize(camera.position, tilt3DCartesian3), tilt3DCartesian3); - var dot = Cartesian3.dot(camera.right, tangent); + var tangent = Cartesian3.cross(verticalCenter, camera.positionWC, tilt3DCartesian3); + var dot = Cartesian3.dot(camera.rightWC, tangent); rotate3D(controller, startPosition, movement, frameState, constrainedAxis, false, true); From d4627b27af62b21713483629b81082be2cf3b316 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 17 Jul 2014 20:13:47 -0400 Subject: [PATCH 73/74] Make tilt in 3D like Columbus view in that it uses the new tilt on terrain when its close enough to pick the terrain. --- Source/Scene/ScreenSpaceCameraController.js | 70 +++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Source/Scene/ScreenSpaceCameraController.js b/Source/Scene/ScreenSpaceCameraController.js index 1deba756c6b3..3e25b15bd07a 100644 --- a/Source/Scene/ScreenSpaceCameraController.js +++ b/Source/Scene/ScreenSpaceCameraController.js @@ -1127,6 +1127,7 @@ define([ var tilt3DOldTransform = new Matrix4(); var tilt3DQuaternion = new Quaternion(); var tilt3DMatrix = new Matrix3(); + var tilt3DCart = new Cartographic(); function tilt3D(controller, startPosition, movement, frameState) { if (!Matrix4.equals(controller._scene.camera.transform, Matrix4.IDENTITY)) { @@ -1137,6 +1138,75 @@ define([ movement = movement.angleAndHeight; } + if (!Cartesian2.equals(startPosition, controller._tiltCenterMousePosition)) { + controller._tiltOnEllipsoid = false; + } + + var camera = controller._scene.camera; + var ellipsoid = controller._ellipsoid; + var cartographic = ellipsoid.cartesianToCartographic(camera.position, tilt3DCart); + + if (controller._tiltOnEllipsoid || cartographic.height > controller.minimumCollisionTerrainHeight) { + controller._tiltOnEllipsoid = true; + tilt3DOnEllipsoid(controller, startPosition, movement, frameState); + } else { + tilt3DOnTerrain(controller, startPosition, movement, frameState); + } + } + + function tilt3DOnEllipsoid(controller, startPosition, movement, frameState) { + var camera = controller._scene.camera; + var ellipsoid = controller._ellipsoid; + var minHeight = controller.minimumZoomDistance * 0.25; + var height = ellipsoid.cartesianToCartographic(camera.positionWC).height; + if (height - minHeight - 1.0 < CesiumMath.EPSILON3 && + movement.endPosition.y - movement.startPosition.y < 0) { + return; + } + + var windowPosition = tilt3DWindowPos; + windowPosition.x = controller._scene.canvas.clientWidth / 2; + windowPosition.y = controller._scene.canvas.clientHeight / 2; + var ray = camera.getPickRay(windowPosition, tilt3DRay); + + var center; + var intersection = IntersectionTests.rayEllipsoid(ray, ellipsoid); + if (defined(intersection)) { + center = Ray.getPoint(ray, intersection.start, tilt3DCenter); + } else { + var grazingAltitudeLocation = IntersectionTests.grazingAltitudeLocation(ray, ellipsoid); + if (!defined(grazingAltitudeLocation)) { + return; + } + var grazingAltitudeCart = ellipsoid.cartesianToCartographic(grazingAltitudeLocation, tilt3DCart); + grazingAltitudeCart.height = 0.0; + center = ellipsoid.cartographicToCartesian(grazingAltitudeCart, tilt3DCenter); + } + + var transform = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, tilt3DTransform); + + var oldGlobe = controller._globe; + var oldEllipsoid = controller._ellipsoid; + controller._globe = undefined; + controller._ellipsoid = Ellipsoid.UNIT_SPHERE; + controller._rotateFactor = 1.0; + controller._rotateRateRangeAdjustment = 1.0; + + var oldTransform = Matrix4.clone(camera.transform, tilt3DOldTransform); + camera.setTransform(transform); + + rotate3D(controller, startPosition, movement, frameState, Cartesian3.UNIT_Z); + + camera.setTransform(oldTransform); + controller._globe = oldGlobe; + controller._ellipsoid = oldEllipsoid; + + var radius = oldEllipsoid.maximumRadius; + controller._rotateFactor = 1.0 / radius; + controller._rotateRateRangeAdjustment = radius; + } + + function tilt3DOnTerrain(controller, startPosition, movement, frameState) { var camera = controller._scene.camera; var ellipsoid = controller._ellipsoid; From 3add858dc8ee6082b37c2e4b3d26ede38cd24e84 Mon Sep 17 00:00:00 2001 From: Dan Bagnell Date: Thu, 17 Jul 2014 20:24:04 -0400 Subject: [PATCH 74/74] Fix tests. --- CHANGES.md | 2 -- Specs/Scene/ScreenSpaceCameraControllerSpec.js | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 31f6e1ae6c01..9af6e98271d9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -97,8 +97,6 @@ Change Log Beta Releases ------------- - - ### b30 - 2014-07-01 * Breaking changes ([why so many?](https://groups.google.com/forum/#!topic/cesium-dev/Y_mG11IZD9k)) diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 531f925e53c7..1e44f6f231f2 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -51,9 +51,7 @@ defineSuite([ this.canvas = canvas; this.camera = camera; this.globe = undefined; - this.mapProjection = { - ellipsoid : ellipsoid - }; + this.mapProjection = new GeographicProjection(ellipsoid); }; beforeEach(function() {