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