Occluder.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. import BoundingSphere from './BoundingSphere.js';
  2. import Cartesian3 from './Cartesian3.js';
  3. import defaultValue from './defaultValue.js';
  4. import defined from './defined.js';
  5. import defineProperties from './defineProperties.js';
  6. import DeveloperError from './DeveloperError.js';
  7. import Ellipsoid from './Ellipsoid.js';
  8. import CesiumMath from './Math.js';
  9. import Rectangle from './Rectangle.js';
  10. import Visibility from './Visibility.js';
  11. /**
  12. * Creates an Occluder derived from an object's position and radius, as well as the camera position.
  13. * The occluder can be used to determine whether or not other objects are visible or hidden behind the
  14. * visible horizon defined by the occluder and camera position.
  15. *
  16. * @alias Occluder
  17. *
  18. * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder.
  19. * @param {Cartesian3} cameraPosition The coordinate of the viewer/camera.
  20. *
  21. * @constructor
  22. *
  23. * @example
  24. * // Construct an occluder one unit away from the origin with a radius of one.
  25. * var cameraPosition = Cesium.Cartesian3.ZERO;
  26. * var occluderBoundingSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 1);
  27. * var occluder = new Cesium.Occluder(occluderBoundingSphere, cameraPosition);
  28. */
  29. function Occluder(occluderBoundingSphere, cameraPosition) {
  30. //>>includeStart('debug', pragmas.debug);
  31. if (!defined(occluderBoundingSphere)) {
  32. throw new DeveloperError('occluderBoundingSphere is required.');
  33. }
  34. if (!defined(cameraPosition)) {
  35. throw new DeveloperError('camera position is required.');
  36. }
  37. //>>includeEnd('debug');
  38. this._occluderPosition = Cartesian3.clone(occluderBoundingSphere.center);
  39. this._occluderRadius = occluderBoundingSphere.radius;
  40. this._horizonDistance = 0.0;
  41. this._horizonPlaneNormal = undefined;
  42. this._horizonPlanePosition = undefined;
  43. this._cameraPosition = undefined;
  44. // cameraPosition fills in the above values
  45. this.cameraPosition = cameraPosition;
  46. }
  47. var scratchCartesian3 = new Cartesian3();
  48. defineProperties(Occluder.prototype, {
  49. /**
  50. * The position of the occluder.
  51. * @memberof Occluder.prototype
  52. * @type {Cartesian3}
  53. */
  54. position: {
  55. get: function() {
  56. return this._occluderPosition;
  57. }
  58. },
  59. /**
  60. * The radius of the occluder.
  61. * @memberof Occluder.prototype
  62. * @type {Number}
  63. */
  64. radius: {
  65. get: function() {
  66. return this._occluderRadius;
  67. }
  68. },
  69. /**
  70. * The position of the camera.
  71. * @memberof Occluder.prototype
  72. * @type {Cartesian3}
  73. */
  74. cameraPosition: {
  75. set: function(cameraPosition) {
  76. //>>includeStart('debug', pragmas.debug);
  77. if (!defined(cameraPosition)) {
  78. throw new DeveloperError('cameraPosition is required.');
  79. }
  80. //>>includeEnd('debug');
  81. cameraPosition = Cartesian3.clone(cameraPosition, this._cameraPosition);
  82. var cameraToOccluderVec = Cartesian3.subtract(this._occluderPosition, cameraPosition, scratchCartesian3);
  83. var invCameraToOccluderDistance = Cartesian3.magnitudeSquared(cameraToOccluderVec);
  84. var occluderRadiusSqrd = this._occluderRadius * this._occluderRadius;
  85. var horizonDistance;
  86. var horizonPlaneNormal;
  87. var horizonPlanePosition;
  88. if (invCameraToOccluderDistance > occluderRadiusSqrd) {
  89. horizonDistance = Math.sqrt(invCameraToOccluderDistance - occluderRadiusSqrd);
  90. invCameraToOccluderDistance = 1.0 / Math.sqrt(invCameraToOccluderDistance);
  91. horizonPlaneNormal = Cartesian3.multiplyByScalar(cameraToOccluderVec, invCameraToOccluderDistance, scratchCartesian3);
  92. var nearPlaneDistance = horizonDistance * horizonDistance * invCameraToOccluderDistance;
  93. horizonPlanePosition = Cartesian3.add(cameraPosition, Cartesian3.multiplyByScalar(horizonPlaneNormal, nearPlaneDistance, scratchCartesian3), scratchCartesian3);
  94. } else {
  95. horizonDistance = Number.MAX_VALUE;
  96. }
  97. this._horizonDistance = horizonDistance;
  98. this._horizonPlaneNormal = horizonPlaneNormal;
  99. this._horizonPlanePosition = horizonPlanePosition;
  100. this._cameraPosition = cameraPosition;
  101. }
  102. }
  103. });
  104. /**
  105. * Creates an occluder from a bounding sphere and the camera position.
  106. *
  107. * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder.
  108. * @param {Cartesian3} cameraPosition The coordinate of the viewer/camera.
  109. * @param {Occluder} [result] The object onto which to store the result.
  110. * @returns {Occluder} The occluder derived from an object's position and radius, as well as the camera position.
  111. */
  112. Occluder.fromBoundingSphere = function(occluderBoundingSphere, cameraPosition, result) {
  113. //>>includeStart('debug', pragmas.debug);
  114. if (!defined(occluderBoundingSphere)) {
  115. throw new DeveloperError('occluderBoundingSphere is required.');
  116. }
  117. if (!defined(cameraPosition)) {
  118. throw new DeveloperError('camera position is required.');
  119. }
  120. //>>includeEnd('debug');
  121. if (!defined(result)) {
  122. return new Occluder(occluderBoundingSphere, cameraPosition);
  123. }
  124. Cartesian3.clone(occluderBoundingSphere.center, result._occluderPosition);
  125. result._occluderRadius = occluderBoundingSphere.radius;
  126. result.cameraPosition = cameraPosition;
  127. return result;
  128. };
  129. var tempVecScratch = new Cartesian3();
  130. /**
  131. * Determines whether or not a point, the <code>occludee</code>, is hidden from view by the occluder.
  132. *
  133. * @param {Cartesian3} occludee The point surrounding the occludee object.
  134. * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.
  135. *
  136. *
  137. * @example
  138. * var cameraPosition = new Cesium.Cartesian3(0, 0, 0);
  139. * var littleSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 0.25);
  140. * var occluder = new Cesium.Occluder(littleSphere, cameraPosition);
  141. * var point = new Cesium.Cartesian3(0, 0, -3);
  142. * occluder.isPointVisible(point); //returns true
  143. *
  144. * @see Occluder#computeVisibility
  145. */
  146. Occluder.prototype.isPointVisible = function(occludee) {
  147. if (this._horizonDistance !== Number.MAX_VALUE) {
  148. var tempVec = Cartesian3.subtract(occludee, this._occluderPosition, tempVecScratch);
  149. var temp = this._occluderRadius;
  150. temp = Cartesian3.magnitudeSquared(tempVec) - (temp * temp);
  151. if (temp > 0.0) {
  152. temp = Math.sqrt(temp) + this._horizonDistance;
  153. tempVec = Cartesian3.subtract(occludee, this._cameraPosition, tempVec);
  154. return temp * temp > Cartesian3.magnitudeSquared(tempVec);
  155. }
  156. }
  157. return false;
  158. };
  159. var occludeePositionScratch = new Cartesian3();
  160. /**
  161. * Determines whether or not a sphere, the <code>occludee</code>, is hidden from view by the occluder.
  162. *
  163. * @param {BoundingSphere} occludee The bounding sphere surrounding the occludee object.
  164. * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.
  165. *
  166. *
  167. * @example
  168. * var cameraPosition = new Cesium.Cartesian3(0, 0, 0);
  169. * var littleSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 0.25);
  170. * var occluder = new Cesium.Occluder(littleSphere, cameraPosition);
  171. * var bigSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -3), 1);
  172. * occluder.isBoundingSphereVisible(bigSphere); //returns true
  173. *
  174. * @see Occluder#computeVisibility
  175. */
  176. Occluder.prototype.isBoundingSphereVisible = function(occludee) {
  177. var occludeePosition = Cartesian3.clone(occludee.center, occludeePositionScratch);
  178. var occludeeRadius = occludee.radius;
  179. if (this._horizonDistance !== Number.MAX_VALUE) {
  180. var tempVec = Cartesian3.subtract(occludeePosition, this._occluderPosition, tempVecScratch);
  181. var temp = this._occluderRadius - occludeeRadius;
  182. temp = Cartesian3.magnitudeSquared(tempVec) - (temp * temp);
  183. if (occludeeRadius < this._occluderRadius) {
  184. if (temp > 0.0) {
  185. temp = Math.sqrt(temp) + this._horizonDistance;
  186. tempVec = Cartesian3.subtract(occludeePosition, this._cameraPosition, tempVec);
  187. return ((temp * temp) + (occludeeRadius * occludeeRadius)) > Cartesian3.magnitudeSquared(tempVec);
  188. }
  189. return false;
  190. }
  191. // Prevent against the case where the occludee radius is larger than the occluder's; since this is
  192. // an uncommon case, the following code should rarely execute.
  193. if (temp > 0.0) {
  194. tempVec = Cartesian3.subtract(occludeePosition, this._cameraPosition, tempVec);
  195. var tempVecMagnitudeSquared = Cartesian3.magnitudeSquared(tempVec);
  196. var occluderRadiusSquared = this._occluderRadius * this._occluderRadius;
  197. var occludeeRadiusSquared = occludeeRadius * occludeeRadius;
  198. if ((((this._horizonDistance * this._horizonDistance) + occluderRadiusSquared) * occludeeRadiusSquared) >
  199. (tempVecMagnitudeSquared * occluderRadiusSquared)) {
  200. // The occludee is close enough that the occluder cannot possible occlude the occludee
  201. return true;
  202. }
  203. temp = Math.sqrt(temp) + this._horizonDistance;
  204. return ((temp * temp) + occludeeRadiusSquared) > tempVecMagnitudeSquared;
  205. }
  206. // The occludee completely encompasses the occluder
  207. return true;
  208. }
  209. return false;
  210. };
  211. var tempScratch = new Cartesian3();
  212. /**
  213. * Determine to what extent an occludee is visible (not visible, partially visible, or fully visible).
  214. *
  215. * @param {BoundingSphere} occludeeBS The bounding sphere of the occludee.
  216. * @returns {Number} Visibility.NONE if the occludee is not visible,
  217. * Visibility.PARTIAL if the occludee is partially visible, or
  218. * Visibility.FULL if the occludee is fully visible.
  219. *
  220. *
  221. * @example
  222. * var sphere1 = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1.5), 0.5);
  223. * var sphere2 = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -2.5), 0.5);
  224. * var cameraPosition = new Cesium.Cartesian3(0, 0, 0);
  225. * var occluder = new Cesium.Occluder(sphere1, cameraPosition);
  226. * occluder.computeVisibility(sphere2); //returns Visibility.NONE
  227. *
  228. * @see Occluder#isVisible
  229. */
  230. Occluder.prototype.computeVisibility = function(occludeeBS) {
  231. //>>includeStart('debug', pragmas.debug);
  232. if (!defined(occludeeBS)) {
  233. throw new DeveloperError('occludeeBS is required.');
  234. }
  235. //>>includeEnd('debug');
  236. // If the occludee radius is larger than the occluders, this will return that
  237. // the entire ocludee is visible, even though that may not be the case, though this should
  238. // not occur too often.
  239. var occludeePosition = Cartesian3.clone(occludeeBS.center);
  240. var occludeeRadius = occludeeBS.radius;
  241. if (occludeeRadius > this._occluderRadius) {
  242. return Visibility.FULL;
  243. }
  244. if (this._horizonDistance !== Number.MAX_VALUE) {
  245. // The camera is outside the occluder
  246. var tempVec = Cartesian3.subtract(occludeePosition, this._occluderPosition, tempScratch);
  247. var temp = this._occluderRadius - occludeeRadius;
  248. var occluderToOccludeeDistSqrd = Cartesian3.magnitudeSquared(tempVec);
  249. temp = occluderToOccludeeDistSqrd - (temp * temp);
  250. if (temp > 0.0) {
  251. // The occludee is not completely inside the occluder
  252. // Check to see if the occluder completely hides the occludee
  253. temp = Math.sqrt(temp) + this._horizonDistance;
  254. tempVec = Cartesian3.subtract(occludeePosition, this._cameraPosition, tempVec);
  255. var cameraToOccludeeDistSqrd = Cartesian3.magnitudeSquared(tempVec);
  256. if (((temp * temp) + (occludeeRadius * occludeeRadius)) < cameraToOccludeeDistSqrd) {
  257. return Visibility.NONE;
  258. }
  259. // Check to see whether the occluder is fully or partially visible
  260. // when the occludee does not intersect the occluder
  261. temp = this._occluderRadius + occludeeRadius;
  262. temp = occluderToOccludeeDistSqrd - (temp * temp);
  263. if (temp > 0.0) {
  264. // The occludee does not intersect the occluder.
  265. temp = Math.sqrt(temp) + this._horizonDistance;
  266. return (cameraToOccludeeDistSqrd < ((temp * temp)) + (occludeeRadius * occludeeRadius)) ? Visibility.FULL : Visibility.PARTIAL;
  267. }
  268. //Check to see if the occluder is fully or partially visible when the occludee DOES
  269. //intersect the occluder
  270. tempVec = Cartesian3.subtract(occludeePosition, this._horizonPlanePosition, tempVec);
  271. return (Cartesian3.dot(tempVec, this._horizonPlaneNormal) > -occludeeRadius) ? Visibility.PARTIAL : Visibility.FULL;
  272. }
  273. }
  274. return Visibility.NONE;
  275. };
  276. var occludeePointScratch = new Cartesian3();
  277. /**
  278. * Computes a point that can be used as the occludee position to the visibility functions.
  279. * Use a radius of zero for the occludee radius. Typically, a user computes a bounding sphere around
  280. * an object that is used for visibility; however it is also possible to compute a point that if
  281. * seen/not seen would also indicate if an object is visible/not visible. This function is better
  282. * called for objects that do not move relative to the occluder and is large, such as a chunk of
  283. * terrain. You are better off not calling this and using the object's bounding sphere for objects
  284. * such as a satellite or ground vehicle.
  285. *
  286. * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder.
  287. * @param {Cartesian3} occludeePosition The point where the occludee (bounding sphere of radius 0) is located.
  288. * @param {Cartesian3[]} positions List of altitude points on the horizon near the surface of the occluder.
  289. * @returns {Object} An object containing two attributes: <code>occludeePoint</code> and <code>valid</code>
  290. * which is a boolean value.
  291. *
  292. * @exception {DeveloperError} <code>positions</code> must contain at least one element.
  293. * @exception {DeveloperError} <code>occludeePosition</code> must have a value other than <code>occluderBoundingSphere.center</code>.
  294. *
  295. * @example
  296. * var cameraPosition = new Cesium.Cartesian3(0, 0, 0);
  297. * var occluderBoundingSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -8), 2);
  298. * var occluder = new Cesium.Occluder(occluderBoundingSphere, cameraPosition);
  299. * var positions = [new Cesium.Cartesian3(-0.25, 0, -5.3), new Cesium.Cartesian3(0.25, 0, -5.3)];
  300. * var tileOccluderSphere = Cesium.BoundingSphere.fromPoints(positions);
  301. * var occludeePosition = tileOccluderSphere.center;
  302. * var occludeePt = Cesium.Occluder.computeOccludeePoint(occluderBoundingSphere, occludeePosition, positions);
  303. */
  304. Occluder.computeOccludeePoint = function(occluderBoundingSphere, occludeePosition, positions) {
  305. //>>includeStart('debug', pragmas.debug);
  306. if (!defined(occluderBoundingSphere)) {
  307. throw new DeveloperError('occluderBoundingSphere is required.');
  308. }
  309. if (!defined(positions)) {
  310. throw new DeveloperError('positions is required.');
  311. }
  312. if (positions.length === 0) {
  313. throw new DeveloperError('positions must contain at least one element');
  314. }
  315. //>>includeEnd('debug');
  316. var occludeePos = Cartesian3.clone(occludeePosition);
  317. var occluderPosition = Cartesian3.clone(occluderBoundingSphere.center);
  318. var occluderRadius = occluderBoundingSphere.radius;
  319. var numPositions = positions.length;
  320. //>>includeStart('debug', pragmas.debug);
  321. if (Cartesian3.equals(occluderPosition, occludeePosition)) {
  322. throw new DeveloperError('occludeePosition must be different than occluderBoundingSphere.center');
  323. }
  324. //>>includeEnd('debug');
  325. // Compute a plane with a normal from the occluder to the occludee position.
  326. var occluderPlaneNormal = Cartesian3.normalize(Cartesian3.subtract(occludeePos, occluderPosition, occludeePointScratch), occludeePointScratch);
  327. var occluderPlaneD = -(Cartesian3.dot(occluderPlaneNormal, occluderPosition));
  328. //For each position, determine the horizon intersection. Choose the position and intersection
  329. //that results in the greatest angle with the occcluder plane.
  330. var aRotationVector = Occluder._anyRotationVector(occluderPosition, occluderPlaneNormal, occluderPlaneD);
  331. var dot = Occluder._horizonToPlaneNormalDotProduct(occluderBoundingSphere, occluderPlaneNormal, occluderPlaneD, aRotationVector, positions[0]);
  332. if (!dot) {
  333. //The position is inside the mimimum radius, which is invalid
  334. return undefined;
  335. }
  336. var tempDot;
  337. for ( var i = 1; i < numPositions; ++i) {
  338. tempDot = Occluder._horizonToPlaneNormalDotProduct(occluderBoundingSphere, occluderPlaneNormal, occluderPlaneD, aRotationVector, positions[i]);
  339. if (!tempDot) {
  340. //The position is inside the minimum radius, which is invalid
  341. return undefined;
  342. }
  343. if (tempDot < dot) {
  344. dot = tempDot;
  345. }
  346. }
  347. //Verify that the dot is not near 90 degress
  348. if (dot < 0.00174532836589830883577820272085) {
  349. return undefined;
  350. }
  351. var distance = occluderRadius / dot;
  352. return Cartesian3.add(occluderPosition, Cartesian3.multiplyByScalar(occluderPlaneNormal, distance, occludeePointScratch), occludeePointScratch);
  353. };
  354. var computeOccludeePointFromRectangleScratch = [];
  355. /**
  356. * Computes a point that can be used as the occludee position to the visibility functions from a rectangle.
  357. *
  358. * @param {Rectangle} rectangle The rectangle used to create a bounding sphere.
  359. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine positions of the rectangle.
  360. * @returns {Object} An object containing two attributes: <code>occludeePoint</code> and <code>valid</code>
  361. * which is a boolean value.
  362. */
  363. Occluder.computeOccludeePointFromRectangle = function(rectangle, ellipsoid) {
  364. //>>includeStart('debug', pragmas.debug);
  365. if (!defined(rectangle)) {
  366. throw new DeveloperError('rectangle is required.');
  367. }
  368. //>>includeEnd('debug');
  369. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  370. var positions = Rectangle.subsample(rectangle, ellipsoid, 0.0, computeOccludeePointFromRectangleScratch);
  371. var bs = BoundingSphere.fromPoints(positions);
  372. // TODO: get correct ellipsoid center
  373. var ellipsoidCenter = Cartesian3.ZERO;
  374. if (!Cartesian3.equals(ellipsoidCenter, bs.center)) {
  375. return Occluder.computeOccludeePoint(new BoundingSphere(ellipsoidCenter, ellipsoid.minimumRadius), bs.center, positions);
  376. }
  377. return undefined;
  378. };
  379. var tempVec0Scratch = new Cartesian3();
  380. Occluder._anyRotationVector = function(occluderPosition, occluderPlaneNormal, occluderPlaneD) {
  381. var tempVec0 = Cartesian3.abs(occluderPlaneNormal, tempVec0Scratch);
  382. var majorAxis = tempVec0.x > tempVec0.y ? 0 : 1;
  383. if (((majorAxis === 0) && (tempVec0.z > tempVec0.x)) || ((majorAxis === 1) && (tempVec0.z > tempVec0.y))) {
  384. majorAxis = 2;
  385. }
  386. var tempVec = new Cartesian3();
  387. var tempVec1;
  388. if (majorAxis === 0) {
  389. tempVec0.x = occluderPosition.x;
  390. tempVec0.y = occluderPosition.y + 1.0;
  391. tempVec0.z = occluderPosition.z + 1.0;
  392. tempVec1 = Cartesian3.UNIT_X;
  393. } else if (majorAxis === 1) {
  394. tempVec0.x = occluderPosition.x + 1.0;
  395. tempVec0.y = occluderPosition.y;
  396. tempVec0.z = occluderPosition.z + 1.0;
  397. tempVec1 = Cartesian3.UNIT_Y;
  398. } else {
  399. tempVec0.x = occluderPosition.x + 1.0;
  400. tempVec0.y = occluderPosition.y + 1.0;
  401. tempVec0.z = occluderPosition.z;
  402. tempVec1 = Cartesian3.UNIT_Z;
  403. }
  404. var u = (Cartesian3.dot(occluderPlaneNormal, tempVec0) + occluderPlaneD) / -(Cartesian3.dot(occluderPlaneNormal, tempVec1));
  405. return Cartesian3.normalize(Cartesian3.subtract(Cartesian3.add(tempVec0, Cartesian3.multiplyByScalar(tempVec1, u, tempVec), tempVec0), occluderPosition, tempVec0), tempVec0);
  406. };
  407. var posDirectionScratch = new Cartesian3();
  408. Occluder._rotationVector = function(occluderPosition, occluderPlaneNormal, occluderPlaneD, position, anyRotationVector) {
  409. //Determine the angle between the occluder plane normal and the position direction
  410. var positionDirection = Cartesian3.subtract(position, occluderPosition, posDirectionScratch);
  411. positionDirection = Cartesian3.normalize(positionDirection, positionDirection);
  412. if (Cartesian3.dot(occluderPlaneNormal, positionDirection) < 0.99999998476912904932780850903444) {
  413. var crossProduct = Cartesian3.cross(occluderPlaneNormal, positionDirection, positionDirection);
  414. var length = Cartesian3.magnitude(crossProduct);
  415. if (length > CesiumMath.EPSILON13) {
  416. return Cartesian3.normalize(crossProduct, new Cartesian3());
  417. }
  418. }
  419. //The occluder plane normal and the position direction are colinear. Use any
  420. //vector in the occluder plane as the rotation vector
  421. return anyRotationVector;
  422. };
  423. var posScratch1 = new Cartesian3();
  424. var occluerPosScratch = new Cartesian3();
  425. var posScratch2 = new Cartesian3();
  426. var horizonPlanePosScratch = new Cartesian3();
  427. Occluder._horizonToPlaneNormalDotProduct = function(occluderBS, occluderPlaneNormal, occluderPlaneD, anyRotationVector, position) {
  428. var pos = Cartesian3.clone(position, posScratch1);
  429. var occluderPosition = Cartesian3.clone(occluderBS.center, occluerPosScratch);
  430. var occluderRadius = occluderBS.radius;
  431. //Verify that the position is outside the occluder
  432. var positionToOccluder = Cartesian3.subtract(occluderPosition, pos, posScratch2);
  433. var occluderToPositionDistanceSquared = Cartesian3.magnitudeSquared(positionToOccluder);
  434. var occluderRadiusSquared = occluderRadius * occluderRadius;
  435. if (occluderToPositionDistanceSquared < occluderRadiusSquared) {
  436. return false;
  437. }
  438. //Horizon parameters
  439. var horizonDistanceSquared = occluderToPositionDistanceSquared - occluderRadiusSquared;
  440. var horizonDistance = Math.sqrt(horizonDistanceSquared);
  441. var occluderToPositionDistance = Math.sqrt(occluderToPositionDistanceSquared);
  442. var invOccluderToPositionDistance = 1.0 / occluderToPositionDistance;
  443. var cosTheta = horizonDistance * invOccluderToPositionDistance;
  444. var horizonPlaneDistance = cosTheta * horizonDistance;
  445. positionToOccluder = Cartesian3.normalize(positionToOccluder, positionToOccluder);
  446. var horizonPlanePosition = Cartesian3.add(pos, Cartesian3.multiplyByScalar(positionToOccluder, horizonPlaneDistance, horizonPlanePosScratch), horizonPlanePosScratch);
  447. var horizonCrossDistance = Math.sqrt(horizonDistanceSquared - (horizonPlaneDistance * horizonPlaneDistance));
  448. //Rotate the position to occluder vector 90 degrees
  449. var tempVec = this._rotationVector(occluderPosition, occluderPlaneNormal, occluderPlaneD, pos, anyRotationVector);
  450. var horizonCrossDirection = Cartesian3.fromElements(
  451. (tempVec.x * tempVec.x * positionToOccluder.x) + ((tempVec.x * tempVec.y - tempVec.z) * positionToOccluder.y) + ((tempVec.x * tempVec.z + tempVec.y) * positionToOccluder.z),
  452. ((tempVec.x * tempVec.y + tempVec.z) * positionToOccluder.x) + (tempVec.y * tempVec.y * positionToOccluder.y) + ((tempVec.y * tempVec.z - tempVec.x) * positionToOccluder.z),
  453. ((tempVec.x * tempVec.z - tempVec.y) * positionToOccluder.x) + ((tempVec.y * tempVec.z + tempVec.x) * positionToOccluder.y) + (tempVec.z * tempVec.z * positionToOccluder.z),
  454. posScratch1);
  455. horizonCrossDirection = Cartesian3.normalize(horizonCrossDirection, horizonCrossDirection);
  456. //Horizon positions
  457. var offset = Cartesian3.multiplyByScalar(horizonCrossDirection, horizonCrossDistance, posScratch1);
  458. tempVec = Cartesian3.normalize(Cartesian3.subtract(Cartesian3.add(horizonPlanePosition, offset, posScratch2), occluderPosition, posScratch2), posScratch2);
  459. var dot0 = Cartesian3.dot(occluderPlaneNormal, tempVec);
  460. tempVec = Cartesian3.normalize(Cartesian3.subtract(Cartesian3.subtract(horizonPlanePosition, offset, tempVec), occluderPosition, tempVec), tempVec);
  461. var dot1 = Cartesian3.dot(occluderPlaneNormal, tempVec);
  462. return (dot0 < dot1) ? dot0 : dot1;
  463. };
  464. export default Occluder;