diff --git a/AGICHANGES.md b/AGICHANGES.md
index 156bbfc6bb1..099b3e86a2d 100644
--- a/AGICHANGES.md
+++ b/AGICHANGES.md
@@ -6,6 +6,7 @@ Beta Releases
### b28 - 2014-05-01
+* Added support for non-convex `CustomSensorVolume` visualization.
* Added `FanGeometry`, `FanOutlineGeometry`, and `DynamicFan`. A Fan is defined by an origin and list of directions. This is useful for drawing static projected shapes such as azimuth elevation masks and body masks.
### b27 - 2014-04-01
diff --git a/Apps/Sandcastle/gallery/Sensors.html b/Apps/Sandcastle/gallery/Sensors.html
index 22ca86cce72..1c5ddc2aa13 100644
--- a/Apps/Sandcastle/gallery/Sensors.html
+++ b/Apps/Sandcastle/gallery/Sensors.html
@@ -69,7 +69,7 @@
require([
'Cesium', 'dijit/form/DropDownButton', 'dijit/DropDownMenu', 'dijit/MenuItem', 'dijit/form/HorizontalSlider', 'dijit/TitlePane'
], function(
- Cesium, DropDownButton, DropDownMenu, MenuItem, HorizontalSlider, TitlePane)
+ Cesium, DropDownButton, DropDownMenu, MenuItem, HorizontalSlider, TitlePane)
{
"use strict";
@@ -137,6 +137,52 @@
return customSensor;
}
+ function addFootprint(ellipsoid) {
+ var foot = new Cesium.CustomSensorVolume();
+
+ var directions = [];
+ directions.push({ clock : Cesium.Math.toRadians(0.0), cone : Cesium.Math.toRadians(14.03624347)});
+ directions.push({ clock : Cesium.Math.toRadians(74.3), cone : Cesium.Math.toRadians(32.98357092)});
+ directions.push({ clock : Cesium.Math.toRadians(80.54), cone : Cesium.Math.toRadians(37.23483398)});
+ directions.push({ clock : Cesium.Math.toRadians(90.0), cone : Cesium.Math.toRadians(37.77568431)});
+ directions.push({ clock : Cesium.Math.toRadians(101.31), cone : Cesium.Math.toRadians(37.41598862)});
+ directions.push({ clock : Cesium.Math.toRadians(116.56), cone : Cesium.Math.toRadians(29.20519037)});
+ directions.push({ clock : Cesium.Math.toRadians(180.0), cone : Cesium.Math.toRadians(14.03624347)});
+ directions.push({ clock : Cesium.Math.toRadians(247.2), cone : Cesium.Math.toRadians(34.11764003)});
+ directions.push({ clock : Cesium.Math.toRadians(251.56), cone : Cesium.Math.toRadians(44.67589157)});
+ directions.push({ clock : Cesium.Math.toRadians(253.96), cone : Cesium.Math.toRadians(46.12330271)});
+ directions.push({ clock : Cesium.Math.toRadians(259.63), cone : Cesium.Math.toRadians(46.19202903)});
+ directions.push({ clock : Cesium.Math.toRadians(262.87), cone : Cesium.Math.toRadians(45.21405547)});
+ directions.push({ clock : Cesium.Math.toRadians(262.4), cone : Cesium.Math.toRadians(37.09839406)});
+ directions.push({ clock : Cesium.Math.toRadians(266.47), cone : Cesium.Math.toRadians(45.42651157)});
+ directions.push({ clock : Cesium.Math.toRadians(270.0), cone : Cesium.Math.toRadians(45.42651157)});
+ directions.push({ clock : Cesium.Math.toRadians(270.97), cone : Cesium.Math.toRadians(36.40877457)});
+ directions.push({ clock : Cesium.Math.toRadians(272.79), cone : Cesium.Math.toRadians(45.70731937)});
+ directions.push({ clock : Cesium.Math.toRadians(278.33), cone : Cesium.Math.toRadians(45.98533395)});
+ directions.push({ clock : Cesium.Math.toRadians(281.89), cone : Cesium.Math.toRadians(36.0358894)});
+ directions.push({ clock : Cesium.Math.toRadians(282.99), cone : Cesium.Math.toRadians(45.0)});
+ directions.push({ clock : Cesium.Math.toRadians(286.99), cone : Cesium.Math.toRadians(43.26652934)});
+ directions.push({ clock : Cesium.Math.toRadians(291.8), cone : Cesium.Math.toRadians(33.97011942)});
+ directions.push({ clock : Cesium.Math.toRadians(293.3), cone : Cesium.Math.toRadians(41.50882857)});
+ directions.push({ clock : Cesium.Math.toRadians(300.96), cone : Cesium.Math.toRadians(36.0826946)});
+ directions.push({ clock : Cesium.Math.toRadians(319.18), cone : Cesium.Math.toRadians(19.9888568)});
+
+ foot.modelMatrix = getModelMatrix(ellipsoid);
+ foot.radius = 10000000.0;
+ foot.setDirections(directions);
+ foot.portionToDisplay = portion;
+
+ foot.material = Cesium.Material.fromType('Color');
+ foot.material.uniforms.color = new Cesium.Color(0.0, 1.0, 1.0, 0.5);
+
+ foot.ellipsoidHorizonSurfaceMaterial = Cesium.Material.fromType('Color');
+ foot.ellipsoidHorizonSurfaceMaterial.uniforms.color = new Cesium.Color(1.0, 0.0, 1.0, 0.5);
+
+ foot.domeSurfaceMaterial = Cesium.Material.fromType('Color');
+ foot.domeSurfaceMaterial.uniforms.color = new Cesium.Color(1.0, 1.0, 0.0, 0.5);
+ return foot;
+ }
+
function createUserInterface(viewer) {
var tp = new TitlePane({
title: 'Manipulate Sensor',
@@ -162,6 +208,9 @@
case 'Custom':
sensor = addCustomSensor(ellipsoid);
break;
+ case 'Foot':
+ sensor = addFootprint(ellipsoid);
+ break;
case 'Rectangular':
sensor = addRectangularSensor(ellipsoid);
}
@@ -192,6 +241,14 @@
}
}));
+ sensorMenu.addChild(new MenuItem({
+ label: 'Foot',
+ onClick: function() {
+ selection = 'Foot';
+ updateSensor();
+ }
+ }));
+
new DropDownButton({
label : 'Select a sensor',
dropDown: sensorMenu
diff --git a/Source/Core/SphericalPolygon.js b/Source/Core/SphericalPolygon.js
new file mode 100644
index 00000000000..f15ae2875f1
--- /dev/null
+++ b/Source/Core/SphericalPolygon.js
@@ -0,0 +1,405 @@
+/*global define*/
+define([
+ './freezeObject',
+ './defaultValue',
+ './defined',
+ './defineProperties',
+ './DeveloperError',
+ './Math',
+ './Cartesian3'
+ ], function(
+ freezeObject,
+ defaultValue,
+ defined,
+ defineProperties,
+ DeveloperError,
+ CesiumMath,
+ Cartesian3) {
+ "use strict";
+
+ var stride = 10;
+ var directionsOffset = 0;
+ var normalsOffset = 3;
+
+ /**
+ * A simple polygon on the unit sphere {S2}.
+ *
+ * @private
+ */
+ var SphericalPolygon = function(vertices) {
+ this._isConvex = undefined;
+ this._vertices = undefined;
+
+ this._referenceAxis = undefined;
+ this._referenceDistance = undefined;
+
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared = undefined;
+
+ if (defined(vertices)) {
+ this.vertices = vertices;
+ }
+
+ this._convexHull = [];
+ };
+
+ var chord21 = new Cartesian3();
+ var chord23 = new Cartesian3();
+ var average = new Cartesian3();
+
+ function computeCircumscribingConeFromThreePoints(p1, p2, p3, axis) {
+ chord21 = Cartesian3.subtract(p1, p2, chord21);
+ chord23 = Cartesian3.subtract(p3, p2, chord23);
+ axis = Cartesian3.normalize(Cartesian3.cross(chord23, chord21, axis), axis);
+ average = Cartesian3.divideByScalar(Cartesian3.add(Cartesian3.add(p1, p2, average), p3, average), 3.0, average);
+ var distance = Cartesian3.dot(average, axis);
+ return distance;
+ }
+
+ function computeCircumscribingConeFromTwoPoints(p1, p2, axis) {
+ axis = Cartesian3.divideByScalar(Cartesian3.add(p1, p2, axis), 2.0, axis);
+ var distance = Cartesian3.magnitude(axis);
+ axis = Cartesian3.normalize(axis, axis);
+ return distance;
+ }
+
+ var axis12 = new Cartesian3();
+ var axis23 = new Cartesian3();
+ var axis31 = new Cartesian3();
+
+ function computeMinimumBoundingConeFromThreePoints(p1, p2, p3, axis) {
+ var distance12 = computeCircumscribingConeFromTwoPoints(p1, p2, axis12);
+ var distance23 = computeCircumscribingConeFromTwoPoints(p2, p3, axis23);
+ var distance31 = computeCircumscribingConeFromTwoPoints(p3, p1, axis31);
+
+ if (distance12 <= distance23) {
+ if (distance12 <= distance31) { // cone12 is largest.
+ if (Cartesian3.dot(p3, axis12) >= distance12) { // p3 is inside cone12
+ axis = Cartesian3.clone(axis12, axis);
+ return distance12;
+ } else {
+ return computeCircumscribingConeFromThreePoints(p1, p2, p3, axis);
+ }
+ } else { // cone 31 is largest.
+ if (Cartesian3.dot(p2, axis31) >= distance31) { // p2 is inside cone31
+ axis = Cartesian3.clone(axis31, axis);
+ return distance31;
+ } else {
+ return computeCircumscribingConeFromThreePoints(p1, p2, p3, axis);
+ }
+ }
+ } else if (distance23 <= distance31) { // cone23 is largest.
+ if (Cartesian3.dot(p1, axis23) >= distance23) { // p1 is inside cone23
+ axis = Cartesian3.clone(axis23, axis);
+ return distance23;
+ } else {
+ return computeCircumscribingConeFromThreePoints(p1, p2, p3, axis);
+ }
+ } else { // cone31 is largest.
+ if (Cartesian3.dot(p2, axis31) >= distance31) { // p2 is inside cone31
+ axis = Cartesian3.clone(axis31, axis);
+ return distance31;
+ } else {
+ return computeCircumscribingConeFromThreePoints(p1, p2, p3, axis);
+ }
+ }
+ }
+
+ var lastDirection = new Cartesian3();
+ var lastNormal = new Cartesian3();
+ var direction = new Cartesian3();
+ var nextDirection = new Cartesian3();
+ var nextNormal = new Cartesian3();
+ var crossProduct = new Cartesian3();
+
+ SphericalPolygon.findConvexHull = function(floats, stride, directionsOffset, sign, initialIndex, finalIndex, hull) {
+ var numberOfVertices = floats.length / stride;
+
+ hull.length = 0;
+ if (initialIndex < finalIndex) {
+ for (var i = initialIndex; i <= finalIndex; ++i) {
+ hull.push(i);
+ }
+ } else {
+ for (var j = initialIndex; j < numberOfVertices; ++j) {
+ hull.push(j);
+ }
+ for (var jj = 0; jj <= finalIndex; ++jj) {
+ hull.push(jj);
+ }
+ }
+
+ var originalLength = hull.length;
+
+ var initialLength;
+ do {
+ initialLength = hull.length;
+ var previousIndex = initialLength - 1;
+ var index = 0;
+ var nextIndex = 1;
+ do {
+ var offset = hull[previousIndex % hull.length] * stride + directionsOffset;
+ lastDirection = Cartesian3.fromArray(floats, offset, lastDirection);
+ offset = hull[index % hull.length] * stride + directionsOffset;
+ direction = Cartesian3.fromArray(floats, offset, direction);
+ offset = hull[nextIndex % hull.length] * stride + directionsOffset;
+ nextDirection = Cartesian3.fromArray(floats, offset, nextDirection);
+
+ lastNormal = Cartesian3.cross(direction, lastDirection, lastNormal);
+ nextNormal = Cartesian3.cross(nextDirection, direction, nextNormal);
+
+ if (sign * Cartesian3.dot(Cartesian3.cross(lastNormal, nextNormal, crossProduct), direction) >= 0.0) {
+ previousIndex = index;
+ index = index + 1;
+ nextIndex = index + 1;
+ } else {
+ hull.splice(index, 1);
+ }
+ } while (index !== hull.length);
+ } while (hull.length !== initialLength);
+
+ var hole;
+ if (hull.length !== originalLength) {
+ hull.holes = [];
+ for (var k = 0; k < hull.length - 1; ++k) {
+ var current = hull[k];
+ var next = hull[k + 1];
+ var difference = (current < next) ? next - current : next + numberOfVertices - current;
+ if (difference > 1) {
+ hole = [];
+ SphericalPolygon.findConvexHull(floats, stride, directionsOffset, sign * -1.0, current, next, hole);
+ hull.holes.push(hole);
+ }
+ }
+
+ var firstIndex = hull[0];
+ var lastIndex = hull[hull.length - 1];
+ if (lastIndex === finalIndex && firstIndex !== initialIndex) {
+ hole = [];
+ SphericalPolygon.findConvexHull(floats, stride, directionsOffset, sign * -1.0, finalIndex, firstIndex, hole);
+ hull.holes.push(hole);
+ } else if (lastIndex !== finalIndex && firstIndex === initialIndex) {
+ hole = [];
+ SphericalPolygon.findConvexHull(floats, stride, directionsOffset, sign * -1.0, lastIndex, initialIndex, hole);
+ hull.holes.push(hole);
+ } else if (lastIndex !== finalIndex && firstIndex !== initialIndex) {
+ hole = [];
+ SphericalPolygon.findConvexHull(floats, stride, directionsOffset, sign * -1.0, lastIndex, firstIndex, hole);
+ hull.holes.push(hole);
+ }
+ }
+ };
+
+ var first = new Cartesian3();
+ var second = new Cartesian3();
+ var third = new Cartesian3();
+ var other = new Cartesian3();
+ var tempAxis = new Cartesian3();
+
+ SphericalPolygon.prototype.computeBoundingCone = function(convexHull) {
+ var length = convexHull.length;
+
+ for (var i = 0; i < length; ++i) {
+ var firstIndex = convexHull[i];
+ first = Cartesian3.fromArray(this._directionsNormalsAndBisectorsWithMagnitudeSquared, firstIndex * stride + directionsOffset, first);
+ for (var j = i + 1; j < length; ++j) {
+ var secondIndex = convexHull[j];
+ second = Cartesian3.fromArray(this._directionsNormalsAndBisectorsWithMagnitudeSquared, secondIndex * stride + directionsOffset, second);
+ for (var k = j + 1; k < length; ++k) {
+ var thirdIndex = convexHull[k];
+ third = Cartesian3.fromArray(this._directionsNormalsAndBisectorsWithMagnitudeSquared, thirdIndex * stride + directionsOffset, third);
+ var tempDistance = computeMinimumBoundingConeFromThreePoints(first, second, third, tempAxis);
+ for (var l = 0; l < length; ++l) {
+ if (l !== i && l !== j && l !== k) {
+ var otherIndex = convexHull[l];
+ other = Cartesian3.fromArray(this._directionsNormalsAndBisectorsWithMagnitudeSquared, otherIndex * stride + directionsOffset, other);
+ if (Cartesian3.dot(other, tempAxis) < tempDistance) {
+ break;
+ }
+ }
+ }
+ if (l === length) {
+ this._referenceAxis = Cartesian3.clone(tempAxis, this._referenceAxis);
+ this._referenceDistance = tempDistance;
+ }
+ }
+ }
+ }
+ };
+
+ SphericalPolygon.prototype.computeBoundingCone2 = function() {
+ var convexHull = this.convexHull;
+ var length = convexHull.length;
+
+ // Find the two vertices with the greatest half-angle as the initial bounding cone.
+ var index1 = -1;
+ var index2 = -1;
+ var distance = 1.0;
+ for (var i = 0; i < length; ++i) {
+ var firstIndex = convexHull[i];
+ first = Cartesian3.fromArray(this._directionsNormalsAndBisectorsWithMagnitudeSquared, firstIndex * stride + directionsOffset, first);
+ for (var j = i + 1; j < length; ++j) {
+ var secondIndex = convexHull[j];
+ second = Cartesian3.fromArray(this._directionsNormalsAndBisectorsWithMagnitudeSquared, secondIndex * stride + directionsOffset, second);
+ var tempDistance = computeCircumscribingConeFromTwoPoints(first, second, tempAxis);
+ if (tempDistance < distance) {
+ index1 = i;
+ index2 = j;
+ this._referenceAxis = Cartesian3.clone(tempAxis, this._referenceAxis);
+ this._referenceDistance = tempDistance;
+ distance = tempDistance;
+ }
+ }
+ }
+
+ // Form the set of vertices from the two vertices that define the initial bounding cone and the vertices which lay ouside.
+ var hull = [];
+ for (var k = 0; k < length; ++k) {
+ if (k === index1 || k === index2) {
+ hull.push(convexHull[k]);
+ } else {
+ var index = convexHull[k];
+ direction = Cartesian3.fromArray(this._directionsNormalsAndBisectorsWithMagnitudeSquared, index * stride + directionsOffset, direction);
+ var dotProduct = Cartesian3.dot(direction, this._referenceAxis);
+ if (dotProduct < this._referenceDistance) {
+ hull.push(index);
+ }
+ }
+ }
+
+ // If there are vertices outside the bounding cone, find the minimum bounding cone.
+ if (hull.length > 2) {
+ this.computeBoundingCone(hull);
+ }
+ };
+
+ var bisector = new Cartesian3();
+ var normal = new Cartesian3();
+ var initialNormal = new Cartesian3();
+ var finalDirection = new Cartesian3();
+
+ defineProperties(SphericalPolygon.prototype, {
+ /**
+ * Gets a value indicating whether the spherical polygon is convex.
+ *
+ * @memberof SphericalPolygon.prototype
+ * @type {Boolean}
+ */
+ isConvex : {
+ get: function() {
+ return this._isConvex;
+ }
+ },
+
+ /**
+ * Gets and sets the vertices which define the spherical polygon. The list of vertices should conform to the following restrictions:
+ *
+ * - Duplicate vertices are not allowed.
+ * - Consecutive vertices should be less than 180 degrees apart.
+ *
+ *
+ * @memberof SphericalPolygon.prototype
+ * @type {Array}
+ */
+ vertices : {
+ get: function() {
+ return this._vertices;
+ },
+ set : function(vertices) {
+ var length = vertices.length;
+ var size = length * 3;
+
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared = new Float32Array(3 * size + length);
+
+ var convexVertices = [];
+
+ this._isConvex = true;
+ finalDirection = Cartesian3.fromSpherical(vertices[length - 1], finalDirection);
+ lastDirection = Cartesian3.clone(finalDirection, lastDirection);
+
+ for (var index = 0; index < length; ++index) {
+ direction = Cartesian3.fromSpherical(vertices[index], direction);
+ bisector = Cartesian3.divideByScalar(Cartesian3.add(lastDirection, direction, bisector), 2.0, bisector);
+ normal = Cartesian3.normalize(Cartesian3.cross(direction, lastDirection, normal), normal);
+
+ if (index === 0) {
+ initialNormal = Cartesian3.clone(normal, initialNormal);
+ } else if (Cartesian3.dot(Cartesian3.cross(lastNormal, normal, crossProduct), lastDirection) < 0.0) {
+ this._isConvex = false;
+ }
+
+ var offset = index * 10;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset] = direction.x;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 1] = direction.y;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 2] = direction.z;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 3] = normal.x;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 4] = normal.y;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 5] = normal.z;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 6] = bisector.x;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 7] = bisector.y;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 8] = bisector.z;
+ this._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 9] = Cartesian3.magnitudeSquared(bisector);
+
+ lastDirection = Cartesian3.clone(direction, lastDirection);
+ lastNormal = Cartesian3.clone(normal, lastNormal);
+ }
+ if (Cartesian3.dot(Cartesian3.cross(lastNormal, initialNormal, crossProduct), finalDirection) < 0.0) {
+ this._isConvex = false;
+ }
+
+ this._vertices = vertices;
+ this._convexHull = [];
+ this._referenceAxis = undefined;
+ this._referenceDistance = undefined;
+ }
+ },
+
+ /**
+ * Gets the array of vertex indices which form the convex hull of this sherical polygon.
+ *
+ * @memberof SphericalPolygon.prototype
+ * @type {Array}
+ */
+ convexHull : {
+ get: function() {
+ if (defined(this._vertices) && this._convexHull.length === 0) {
+ SphericalPolygon.findConvexHull(this._directionsNormalsAndBisectorsWithMagnitudeSquared, stride, directionsOffset, 1.0, 0, this._vertices.length - 1, this._convexHull);
+ }
+ return this._convexHull;
+ }
+ },
+
+ /**
+ * Gets the reference axis for the spherical polygon.
+ * With the SphericalPolygon#referenceDistance, this axis defines the minimum bounding cone.
+ *
+ * @memberof SphericalPolygon.prototype
+ * @type {Cartesian3}
+ */
+ referenceAxis : {
+ get: function() {
+ if (!defined(this._referenceAxis) && this.convexHull.length > 0) {
+ this.computeBoundingCone2();
+ }
+ return this._referenceAxis;
+ }
+ },
+
+ /**
+ * Gets the reference distance for the spherical polygon.
+ * With the SphericalPolygon#referenceAxis, this distance defines the minimum bounding cone.
+ *
+ * @memberof SphericalPolygon.prototype
+ * @type {Number}
+ */
+ referenceDistance : {
+ get: function() {
+ if (!defined(this._referenceDistance) && this.convexHull.length > 0) {
+ this.computeBoundingCone2();
+ }
+ return this._referenceDistance;
+ }
+ }
+ });
+
+ return SphericalPolygon;
+});
diff --git a/Source/DynamicScene/DynamicConeVisualizerUsingCustomSensor.js b/Source/DynamicScene/DynamicConeVisualizerUsingCustomSensor.js
index 5aa7a66110b..7a600e05258 100644
--- a/Source/DynamicScene/DynamicConeVisualizerUsingCustomSensor.js
+++ b/Source/DynamicScene/DynamicConeVisualizerUsingCustomSensor.js
@@ -47,16 +47,25 @@ define([
spherical.magnitude = 1.0;
}
- function computeDirections(minimumClockAngle, maximumClockAngle, innerHalfAngle, outerHalfAngle, result) {
+ function computeDirections(sphericalPolygon, minimumClockAngle, maximumClockAngle, innerHalfAngle, outerHalfAngle, result) {
var n = innerHalfAngle ? 90 : 180; // number of divisions of a full circle.
var angleStep = CesiumMath.TWO_PI / n;
var angle;
var i = 0;
+
+ // Define the bounding cone for the custom sensor to improve performance.
+ sphericalPolygon._referenceAxis = new Cartesian3();
+ sphericalPolygon._referenceAxis = Cartesian3.clone(Cartesian3.UNIT_Z, sphericalPolygon._referenceAxis);
+ sphericalPolygon._referenceDistance = Math.cos(outerHalfAngle);
+
if (minimumClockAngle === 0.0 && maximumClockAngle === CesiumMath.TWO_PI) {
// No clock angle limits, so this is just a circle.
// There might be a hole but we're ignoring it for now.
angle = 0.0;
+ // Define the convex hull for the custom sensor to improve performance.
+ var convexHull = sphericalPolygon._convexHull;
for (i = 0; i < n; ++i) {
+ convexHull.push(i);
assignSpherical(i, result, angle, outerHalfAngle);
angle += angleStep;
}
@@ -331,7 +340,7 @@ define([
innerHalfAngle !== cone.innerHalfAngle ||
outerHalfAngle !== cone.outerHalfAngle) {
- cone.setDirections(computeDirections(minimumClockAngle, maximumClockAngle, innerHalfAngle, outerHalfAngle, cone._directionsScratch));
+ cone.setDirections(computeDirections(cone._sphericalPolygon, minimumClockAngle, maximumClockAngle, innerHalfAngle, outerHalfAngle, cone._directionsScratch));
cone.innerHalfAngle = innerHalfAngle;
cone.maximumClockAngle = maximumClockAngle;
cone.outerHalfAngle = outerHalfAngle;
diff --git a/Source/Scene/CustomSensorVolume.js b/Source/Scene/CustomSensorVolume.js
index 46ec5cfeada..cf769ae42cc 100644
--- a/Source/Scene/CustomSensorVolume.js
+++ b/Source/Scene/CustomSensorVolume.js
@@ -16,6 +16,7 @@ define([
'../Core/ComponentDatatype',
'../Core/PrimitiveType',
'../Core/BoundingSphere',
+ '../Core/SphericalPolygon',
'../Renderer/BufferUsage',
'../Renderer/BlendingState',
'../Renderer/DrawCommand',
@@ -36,6 +37,7 @@ define([
'../Shaders/EllipsoidHorizonFacetFS',
'../Shaders/EllipsoidHorizonFacetInsideFS',
'../Shaders/EllipsoidHorizonFacetOutsideFS',
+ './SphericalPolygonShaderSupport',
'./SceneMode'
], function(
defaultValue,
@@ -54,6 +56,7 @@ define([
ComponentDatatype,
PrimitiveType,
BoundingSphere,
+ SphericalPolygon,
BufferUsage,
BlendingState,
DrawCommand,
@@ -74,6 +77,7 @@ define([
EllipsoidHorizonFacetFS,
EllipsoidHorizonFacetInsideFS,
EllipsoidHorizonFacetOutsideFS,
+ SphericalPolygonShaderSupport,
SceneMode) {
"use strict";
@@ -92,6 +96,13 @@ define([
var numberOfFloatsPerHorizonCommand = numberOfVerticesPerHorizonCommand * numberOfFloatsPerVertex;
var numberOfFloatsPerDomeCommand = 3 * 4 * 3 * numberOfFloatsPerVertex; // (3 sides per command)(4 triangles/side)(3 vertices/triangle)(2 Cartesians/vertex)(3 floats/Cartesian)
+ var Crossing = function(r, cosine, sine, kind) {
+ this.r = Cartesian3.clone(r);
+ this.cosine = cosine;
+ this.sine = sine;
+ this.kind = kind;
+ };
+
/**
* DOC_TBA
*
@@ -109,20 +120,18 @@ define([
this._pickCommand = new DrawCommand();
this._ellipsoidHorizonSurfaceColorCommands = [];
this._ellipsoidHorizonSurfaceColorCommandsSource = [];
- this._domeColorCommands = [];
- this._domeColorCommandsSource = [];
+ this._domeColorCommand = new DrawCommand();
+ this._domeColorCommandSource = undefined;
this._ellipsoidHorizonSurfaceCommandsVertices = undefined;
this._ellipsoidHorizonSurfaceCommandsVertexArray = undefined;
this._ellipsoidHorizonSurfaceCommandsBuffer = undefined;
this._ellipsoidHorizonSurfaceColorCommandList = [];
- this._domeVertexCount = 0;
this._domeCommandsVertices = undefined;
this._domeCommandsVertexArray = undefined;
this._domeCommandsBuffer = undefined;
this._domeColorCommandList = [];
- this._completeDomeVertexCount = 0;
this._completeDomeBoundingVolumeMC = undefined;
this._boundingSphere = new BoundingSphere();
@@ -236,7 +245,6 @@ define([
this._sphericals = undefined;
this._sphericalsDirty = false;
- this._directionsNormalsAndBisectorsWithMagnitudeSquared = undefined;
this.setDirections(options.directions);
/**
@@ -409,6 +417,10 @@ define([
this._cameraIsInsideEllipsoidHorizonCone = undefined;
this._cameraIsInsideDome = undefined;
+ this._sphericalPolygon = new SphericalPolygon();
+ this._sensorGlsl = undefined;
+ this._sensorUniforms = undefined;
+
// These elements are for the scaled ellipsoid horizon cone.
this._p = undefined;
this._q = undefined;
@@ -419,7 +431,12 @@ define([
};
/**
- * DOC_TBA
+ * Sets the directions which define the vertices of the custom sensor volume.
+ * The list of vertices should conform to the following restrictions:
+ *
+ * - Duplicate vertices are not allowed.
+ * - Consecutive vertices should be less than 180 degrees apart.
+ *
*
* @memberof CustomSensorVolume
*
@@ -462,14 +479,20 @@ define([
return context.createVertexArray(attributes);
}
+ var axis = new Cartesian3();
+
function updateDefinitionDependentData(customSensorVolume, context) {
+ var sphericalPolygon = customSensorVolume._sphericalPolygon;
+ sphericalPolygon.vertices = customSensorVolume._sphericals;
+
+ customSensorVolume._sensorGlsl = SphericalPolygonShaderSupport.implicitSurfaceFunction(sphericalPolygon);
+ customSensorVolume._sensorUniforms = SphericalPolygonShaderSupport.uniforms(sphericalPolygon);
+
var sphericals = customSensorVolume._sphericals;
var length = sphericals.length;
var size = length * 3;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared = new Float32Array(3 * size + length);
customSensorVolume._ellipsoidHorizonSurfaceColorCommands = new Array(length + 1);
- customSensorVolume._domeColorCommands = new Array(length + 1);
var horizonVertices = new Float32Array(numberOfFloatsForCompleteHorizonPyramidalFrustumCommand + numberOfFloatsPerHorizonCommand * length); // vertices for each side.
customSensorVolume._ellipsoidHorizonSurfaceCommandsVertices = horizonVertices;
@@ -478,52 +501,20 @@ define([
var horizonVertexArray = makeVertexArray(customSensorVolume, context, horizonBuffer);
customSensorVolume._ellipsoidHorizonSurfaceCommandsVertexArray = horizonVertexArray;
- var numberOfFloatsForCompleteDomeCommand = length * 4 * 3 * numberOfFloatsPerVertex; // ("length" sides for command)(4 triangles/side)(3 vertices/triangle)(2 Cartesians/vertex)(3 floats/Cartesian)
- var domeVertices = new Float32Array(numberOfFloatsForCompleteDomeCommand + numberOfFloatsPerDomeCommand * length); // vertices for each side.
- customSensorVolume._domeCommandsVertices = domeVertices;
- var domeBuffer = context.createVertexBuffer(domeVertices, customSensorVolume.bufferUsage);
- customSensorVolume._domeCommandsBuffer = domeBuffer;
- var domeVertexArray = makeVertexArray(customSensorVolume, context, domeBuffer);
- customSensorVolume._domeCommandsVertexArray = domeVertexArray;
-
var directions = [];
var primitiveType = customSensorVolume.debugShowProxyGeometry ? PrimitiveType.LINES : customSensorVolume._frontFaceColorCommand.primitiveType;
- var last = Cartesian3.fromSpherical(sphericals[length - 1]);
for (var index = 0; index < length; ++index) {
var direction = Cartesian3.fromSpherical(sphericals[index]);
- var bisector = Cartesian3.divideByScalar(Cartesian3.add(last, direction), 2.0);
- var normal = Cartesian3.normalize(Cartesian3.cross(direction, last));
- last = direction;
-
directions.push(Cartesian3.clone(direction));
- var offset = index * 10;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset] = direction.x;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 1] = direction.y;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 2] = direction.z;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 3] = normal.x;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 4] = normal.y;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 5] = normal.z;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 6] = bisector.x;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 7] = bisector.y;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 8] = bisector.z;
- customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 9] = Cartesian3.magnitudeSquared(bisector);
-
var horizonCommand = new DrawCommand();
horizonCommand.primitiveType = primitiveType;
horizonCommand.owner = customSensorVolume;
horizonCommand.vertexArray = horizonVertexArray;
customSensorVolume._ellipsoidHorizonSurfaceColorCommands[index + 1] = horizonCommand;
customSensorVolume._ellipsoidHorizonSurfaceColorCommandsSource[index + 1] = RectangularPyramidSensorVolume;
-
- var domeCommand = new DrawCommand();
- domeCommand.primitiveType = primitiveType;
- domeCommand.owner = customSensorVolume;
- domeCommand.vertexArray = domeVertexArray;
- customSensorVolume._domeColorCommands[index + 1] = domeCommand;
- customSensorVolume._domeColorCommandsSource[index + 1] = RectangularPyramidSensorVolume;
}
var positions = new Float32Array(size);
@@ -586,14 +577,18 @@ define([
customSensorVolume._ellipsoidHorizonSurfaceColorCommands[0] = command;
customSensorVolume._ellipsoidHorizonSurfaceColorCommandsSource[0] = kDopImplicitSurfaceFunction(numberOfSidesForCompleteHorizonCommand);
- command = new DrawCommand();
- command.primitiveType = primitiveType;
- command.owner = customSensorVolume;
- command.vertexArray = domeVertexArray;
- customSensorVolume._domeColorCommands[0] = command;
- customSensorVolume._domeVertexCount = 0;
- customSensorVolume._completeDomeBoundingVolumeMC = updateDomeCommand(command, 0, customSensorVolume, context, directions, r);
- customSensorVolume._completeDomeVertexCount = customSensorVolume._domeVertexCount;
+ var numberOfFloatsForCompleteDomeCommand = length * 4 * 3 * numberOfFloatsPerVertex; // ("length" sides for command)(4 triangles/side)(3 vertices/triangle)(2 Cartesians/vertex)(3 floats/Cartesian)
+ var domeVertices = new Float32Array(numberOfFloatsForCompleteDomeCommand); // vertices for each side.
+ customSensorVolume._domeCommandsVertices = domeVertices;
+ var domeBuffer = context.createVertexBuffer(domeVertices, customSensorVolume.bufferUsage);
+ customSensorVolume._domeCommandsBuffer = domeBuffer;
+ var domeVertexArray = makeVertexArray(customSensorVolume, context, domeBuffer);
+ customSensorVolume._domeCommandsVertexArray = domeVertexArray;
+
+ customSensorVolume._domeColorCommand.primitiveType = primitiveType;
+ customSensorVolume._domeColorCommand.owner = customSensorVolume;
+ customSensorVolume._domeColorCommand.vertexArray = domeVertexArray;
+ customSensorVolume._completeDomeBoundingVolumeMC = updateDomeCommand(customSensorVolume._domeColorCommand, customSensorVolume, context, r);
var vertexBuffer = context.createVertexBuffer(vertices, customSensorVolume.bufferUsage);
return makeVertexArray(customSensorVolume, context, vertexBuffer);
@@ -1089,18 +1084,23 @@ define([
customSensorVolume._ellipsoidHorizonSurfaceCommandsBuffer.copyFromArrayView(vertices, Float32Array.BYTES_PER_ELEMENT * (numberOfFloatsForCompleteHorizonPyramidalFrustumCommand + numberOfFloatsPerHorizonCommand * index));
}
- function computeBoundingPyramidalFrustumVertices(directions, frontCenter, frontSides, backCenter, backSides, vertices) {
+ var previous = new Cartesian3();
+ var current = new Cartesian3();
+ var next = new Cartesian3();
+
+ function computeBoundingPyramidalFrustumVertices(directionsNormalsAndBisectorsWithMagnitudeSquared, directions, frontCenter, frontSides, backCenter, backSides, vertices) {
+ var stride = 10;
var length = directions.length;
var normals = [];
var k = -1;
- var last = directions[length - 1];
+ previous = Cartesian3.fromArray(directionsNormalsAndBisectorsWithMagnitudeSquared, directions[length - 1] * stride, previous);
var lastFront = frontSides[length - 1];
var lastBack = backSides[length - 1];
for (var index = 0; index < length; ++index) {
- var direction = directions[index];
+ current = Cartesian3.fromArray(directionsNormalsAndBisectorsWithMagnitudeSquared, directions[index] * stride, current);
var front = frontSides[index];
var back = backSides[index];
- n = Cartesian3.normalize(Cartesian3.cross(direction, last, n), n);
+ n = Cartesian3.normalize(Cartesian3.cross(current, previous, n), n);
normals.push(Cartesian3.clone(n));
Cartesian3.pack(front, vertices, ++k * 3);
Cartesian3.pack(n, vertices, ++k * 3);
@@ -1130,7 +1130,55 @@ define([
Cartesian3.pack(lastBack, vertices, ++k * 3);
Cartesian3.pack(n, vertices, ++k * 3);
- last = direction;
+ previous = Cartesian3.clone(current, previous);
+ lastFront = front;
+ lastBack = back;
+ }
+
+ return normals;
+ }
+
+ function computeBoundingPyramidalFrustumVerticesFromDirections(directions, frontCenter, frontSides, backCenter, backSides, vertices) {
+ var length = directions.length;
+ var normals = [];
+ var k = -1;
+ var lastIndex = length - 1;
+ var lastFront = frontSides[length - 1];
+ var lastBack = backSides[length - 1];
+ for (var index = 0; index < length; ++index) {
+ var front = frontSides[index];
+ var back = backSides[index];
+ n = Cartesian3.normalize(Cartesian3.cross(directions[index], directions[lastIndex], n), n);
+ normals.push(Cartesian3.clone(n));
+ Cartesian3.pack(front, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(back, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(lastBack, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(lastBack, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(lastFront, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(front, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+
+ n = Cartesian3.normalize(Cartesian3.cross(Cartesian3.cross(front, frontCenter), Cartesian3.cross(lastFront, frontCenter)), n);
+ Cartesian3.pack(lastFront, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(frontCenter, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(front, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ n = Cartesian3.normalize(Cartesian3.cross(Cartesian3.cross(lastBack, backCenter), Cartesian3.cross(back, backCenter)), n);
+ Cartesian3.pack(back, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(backCenter, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+ Cartesian3.pack(lastBack, vertices, ++k * 3);
+ Cartesian3.pack(n, vertices, ++k * 3);
+
+ lastIndex = index;
lastFront = front;
lastBack = back;
}
@@ -1174,63 +1222,68 @@ define([
var centerFront = new Cartesian3();
var centerBack = new Cartesian3();
- function computeDomeVertices(customSensorVolume, directions, radius, vertices) {
+ function computeDomeVertices(customSensor, directionsNormalsAndBisectorsWithMagnitudeSquared, directions, radius, vertices) {
+ var stride = 10;
+
+ d = Cartesian3.clone(customSensor._sphericalPolygon.referenceAxis, d);
+
var length = directions.length;
- d = Cartesian3.clone(Cartesian3.ZERO, d);
- for (var ii = 0; ii < length; ++ii) {
- d = Cartesian3.add(d, directions[ii], d);
- }
- d = Cartesian3.magnitudeSquared(d) > CesiumMath.EPSILON15 ? Cartesian3.normalize(d, d) : Cartesian3.clone(Cartesian3.UNIT_Z, d);
var minDot = 1.0;
+ var maxDot = -1.0;
+ var lastIndex = directions[length - 1];
for (var iii = 0; iii < length; ++iii) {
- minDot = Math.min(Cartesian3.dot(directions[iii], d), minDot);
+ var index = directions[iii];
+ previous = Cartesian3.fromArray(directionsNormalsAndBisectorsWithMagnitudeSquared, lastIndex * stride, previous);
+ current = Cartesian3.fromArray(directionsNormalsAndBisectorsWithMagnitudeSquared, index * stride, current);
+ minDot = Math.min(Cartesian3.dot(current, d), minDot);
+ bisector = Cartesian3.normalize(Cartesian3.add(previous, current, bisector), bisector);
+ maxDot = Math.max(Cartesian3.dot(bisector, d), maxDot);
+ lastIndex = index;
}
var fronts = [];
var backs = [];
- for ( var i = length - 2, j = length - 1, k = 0; k < length; i = j++, j = k++) {
- // PERFORMANCE_IDEA: We can avoid redundant operations for adjacent edges.
- var previous = directions[i];
- var temp = directions[j];
- var next = directions[k];
+ for ( var i = 0; i < length; ++i) {
+ current = Cartesian3.fromArray(directionsNormalsAndBisectorsWithMagnitudeSquared, directions[i] * stride, current);
- // Extend position so the volume encompasses the sensor's radius. Use the shorter bisector in the scaling.
- var factor = 2.0 / Math.min(Cartesian3.magnitude(Cartesian3.add(previous, temp)), Cartesian3.magnitude(Cartesian3.add(temp, next)));
- var dot = Cartesian3.dot(temp, d);
- fronts.push(Cartesian3.add(Cartesian3.multiplyByScalar(temp, radius * factor), Cartesian3.multiplyByScalar(d, radius * (minDot - factor * dot))));
- backs.push(Cartesian3.add(Cartesian3.multiplyByScalar(temp, radius * factor), Cartesian3.multiplyByScalar(d, radius * (1.0 - factor * dot))));
+ var dot = Cartesian3.dot(current, d);
+ if (dot === 0.0) {
+ fronts.push(Cartesian3.multiplyByScalar(current, radius));
+ backs.push(Cartesian3.add(Cartesian3.multiplyByScalar(current, radius), Cartesian3.multiplyByScalar(d, radius)));
+ } else {
+ fronts.push(Cartesian3.subtract(Cartesian3.multiplyByScalar(current, radius * maxDot / dot), Cartesian3.multiplyByScalar(d, radius * (maxDot - minDot))));
+ backs.push(Cartesian3.add(Cartesian3.multiplyByScalar(current, radius * maxDot / dot), Cartesian3.multiplyByScalar(d, radius * (1.0 - maxDot))));
+ }
}
centerFront = Cartesian3.multiplyByScalar(d, radius * minDot, centerFront);
centerBack = Cartesian3.multiplyByScalar(d, radius, centerBack);
- return computeBoundingPyramidalFrustumVertices(directions, centerFront, fronts, centerBack, backs, vertices);
+ return computeBoundingPyramidalFrustumVertices(directionsNormalsAndBisectorsWithMagnitudeSquared, directions, centerFront, fronts, centerBack, backs, vertices);
}
- function updateDomeCommand(command, index, customSensorVolume, context, directions, radius) {
- var numberOfVertices = directions.length * 4 * 3; // (sides)(4 triangles/side)(3 vertices/triangle)
+ function updateDomeCommand(command, customSensorVolume, context, radius) {
+ var convexHull = customSensorVolume._sphericalPolygon.convexHull;
+ var numberOfVertices = convexHull.length * 4 * 3; // (sides)(4 triangles/side)(3 vertices/triangle)
var numberOfFloats = numberOfVertices * numberOfFloatsPerVertex;
- var domeFloatOffset = customSensorVolume._domeVertexCount * numberOfFloatsPerVertex;
- var vertices = new Float32Array(customSensorVolume._domeCommandsVertices.buffer, Float32Array.BYTES_PER_ELEMENT * domeFloatOffset, numberOfFloats);
- var normals = computeDomeVertices(customSensorVolume, directions, radius, vertices);
- var uniforms = kDopUniforms(normals);
+ var vertices = new Float32Array(customSensorVolume._domeCommandsVertices.buffer, 0, numberOfFloats);
+ var normals = computeDomeVertices(customSensorVolume, customSensorVolume._sphericalPolygon._directionsNormalsAndBisectorsWithMagnitudeSquared, convexHull, radius, vertices);
+ var uniforms = customSensorVolume._sensorUniforms;
- command.offset = customSensorVolume._domeVertexCount;
+ command.offset = 0;
var boundingVolume = BoundingSphere.fromVertices(vertices, undefined, numberOfFloatsPerVertex, command.boundingVolume);
command.uniformMap = combine(combine(customSensorVolume._uniforms, customSensorVolume._domeSurfaceMaterial._uniforms), uniforms);
command.boundingVolume = BoundingSphere.transform(boundingVolume, customSensorVolume.modelMatrix, command.boundingVolume);
command.modelMatrix = customSensorVolume.modelMatrix;
- customSensorVolume._domeVertexCount += numberOfVertices;
- customSensorVolume._domeCommandsBuffer.copyFromArrayView(vertices, Float32Array.BYTES_PER_ELEMENT * domeFloatOffset);
+ customSensorVolume._domeCommandsBuffer.copyFromArrayView(vertices, 0);
if (command.count !== numberOfVertices) {
command.count = numberOfVertices;
- var glsl = kDopImplicitSurfaceFunction(normals.length);
- customSensorVolume._domeColorCommandsSource[index] = glsl;
+ var glsl = customSensorVolume._sensorGlsl;
var sensorDomeFS = customSensorVolume._cameraIsInsideDome ? SensorDomeInsideFS : SensorDomeOutsideFS;
@@ -1276,7 +1329,7 @@ define([
centerBack = Cartesian3.multiplyByScalar(d, radius, centerBack);
if (customSensorVolume.portionToDisplay === SensorVolumePortionToDisplay.COMPLETE) {
- return computeBoundingPyramidalFrustumVertices(directions, centerFront, fronts, centerBack, backs, vertices);
+ return computeBoundingPyramidalFrustumVerticesFromDirections(directions, centerFront, fronts, centerBack, backs, vertices);
} else if (customSensorVolume.showThroughEllipsoid || customSensorVolume.portionToDisplay === SensorVolumePortionToDisplay.ABOVE_ELLIPSOID_HORIZON) {
return computeBoundingPyramidalVertices(directions, centerBack, backs, vertices);
} else if (customSensorVolume.portionToDisplay === SensorVolumePortionToDisplay.BELOW_ELLIPSOID_HORIZON) {
@@ -1336,12 +1389,30 @@ define([
}
function renderCompleteDome(customSensorVolume, context, radius) {
- var command = customSensorVolume._domeColorCommands[0];
+ var command = customSensorVolume._domeColorCommand;
command.boundingVolume = BoundingSphere.transform(customSensorVolume._completeDomeBoundingVolumeMC, customSensorVolume.modelMatrix, command.boundingVolume);
command.modelMatrix = customSensorVolume.modelMatrix;
customSensorVolume._domeColorCommandList.push(command);
}
+ function angularSortUsingSineAndCosine(a, b) {
+ function computeSortValue(o) {
+ if (o.sine > 0.0) {
+ return -o.cosine - 1.0;
+ } else if (o.sine < 0.0) {
+ return o.cosine + 1.0;
+ } else if (o.cosine > 0.0) {
+ return -2.0;
+ } else if (o.cosine < 0.0) {
+ return 0.0;
+ } else {
+ throw new DeveloperError('Angle value is undefined (sine and cosine are both zero).');
+ }
+ }
+
+ return computeSortValue(a) - computeSortValue(b);
+ }
+
// Scratch variables...
var modelToWorld = new Matrix3();
var worldToModel = new Matrix3();
@@ -1362,8 +1433,13 @@ define([
var offCrossing = new Cartesian3();
var firstOnCrossing = new Cartesian3();
var firstOnCrossingDirection = new Cartesian3();
+ var firstOffCrossing = new Cartesian3();
var g = new Cartesian3();
var scaledQ = new Cartesian3();
+ var crossings = [];
+ var xAxis = new Cartesian3();
+ var yAxis = new Cartesian3();
+ var t = new Cartesian3();
function computeCrossings(customSensorVolume, context) {
modelToWorld = Matrix4.getRotation(customSensorVolume.modelMatrix, modelToWorld);
@@ -1402,12 +1478,20 @@ define([
var earthCenterIsInsideSensor = true;
var noLateralFacetsIntersectEllipsoidHorizonSurface = true;
+ var isConvex = customSensorVolume._sphericalPolygon.isConvex;
+ if (!isConvex) {
+ crossings.length = 0;
+ mostOrthogonalAxis = Cartesian3.mostOrthogonalAxis(qUnit, mostOrthogonalAxis);
+ xAxis = Cartesian3.normalize(Cartesian3.cross(mostOrthogonalAxis, qUnit, xAxis), xAxis);
+ yAxis = Cartesian3.normalize(Cartesian3.cross(qUnit, xAxis, yAxis), yAxis);
+ }
+
var count = 0;
var length = customSensorVolume._sphericals.length;
for (var index = 0; index < length; ++index) {
var offset = index * 10;
- direction = Cartesian3.fromArray(customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared, offset, direction);
- normal = Cartesian3.fromArray(customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared, offset + 3, normal);
+ direction = Cartesian3.fromArray(customSensorVolume._sphericalPolygon._directionsNormalsAndBisectorsWithMagnitudeSquared, offset, direction);
+ normal = Cartesian3.fromArray(customSensorVolume._sphericalPolygon._directionsNormalsAndBisectorsWithMagnitudeSquared, offset + 3, normal);
b = customSensorVolume.ellipsoid.transformPositionFromScaledSpace(Matrix3.multiplyByVector(modelToWorld, normal, b), b);
bUnit = Cartesian3.normalize(b, bUnit);
var cosineSigma = Cartesian3.dot(q, bUnit);
@@ -1431,8 +1515,8 @@ define([
s = customSensorVolume.ellipsoid.transformPositionFromScaledSpace(r, s);
v = Matrix3.multiplyByVector(worldToModel, Cartesian3.subtract(s, p, v), v);
d = Cartesian3.normalize(v, d);
- bisector = Cartesian3.fromArray(customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared, offset + 6, bisector);
- var bisectorMagnitudeSquared = customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 9];
+ bisector = Cartesian3.fromArray(customSensorVolume._sphericalPolygon._directionsNormalsAndBisectorsWithMagnitudeSquared, offset + 6, bisector);
+ var bisectorMagnitudeSquared = customSensorVolume._sphericalPolygon._directionsNormalsAndBisectorsWithMagnitudeSquared[offset + 9];
if ((customSensorVolume.portionToDisplay !== SensorVolumePortionToDisplay.COMPLETE || Cartesian3.magnitudeSquared(v) <= radiusSquared) &&
Cartesian3.dot(d, bisector) > bisectorMagnitudeSquared) {
if (!foundOffCrossing) {
@@ -1445,6 +1529,15 @@ define([
directions.push(Cartesian3.clone(d));
foundOnCrossing = true;
}
+
+ if (!isConvex) {
+ t = Cartesian3.normalize(Cartesian3.subtract(r, qUnit, t), t);
+ var cosineOn = Cartesian3.dot(t, xAxis);
+ var sineOn = Cartesian3.dot(t, yAxis);
+ var cOn = new Crossing(r, cosineOn, sineOn, 1);
+ crossings.push(cOn);
+ }
+
if (customSensorVolume.debugShowCrossingPoints) {
labelCollection.add({
position : v,
@@ -1454,15 +1547,10 @@ define([
} else if (foundOffCrossing) {
directions.push(Cartesian3.clone(direction));
}
- if (foundOnCrossing && foundOffCrossing) {
+ if (isConvex && foundOnCrossing && foundOffCrossing) {
var command = customSensorVolume._ellipsoidHorizonSurfaceColorCommands[count + 1];
updateHorizonCommand(count, command, customSensorVolume, context, offCrossing, onCrossing, worldToModel, p, q, qMagnitudeSquared, radius);
customSensorVolume._ellipsoidHorizonSurfaceColorCommandList.push(command);
- if (customSensorVolume.portionToDisplay !== SensorVolumePortionToDisplay.BELOW_ELLIPSOID_HORIZON) {
- var domeCommand = customSensorVolume._domeColorCommands[count + 1];
- updateDomeCommand(domeCommand, count + 1, customSensorVolume, context, directions, radius);
- customSensorVolume._domeColorCommandList.push(domeCommand);
- }
foundOnCrossing = false;
foundOffCrossing = false;
directions.length = 0;
@@ -1478,6 +1566,15 @@ define([
directions.push(Cartesian3.clone(d));
directions.push(Cartesian3.clone(direction));
foundOffCrossing = true;
+
+ if (!isConvex) {
+ t = Cartesian3.normalize(Cartesian3.subtract(r, qUnit, t), t);
+ var cosineOff = Cartesian3.dot(t, xAxis);
+ var sineOff = Cartesian3.dot(t, yAxis);
+ var cOff = new Crossing(r, cosineOff, sineOff, -1);
+ crossings.push(cOff);
+ }
+
if (customSensorVolume.debugShowCrossingPoints) {
labelCollection.add({
position : v,
@@ -1492,10 +1589,10 @@ define([
directions.push(Cartesian3.clone(direction));
}
}
- if (foundOnCrossingFirst && foundOffCrossing) {
+ if (isConvex && foundOnCrossingFirst && foundOffCrossing) {
for (var i = 0; i < firstOnCrossingIndex; ++i) {
var offset2 = i * 10;
- direction = Cartesian3.fromArray(customSensorVolume._directionsNormalsAndBisectorsWithMagnitudeSquared, offset2, direction);
+ direction = Cartesian3.fromArray(customSensorVolume._sphericalPolygon._directionsNormalsAndBisectorsWithMagnitudeSquared, offset2, direction);
directions.push(Cartesian3.clone(direction));
}
directions.push(Cartesian3.clone(firstOnCrossingDirection));
@@ -1503,13 +1600,47 @@ define([
updateHorizonCommand(count, hc, customSensorVolume, context, offCrossing, firstOnCrossing, worldToModel, p, q, qMagnitudeSquared, radius);
customSensorVolume._ellipsoidHorizonSurfaceColorCommandList.push(hc);
- if (customSensorVolume.portionToDisplay !== SensorVolumePortionToDisplay.BELOW_ELLIPSOID_HORIZON) {
- var dc = customSensorVolume._domeColorCommands[count + 1];
- updateDomeCommand(dc, count + 1, customSensorVolume, context, directions, radius);
- customSensorVolume._domeColorCommandList.push(dc);
+ ++count;
+ }
+
+ if (!isConvex && crossings.length > 0) {
+ crossings.sort(angularSortUsingSineAndCosine);
+ foundOnCrossing = false;
+ foundOffCrossing = false;
+ var foundOffCrossingFirst = false;
+ count = 0;
+ for (var j = 0; j < crossings.length; ++j) {
+ var c = crossings[j];
+ if (c.kind === -1) {
+ if (!foundOnCrossing) {
+ Cartesian3.clone(c.r, firstOffCrossing);
+ foundOffCrossingFirst = true;
+ } else {
+ Cartesian3.clone(c.r, offCrossing);
+ foundOffCrossing = true;
+ }
+ }
+ if (foundOnCrossing && foundOffCrossing) {
+ var command2 = customSensorVolume._ellipsoidHorizonSurfaceColorCommands[count + 1];
+ updateHorizonCommand(count, command2, customSensorVolume, context, offCrossing, onCrossing, worldToModel, p, q, qMagnitudeSquared, radius);
+ customSensorVolume._ellipsoidHorizonSurfaceColorCommandList.push(command2);
+ foundOnCrossing = false;
+ foundOffCrossing = false;
+
+ ++count;
+ }
+ if (c.kind === 1) {
+ Cartesian3.clone(c.r, onCrossing);
+ foundOnCrossing = true;
+ }
}
+ if (foundOffCrossingFirst && foundOnCrossing) {
+ var hc2 = customSensorVolume._ellipsoidHorizonSurfaceColorCommands[count + 1];
+ updateHorizonCommand(count, hc2, customSensorVolume, context, onCrossing, firstOffCrossing, worldToModel, p, q, qMagnitudeSquared, radius);
+ customSensorVolume._ellipsoidHorizonSurfaceColorCommandList.push(hc2);
- ++count;
+ ++count;
+ }
}
// PERFORMANCE_IDEA: Tighten the proxy geometry for the BELOW_ELLIPSOID_HORIZON case.
@@ -1523,8 +1654,7 @@ define([
renderCompleteEllipsoidHorizonSurface(customSensorVolume, context, radius, p, q, qMagnitudeSquared, oneOverQ, qUnit, modelToWorld, worldToModel);
}
}
- } else if (customSensorVolume.portionToDisplay !== SensorVolumePortionToDisplay.ABOVE_ELLIPSOID_HORIZON &&
- customSensorVolume.portionToDisplay !== SensorVolumePortionToDisplay.BELOW_ELLIPSOID_HORIZON) {
+ } else if (customSensorVolume.portionToDisplay !== SensorVolumePortionToDisplay.BELOW_ELLIPSOID_HORIZON) {
renderCompleteDome(customSensorVolume, context, radius);
}
}
@@ -1733,7 +1863,7 @@ define([
// Initial render state creation
if (definitionChanged || showThroughEllipsoidChanged ||
- (!defined(this._domeColorCommands)) ||
+ (!defined(this._domeColorCommand)) ||
(this._domeSurfaceIsTranslucent !== domeSurfaceIsTranslucent)) {
this._domeSurfaceIsTranslucent = domeSurfaceIsTranslucent;
@@ -1767,12 +1897,9 @@ define([
});
}
- var length2 = this._domeColorCommands.length;
- for (var index2 = 0; index2 < length2; ++index2) {
- var domeColorCommand = this._domeColorCommands[index2];
- domeColorCommand.renderState = domeSurfaceRenderState;
- domeColorCommand.pass = domeSurfaceIsTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
- }
+ var domeColorCommand = this._domeColorCommand;
+ domeColorCommand.renderState = domeSurfaceRenderState;
+ domeColorCommand.pass = domeSurfaceIsTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
}
if (!defined(this._frontFaceColorCommand.vertexArray)) {
@@ -1811,7 +1938,6 @@ define([
if (portionToDisplayChanged || definitionChanged || modelMatrixChanged) {
this._ellipsoidHorizonSurfaceColorCommandList.length = 0;
this._domeColorCommandList.length = 0;
- this._domeVertexCount = this._completeDomeVertexCount; // These vertices are reserved for rendering the complete dome.
computeCrossings(this, context);
}
@@ -1869,25 +1995,22 @@ define([
var sensorDomeFS = cameraIsInsideDome ? SensorDomeInsideFS : SensorDomeOutsideFS;
this._cameraIsInsideDome = cameraIsInsideDome;
- var ll = this._domeColorCommands.length;
- for (var ii = 0; ii < ll; ++ii) {
- var domeCommand = this._domeColorCommands[ii];
- var domeSource = this._domeColorCommandsSource[ii];
- if (domeSurfaceMaterialChanged) {
- domeCommand.uniformMap = combine(this._domeSurfaceMaterial._uniforms, domeCommand.uniformMap);
- }
-
- var fragmentShaderSource = createShaderSource({
- defines : [ this.debugShowProxyGeometry ? 'ONLY_WIRE_FRAME' : '',
- context.fragmentDepth ? 'WRITE_DEPTH' : '' ,
- this.showIntersection ? 'SHOW_INTERSECTION' : '',
- this.showThroughEllipsoid ? 'SHOW_THROUGH_ELLIPSOID' : '',
- SensorVolumePortionToDisplay.toString(this.portionToDisplay)],
- sources : [ShadersSensorVolume, domeSource, this._domeSurfaceMaterial.shaderSource, SensorDomeFS, sensorDomeFS]
- });
- domeCommand.primitiveType = primitiveType;
- domeCommand.shaderProgram = context.shaderCache.replaceShaderProgram(domeCommand.shaderProgram, CommonSensorVolumeVS, fragmentShaderSource, attributeLocations);
+ var domeCommand = this._domeColorCommand;
+ var domeSource = this._sensorGlsl;
+ if (domeSurfaceMaterialChanged) {
+ domeCommand.uniformMap = combine(this._domeSurfaceMaterial._uniforms, domeCommand.uniformMap);
}
+
+ var fragmentShaderSource = createShaderSource({
+ defines : [ this.debugShowProxyGeometry ? 'ONLY_WIRE_FRAME' : '',
+ context.fragmentDepth ? 'WRITE_DEPTH' : '' ,
+ this.showIntersection ? 'SHOW_INTERSECTION' : '',
+ this.showThroughEllipsoid ? 'SHOW_THROUGH_ELLIPSOID' : '',
+ SensorVolumePortionToDisplay.toString(this.portionToDisplay)],
+ sources : [ShadersSensorVolume, domeSource, this._domeSurfaceMaterial.shaderSource, SensorDomeFS, sensorDomeFS]
+ });
+ domeCommand.primitiveType = primitiveType;
+ domeCommand.shaderProgram = context.shaderCache.replaceShaderProgram(domeCommand.shaderProgram, CommonSensorVolumeVS, fragmentShaderSource, attributeLocations);
}
if (pass.render) {
@@ -1972,10 +2095,7 @@ define([
for (var index = 0; index < length; ++index) {
this._ellipsoidHorizonSurfaceColorCommands[index].shaderProgram = this._ellipsoidHorizonSurfaceColorCommands[index].shaderProgram && this._ellipsoidHorizonSurfaceColorCommands[index].shaderProgram.release();
}
- length = this._domeColorCommands.length;
- for (var index2 = 0; index2 < length; ++index2) {
- this._domeColorCommands[index2].shaderProgram = this._domeColorCommands[index2].shaderProgram && this._domeColorCommands[index2].shaderProgram.release();
- }
+ this._domeColorCommand.shaderProgram = this._domeColorCommand.shaderProgram && this._domeColorCommand.shaderProgram.release();
this._pickCommand.shaderProgram = this._pickCommand.shaderProgram && this._pickCommand.shaderProgram.release();
this._pickId = this._pickId && this._pickId.destroy();
return destroyObject(this);
diff --git a/Source/Scene/SphericalPolygonShaderSupport.js b/Source/Scene/SphericalPolygonShaderSupport.js
new file mode 100644
index 00000000000..f11a9199ac0
--- /dev/null
+++ b/Source/Scene/SphericalPolygonShaderSupport.js
@@ -0,0 +1,174 @@
+/*global define*/
+define([
+ '../Core/freezeObject',
+ '../Core/defaultValue',
+ '../Core/defined',
+ '../Core/defineProperties',
+ '../Core/DeveloperError',
+ '../Core/Math',
+ '../Core/Cartesian3',
+ '../Core/SphericalPolygon'
+ ], function(
+ freezeObject,
+ defaultValue,
+ defined,
+ defineProperties,
+ DeveloperError,
+ CesiumMath,
+ Cartesian3,
+ SphericalPolygon) {
+ "use strict";
+
+ var stride = 10;
+ var directionsOffset = 0;
+ var normalsOffset = 3;
+
+ /**
+ * @private
+ */
+ var SphericalPolygonShaderSupport = function() {};
+
+ function kDopFacetNormalName(i, j) {
+ return 'u_kDopFacetNormal_' + i + '_' + j;
+ }
+
+ function convexHullImplicitSurfaceFunction(vertices, name, sign) {
+ var length = vertices.length;
+ var glsl = '';
+ var result = '';
+ var oppositeSign = (sign === '+') ? '-' : '+';
+ for (var i = 0; i < length; ++i) {
+ var j = (i + 1 === length) ? 0 : i + 1;
+ var initialIndex = vertices[i];
+ var finalIndex = vertices[j];
+ var uniform = (initialIndex < finalIndex) ? sign + kDopFacetNormalName(initialIndex, finalIndex) : oppositeSign + kDopFacetNormalName(finalIndex, initialIndex);
+ if (i === 0) {
+ result += '\tfloat value = dot(displacement, ' + uniform + ');\n';
+ } else {
+ result += '\tvalue = max(value, dot(displacement, ' + uniform + '));\n';
+ }
+ }
+ glsl += '\nfloat ' + name + '(vec3 displacement)\n{\n' + result + '\treturn value;\n}\n';
+ return glsl;
+ }
+
+ function sphericalPolygonImplicitSurfaceFunction(hull, name, sign) {
+ var result = '';
+ if (defined(hull.holes)) {
+ var oppositeSign = (sign === '+') ? '-' : '+';
+ for (var h = 0; h < hull.holes.length; ++h) {
+ var functionName = name + '_' + h;
+ result += sphericalPolygonImplicitSurfaceFunction(hull.holes[h], functionName, oppositeSign);
+ }
+ }
+ result += convexHullImplicitSurfaceFunction(hull, name, sign);
+ return result;
+ }
+
+ function sphericalPolygonImplicitSurfaceFunctionUniforms(hull, uniforms) {
+ var result = '';
+ if (defined(hull.holes)) {
+ for (var h = 0; h < hull.holes.length; ++h) {
+ result += sphericalPolygonImplicitSurfaceFunctionUniforms(hull.holes[h], uniforms);
+ }
+ }
+ var length = hull.length;
+ for (var i = 0; i < length; ++i) {
+ var j = (i + 1 === length) ? 0 : i + 1;
+ var initialIndex = hull[i];
+ var finalIndex = hull[j];
+ var name = (initialIndex < finalIndex) ? kDopFacetNormalName(initialIndex, finalIndex) : kDopFacetNormalName(finalIndex, initialIndex);
+
+ if (!defined(uniforms[name])) {
+ uniforms[name] = true;
+ result += 'uniform vec3 ' + name + ';\n';
+ }
+ }
+ return result;
+ }
+
+ function aggregateFunction(hull, functionName, variableName) {
+ var result = '\tfloat ' + variableName + ' = ' + functionName + '(displacement);\n';
+ if (defined(hull.holes)) {
+ for (var i = 0; i < hull.holes.length; ++i) {
+ var name = functionName + '_' + i;
+ result += '\t' + variableName + ' = max(' + variableName + ', -' + name + '(displacement));\n';
+ var hole = hull.holes[i];
+ if (defined(hole.holes)) {
+ var variable = variableName + '_' + i;
+ for (var j = 0; j < hole.holes.length; ++j) {
+ var v = variable + '_' + j;
+ result += aggregateFunction(hole.holes[j], name + '_' + j, v);
+ result += '\t' + variableName + ' = min(' + variableName + ', ' + v + ');\n';
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ function returnNormal(normal) {
+ return function() {
+ return normal;
+ };
+ }
+
+ var lastDirection = new Cartesian3();
+ var offset = new Cartesian3();
+ var direction = new Cartesian3();
+
+ function addUniforms(floats, hull, uniforms) {
+ var numberOfVertices = floats.length / stride;
+
+ if (defined(hull.holes)) {
+ for (var h = 0; h < hull.holes.length; ++h) {
+ addUniforms(floats, hull.holes[h], uniforms);
+ }
+ }
+ var length = hull.length;
+ for (var i = 0; i < length; ++i) {
+ var j = (i + 1 === length) ? 0 : i + 1;
+ var initialIndex = hull[i];
+ var finalIndex = hull[j];
+
+ var offset = initialIndex * stride + directionsOffset;
+ lastDirection = Cartesian3.fromArray(floats, offset, lastDirection);
+ offset = finalIndex * stride + directionsOffset;
+ direction = Cartesian3.fromArray(floats, offset, direction);
+ var name = (initialIndex < finalIndex) ? kDopFacetNormalName(initialIndex, finalIndex) : kDopFacetNormalName(finalIndex, initialIndex);
+
+ if (!defined(uniforms[name])) {
+ var difference = (initialIndex < finalIndex) ? finalIndex - initialIndex : finalIndex + numberOfVertices - initialIndex;
+ if (difference === 1) {
+ uniforms[name] = (initialIndex < finalIndex) ? returnNormal(Cartesian3.fromArray(floats, finalIndex * stride + normalsOffset)) : returnNormal(Cartesian3.negate(Cartesian3.fromArray(floats, finalIndex * stride + normalsOffset)));
+ } else {
+ uniforms[name] = (initialIndex < finalIndex) ? returnNormal(Cartesian3.cross(direction, lastDirection)) : returnNormal(Cartesian3.cross(lastDirection, direction));
+ }
+ }
+ }
+ }
+
+ SphericalPolygonShaderSupport.uniforms = function(sphericalPolygon) {
+ var uniforms = {};
+ addUniforms(sphericalPolygon._directionsNormalsAndBisectorsWithMagnitudeSquared, sphericalPolygon.convexHull, uniforms);
+ return uniforms;
+ };
+
+ SphericalPolygonShaderSupport.implicitSurfaceFunction = function(sphericalPolygon) {
+ var convexHull = sphericalPolygon.convexHull;
+ var glsl = '\n';
+ // Emit uniforms.
+ var uniforms = {};
+ glsl += sphericalPolygonImplicitSurfaceFunctionUniforms(convexHull, uniforms);
+ // Emit convex hull funtions.
+ var functionName = 'convexHull';
+ var variableName = 'value';
+ glsl += sphericalPolygonImplicitSurfaceFunction(convexHull, functionName, '+');
+ // Emit implicit surface function.
+ var result = aggregateFunction(convexHull, functionName, variableName);
+ glsl += '\nfloat sensorSurfaceFunction(vec3 displacement)\n{\n' + result + '\treturn ' + variableName + ';\n}\n';
+ return glsl;
+ };
+
+ return SphericalPolygonShaderSupport;
+});
diff --git a/Source/Shaders/SensorVolume.glsl b/Source/Shaders/SensorVolume.glsl
index 0fd07ad2245..c319899fc4b 100644
--- a/Source/Shaders/SensorVolume.glsl
+++ b/Source/Shaders/SensorVolume.glsl
@@ -17,7 +17,7 @@ float getIntersectionWidth()
vec2 sensorCartesianToNormalizedPolarTextureCoordinates(float radius, vec3 point)
{
- // Maps (-180 to 180, -radius to +radius) coordinates both to the range [0.0, 1.0].
+ // Maps (-180 to 180, 0 to +radius) coordinates both to the range [0.0, 1.0].
return vec2(atan(point.y, point.x) * czm_oneOverTwoPi + 0.5, length(point) / radius);
}
diff --git a/Specs/Core/SphericalPolygonSpec.js b/Specs/Core/SphericalPolygonSpec.js
new file mode 100644
index 00000000000..f4c54cf9c6a
--- /dev/null
+++ b/Specs/Core/SphericalPolygonSpec.js
@@ -0,0 +1,203 @@
+/*global defineSuite*/
+defineSuite([
+ 'Core/SphericalPolygon',
+ 'Core/Cartesian3',
+ 'Core/Math'
+ ], function(
+ SphericalPolygon,
+ Cartesian3,
+ CesiumMath) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ it('default constructor has undefined properties', function() {
+ var s = new SphericalPolygon();
+ expect(s.isConvex).toEqual(undefined);
+ expect(s.vertices).toEqual(undefined);
+ expect(s.convexHull.length).toEqual(0);
+ expect(s.referenceAxis).toEqual(undefined);
+ expect(s.referenceDistance).toEqual(undefined);
+ });
+
+ it('constructor sets expected properties', function() {
+ var vertices = [];
+ vertices.push({ clock : CesiumMath.toRadians(-135.0), cone : CesiumMath.toRadians(60.0)});
+ vertices.push({ clock : CesiumMath.toRadians(-45.0), cone : CesiumMath.toRadians(60.0)});
+ vertices.push({ clock : CesiumMath.toRadians(45.0), cone : CesiumMath.toRadians(60.0)});
+ vertices.push({ clock : CesiumMath.toRadians(135.0), cone : CesiumMath.toRadians(60.0)});
+
+ var s = new SphericalPolygon(vertices);
+ expect(s.isConvex).toEqual(true);
+ expect(s.vertices).toEqual(vertices);
+ expect(s.convexHull.length).toEqual(4);
+ expect(s.convexHull[0]).toEqual(0);
+ expect(s.convexHull[1]).toEqual(1);
+ expect(s.convexHull[2]).toEqual(2);
+ expect(s.convexHull[3]).toEqual(3);
+ expect(s.referenceAxis).toEqual(Cartesian3.UNIT_Z);
+ expect(s.referenceDistance).toEqual(0.5);
+ });
+
+ it('changing vertices causes properties to be recomputed', function() {
+ var vertices = [];
+ vertices.push({ clock : CesiumMath.toRadians(-135.0), cone : CesiumMath.toRadians(60.0)});
+ vertices.push({ clock : CesiumMath.toRadians(-45.0), cone : CesiumMath.toRadians(60.0)});
+ vertices.push({ clock : CesiumMath.toRadians(45.0), cone : CesiumMath.toRadians(60.0)});
+ vertices.push({ clock : CesiumMath.toRadians(135.0), cone : CesiumMath.toRadians(60.0)});
+
+ var s = new SphericalPolygon(vertices);
+ expect(s.isConvex).toEqual(true);
+ expect(s.vertices).toEqual(vertices);
+ expect(s.convexHull.length).toEqual(4);
+ expect(s.convexHull[0]).toEqual(0);
+ expect(s.convexHull[1]).toEqual(1);
+ expect(s.convexHull[2]).toEqual(2);
+ expect(s.convexHull[3]).toEqual(3);
+ expect(s.referenceAxis).toEqual(Cartesian3.UNIT_Z);
+ expect(s.referenceDistance).toEqual(0.5);
+
+ vertices.length = 0;
+ vertices.push({ clock : CesiumMath.toRadians(135.0), cone : CesiumMath.toRadians(135.0)});
+ vertices.push({ clock : CesiumMath.toRadians(-135.0), cone : CesiumMath.toRadians(150.0)});
+ vertices.push({ clock : CesiumMath.toRadians(-45.0), cone : CesiumMath.toRadians(135.0)});
+ vertices.push({ clock : CesiumMath.toRadians(-135.0), cone : CesiumMath.toRadians(135.0)});
+ s.vertices = vertices;
+ expect(s.isConvex).toEqual(false);
+ expect(s.vertices).toEqual(vertices);
+ expect(s.convexHull.length).toEqual(3);
+ expect(s.convexHull[0]).toEqual(0);
+ expect(s.convexHull[1]).toEqual(2);
+ expect(s.convexHull[2]).toEqual(3);
+ expect(s.referenceAxis).toEqual(Cartesian3.negate(Cartesian3.UNIT_Z));
+ expect(s.referenceDistance).toEqualEpsilon(1.0 / Math.sqrt(2.0), CesiumMath.EPSILON7);
+ });
+
+ it('foot sensor produces correct results', function() {
+ var vertices = [];
+ vertices.push({ clock : CesiumMath.toRadians(0.0), cone : CesiumMath.toRadians(14.03624347)});
+ vertices.push({ clock : CesiumMath.toRadians(74.3), cone : CesiumMath.toRadians(32.98357092)});
+ vertices.push({ clock : CesiumMath.toRadians(80.54), cone : CesiumMath.toRadians(37.23483398)});
+ vertices.push({ clock : CesiumMath.toRadians(90.0), cone : CesiumMath.toRadians(37.77568431)});
+ vertices.push({ clock : CesiumMath.toRadians(101.31), cone : CesiumMath.toRadians(37.41598862)});
+ vertices.push({ clock : CesiumMath.toRadians(116.56), cone : CesiumMath.toRadians(29.20519037)});
+ vertices.push({ clock : CesiumMath.toRadians(180.0), cone : CesiumMath.toRadians(14.03624347)});
+ vertices.push({ clock : CesiumMath.toRadians(247.2), cone : CesiumMath.toRadians(34.11764003)});
+ vertices.push({ clock : CesiumMath.toRadians(251.56), cone : CesiumMath.toRadians(44.67589157)});
+ vertices.push({ clock : CesiumMath.toRadians(253.96), cone : CesiumMath.toRadians(46.12330271)});
+ vertices.push({ clock : CesiumMath.toRadians(259.63), cone : CesiumMath.toRadians(46.19202903)});
+ vertices.push({ clock : CesiumMath.toRadians(262.87), cone : CesiumMath.toRadians(45.21405547)});
+ vertices.push({ clock : CesiumMath.toRadians(262.4), cone : CesiumMath.toRadians(37.09839406)});
+ vertices.push({ clock : CesiumMath.toRadians(266.47), cone : CesiumMath.toRadians(45.42651157)});
+ vertices.push({ clock : CesiumMath.toRadians(270.0), cone : CesiumMath.toRadians(45.42651157)});
+ vertices.push({ clock : CesiumMath.toRadians(270.97), cone : CesiumMath.toRadians(36.40877457)});
+ vertices.push({ clock : CesiumMath.toRadians(272.79), cone : CesiumMath.toRadians(45.70731937)});
+ vertices.push({ clock : CesiumMath.toRadians(278.33), cone : CesiumMath.toRadians(45.98533395)});
+ vertices.push({ clock : CesiumMath.toRadians(281.89), cone : CesiumMath.toRadians(36.0358894)});
+ vertices.push({ clock : CesiumMath.toRadians(282.99), cone : CesiumMath.toRadians(45.0)});
+ vertices.push({ clock : CesiumMath.toRadians(286.99), cone : CesiumMath.toRadians(43.26652934)});
+ vertices.push({ clock : CesiumMath.toRadians(291.8), cone : CesiumMath.toRadians(33.97011942)});
+ vertices.push({ clock : CesiumMath.toRadians(293.3), cone : CesiumMath.toRadians(41.50882857)});
+ vertices.push({ clock : CesiumMath.toRadians(300.96), cone : CesiumMath.toRadians(36.0826946)});
+ vertices.push({ clock : CesiumMath.toRadians(319.18), cone : CesiumMath.toRadians(19.9888568)});
+
+ var s = new SphericalPolygon(vertices);
+ expect(s.isConvex).toEqual(false);
+ expect(s.vertices).toEqual(vertices);
+ expect(s.convexHull.length).toEqual(12);
+ expect(s.convexHull[0]).toEqual(1);
+ expect(s.convexHull[1]).toEqual(2);
+ expect(s.convexHull[2]).toEqual(3);
+ expect(s.convexHull[3]).toEqual(4);
+ expect(s.convexHull[4]).toEqual(5);
+ expect(s.convexHull[5]).toEqual(8);
+ expect(s.convexHull[6]).toEqual(9);
+ expect(s.convexHull[7]).toEqual(10);
+ expect(s.convexHull[8]).toEqual(17);
+ expect(s.convexHull[9]).toEqual(19);
+ expect(s.convexHull[10]).toEqual(22);
+ expect(s.convexHull[11]).toEqual(23);
+ expect(s.convexHull.holes.length).toEqual(5);
+
+ var hole = s.convexHull.holes[0];
+ expect(hole.length).toEqual(4);
+ expect(hole[0]).toEqual(5);
+ expect(hole[1]).toEqual(6);
+ expect(hole[2]).toEqual(7);
+ expect(hole[3]).toEqual(8);
+ expect(hole.holes).toEqual(undefined);
+
+ hole = s.convexHull.holes[1];
+ expect(hole.length).toEqual(4);
+ expect(hole[0]).toEqual(10);
+ expect(hole[1]).toEqual(12);
+ expect(hole[2]).toEqual(15);
+ expect(hole[3]).toEqual(17);
+ expect(hole.holes.length).toEqual(3);
+
+ var hull = hole.holes[0];
+ expect(hull.length).toEqual(3);
+ expect(hull[0]).toEqual(10);
+ expect(hull[1]).toEqual(11);
+ expect(hull[2]).toEqual(12);
+ expect(hull.holes).toEqual(undefined);
+
+ hull = hole.holes[1];
+ expect(hull.length).toEqual(4);
+ expect(hull[0]).toEqual(12);
+ expect(hull[1]).toEqual(13);
+ expect(hull[2]).toEqual(14);
+ expect(hull[3]).toEqual(15);
+ expect(hull.holes).toEqual(undefined);
+
+ hull = hole.holes[2];
+ expect(hull.length).toEqual(3);
+ expect(hull[0]).toEqual(15);
+ expect(hull[1]).toEqual(16);
+ expect(hull[2]).toEqual(17);
+ expect(hull.holes).toEqual(undefined);
+
+ hole = s.convexHull.holes[2];
+ expect(hole.length).toEqual(3);
+ expect(hole[0]).toEqual(17);
+ expect(hole[1]).toEqual(18);
+ expect(hole[2]).toEqual(19);
+ expect(hole.holes).toEqual(undefined);
+
+ hole = s.convexHull.holes[3];
+ expect(hole.length).toEqual(3);
+ expect(hole[0]).toEqual(19);
+ expect(hole[1]).toEqual(21);
+ expect(hole[2]).toEqual(22);
+ expect(hole.holes.length).toEqual(1);
+
+ hull = hole.holes[0];
+ expect(hull.length).toEqual(3);
+ expect(hull[0]).toEqual(19);
+ expect(hull[1]).toEqual(20);
+ expect(hull[2]).toEqual(21);
+ expect(hull.holes).toEqual(undefined);
+
+ hole = s.convexHull.holes[4];
+ expect(hole.length).toEqual(3);
+ expect(hole[0]).toEqual(23);
+ expect(hole[1]).toEqual(24);
+ expect(hole[2]).toEqual(1);
+ expect(hole.holes.length).toEqual(1);
+
+ hull = hole.holes[0];
+ expect(hull.length).toEqual(3);
+ expect(hull[0]).toEqual(24);
+ expect(hull[1]).toEqual(0);
+ expect(hull[2]).toEqual(1);
+ expect(hull.holes).toEqual(undefined);
+
+ var axis = new Cartesian3(
+ -0.011600696329179922,
+ -0.07289265444780975,
+ 0.9972723222732247
+ );
+
+ expect(s.referenceAxis).toEqualEpsilon(axis, CesiumMath.EPSILON7);
+ expect(s.referenceDistance).toEqualEpsilon(0.7436070769796277, CesiumMath.EPSILON7);
+ });
+});
diff --git a/Specs/Scene/CustomSensorVolumeSpec.js b/Specs/Scene/CustomSensorVolumeSpec.js
index ba93fbccdf3..bbf55814244 100644
--- a/Specs/Scene/CustomSensorVolumeSpec.js
+++ b/Specs/Scene/CustomSensorVolumeSpec.js
@@ -268,6 +268,56 @@ defineSuite([
expect(context.readPixels()).not.toEqual([0, 0, 0, 255]);
});
+ it('renders nonconvex volume', function() {
+ var camera = scene.camera;
+ camera.direction = Cartesian3.clone(Cartesian3.UNIT_Z, camera.direction);
+ camera.up = Cartesian3.clone(Cartesian3.UNIT_Y, camera.up);
+ var x = 1000000.0;
+ var y = 1000000.0;
+ var z = -(Ellipsoid.WGS84.radii.z + 10000000.0);
+ camera.position = new Cartesian3(x, y, z);
+
+ scene.render();
+ expect(context.readPixels()).toEqual([0, 0, 0, 255]);
+ var sensor = addSensor({
+ latitude : -90.0,
+ altitude : 9000000.0
+ });
+
+ var directions = [];
+ directions.push({ clock : CesiumMath.toRadians(0.0), cone : CesiumMath.toRadians(14.03624347)});
+ directions.push({ clock : CesiumMath.toRadians(74.3), cone : CesiumMath.toRadians(32.98357092)});
+ directions.push({ clock : CesiumMath.toRadians(80.54), cone : CesiumMath.toRadians(37.23483398)});
+ directions.push({ clock : CesiumMath.toRadians(90.0), cone : CesiumMath.toRadians(37.77568431)});
+ directions.push({ clock : CesiumMath.toRadians(101.31), cone : CesiumMath.toRadians(37.41598862)});
+ directions.push({ clock : CesiumMath.toRadians(116.56), cone : CesiumMath.toRadians(29.20519037)});
+ directions.push({ clock : CesiumMath.toRadians(180.0), cone : CesiumMath.toRadians(14.03624347)});
+ directions.push({ clock : CesiumMath.toRadians(247.2), cone : CesiumMath.toRadians(34.11764003)});
+ directions.push({ clock : CesiumMath.toRadians(251.56), cone : CesiumMath.toRadians(44.67589157)});
+ directions.push({ clock : CesiumMath.toRadians(253.96), cone : CesiumMath.toRadians(46.12330271)});
+ directions.push({ clock : CesiumMath.toRadians(259.63), cone : CesiumMath.toRadians(46.19202903)});
+ directions.push({ clock : CesiumMath.toRadians(262.87), cone : CesiumMath.toRadians(45.21405547)});
+ directions.push({ clock : CesiumMath.toRadians(262.4), cone : CesiumMath.toRadians(37.09839406)});
+ directions.push({ clock : CesiumMath.toRadians(266.47), cone : CesiumMath.toRadians(45.42651157)});
+ directions.push({ clock : CesiumMath.toRadians(270.0), cone : CesiumMath.toRadians(45.42651157)});
+ directions.push({ clock : CesiumMath.toRadians(270.97), cone : CesiumMath.toRadians(36.40877457)});
+ directions.push({ clock : CesiumMath.toRadians(272.79), cone : CesiumMath.toRadians(45.70731937)});
+ directions.push({ clock : CesiumMath.toRadians(278.33), cone : CesiumMath.toRadians(45.98533395)});
+ directions.push({ clock : CesiumMath.toRadians(281.89), cone : CesiumMath.toRadians(36.0358894)});
+ directions.push({ clock : CesiumMath.toRadians(282.99), cone : CesiumMath.toRadians(45.0)});
+ directions.push({ clock : CesiumMath.toRadians(286.99), cone : CesiumMath.toRadians(43.26652934)});
+ directions.push({ clock : CesiumMath.toRadians(291.8), cone : CesiumMath.toRadians(33.97011942)});
+ directions.push({ clock : CesiumMath.toRadians(293.3), cone : CesiumMath.toRadians(41.50882857)});
+ directions.push({ clock : CesiumMath.toRadians(300.96), cone : CesiumMath.toRadians(36.0826946)});
+ directions.push({ clock : CesiumMath.toRadians(319.18), cone : CesiumMath.toRadians(19.9888568)});
+
+ sensor.radius = 10000000.0;
+ sensor.setDirections(directions);
+
+ scene.render();
+ expect(context.readPixels()).not.toEqual([0, 0, 0, 255]);
+ });
+
it('does not render when show is false', function() {
var sensor = addSensor({
latitude : -90.0,