123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- import Cartesian3 from "./Cartesian3.js";
- import Cartesian4 from "./Cartesian4.js";
- import defaultValue from "./defaultValue.js";
- import defined from "./defined.js";
- import DeveloperError from "./DeveloperError.js";
- import Intersect from "./Intersect.js";
- import Plane from "./Plane.js";
- /**
- * The culling volume defined by planes.
- *
- * @alias CullingVolume
- * @constructor
- *
- * @param {Cartesian4[]} [planes] An array of clipping planes.
- */
- function CullingVolume(planes) {
- /**
- * Each plane is represented by a Cartesian4 object, where the x, y, and z components
- * define the unit vector normal to the plane, and the w component is the distance of the
- * plane from the origin.
- * @type {Cartesian4[]}
- * @default []
- */
- this.planes = defaultValue(planes, []);
- }
- const faces = [new Cartesian3(), new Cartesian3(), new Cartesian3()];
- Cartesian3.clone(Cartesian3.UNIT_X, faces[0]);
- Cartesian3.clone(Cartesian3.UNIT_Y, faces[1]);
- Cartesian3.clone(Cartesian3.UNIT_Z, faces[2]);
- const scratchPlaneCenter = new Cartesian3();
- const scratchPlaneNormal = new Cartesian3();
- const scratchPlane = new Plane(new Cartesian3(1.0, 0.0, 0.0), 0.0);
- /**
- * Constructs a culling volume from a bounding sphere. Creates six planes that create a box containing the sphere.
- * The planes are aligned to the x, y, and z axes in world coordinates.
- *
- * @param {BoundingSphere} boundingSphere The bounding sphere used to create the culling volume.
- * @param {CullingVolume} [result] The object onto which to store the result.
- * @returns {CullingVolume} The culling volume created from the bounding sphere.
- */
- CullingVolume.fromBoundingSphere = function (boundingSphere, result) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(boundingSphere)) {
- throw new DeveloperError("boundingSphere is required.");
- }
- //>>includeEnd('debug');
- if (!defined(result)) {
- result = new CullingVolume();
- }
- const length = faces.length;
- const planes = result.planes;
- planes.length = 2 * length;
- const center = boundingSphere.center;
- const radius = boundingSphere.radius;
- let planeIndex = 0;
- for (let i = 0; i < length; ++i) {
- const faceNormal = faces[i];
- let plane0 = planes[planeIndex];
- let plane1 = planes[planeIndex + 1];
- if (!defined(plane0)) {
- plane0 = planes[planeIndex] = new Cartesian4();
- }
- if (!defined(plane1)) {
- plane1 = planes[planeIndex + 1] = new Cartesian4();
- }
- Cartesian3.multiplyByScalar(faceNormal, -radius, scratchPlaneCenter);
- Cartesian3.add(center, scratchPlaneCenter, scratchPlaneCenter);
- plane0.x = faceNormal.x;
- plane0.y = faceNormal.y;
- plane0.z = faceNormal.z;
- plane0.w = -Cartesian3.dot(faceNormal, scratchPlaneCenter);
- Cartesian3.multiplyByScalar(faceNormal, radius, scratchPlaneCenter);
- Cartesian3.add(center, scratchPlaneCenter, scratchPlaneCenter);
- plane1.x = -faceNormal.x;
- plane1.y = -faceNormal.y;
- plane1.z = -faceNormal.z;
- plane1.w = -Cartesian3.dot(
- Cartesian3.negate(faceNormal, scratchPlaneNormal),
- scratchPlaneCenter
- );
- planeIndex += 2;
- }
- return result;
- };
- /**
- * Determines whether a bounding volume intersects the culling volume.
- *
- * @param {object} boundingVolume The bounding volume whose intersection with the culling volume is to be tested.
- * @returns {Intersect} Intersect.OUTSIDE, Intersect.INTERSECTING, or Intersect.INSIDE.
- */
- CullingVolume.prototype.computeVisibility = function (boundingVolume) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(boundingVolume)) {
- throw new DeveloperError("boundingVolume is required.");
- }
- //>>includeEnd('debug');
- const planes = this.planes;
- let intersecting = false;
- for (let k = 0, len = planes.length; k < len; ++k) {
- const result = boundingVolume.intersectPlane(
- Plane.fromCartesian4(planes[k], scratchPlane)
- );
- if (result === Intersect.OUTSIDE) {
- return Intersect.OUTSIDE;
- } else if (result === Intersect.INTERSECTING) {
- intersecting = true;
- }
- }
- return intersecting ? Intersect.INTERSECTING : Intersect.INSIDE;
- };
- /**
- * Determines whether a bounding volume intersects the culling volume.
- *
- * @param {object} boundingVolume The bounding volume whose intersection with the culling volume is to be tested.
- * @param {number} parentPlaneMask A bit mask from the boundingVolume's parent's check against the same culling
- * volume, such that if (planeMask & (1 << planeIndex) === 0), for k < 31, then
- * the parent (and therefore this) volume is completely inside plane[planeIndex]
- * and that plane check can be skipped.
- * @returns {number} A plane mask as described above (which can be applied to this boundingVolume's children).
- *
- * @private
- */
- CullingVolume.prototype.computeVisibilityWithPlaneMask = function (
- boundingVolume,
- parentPlaneMask
- ) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(boundingVolume)) {
- throw new DeveloperError("boundingVolume is required.");
- }
- if (!defined(parentPlaneMask)) {
- throw new DeveloperError("parentPlaneMask is required.");
- }
- //>>includeEnd('debug');
- if (
- parentPlaneMask === CullingVolume.MASK_OUTSIDE ||
- parentPlaneMask === CullingVolume.MASK_INSIDE
- ) {
- // parent is completely outside or completely inside, so this child is as well.
- return parentPlaneMask;
- }
- // Start with MASK_INSIDE (all zeros) so that after the loop, the return value can be compared with MASK_INSIDE.
- // (Because if there are fewer than 31 planes, the upper bits wont be changed.)
- let mask = CullingVolume.MASK_INSIDE;
- const planes = this.planes;
- for (let k = 0, len = planes.length; k < len; ++k) {
- // For k greater than 31 (since 31 is the maximum number of INSIDE/INTERSECTING bits we can store), skip the optimization.
- const flag = k < 31 ? 1 << k : 0;
- if (k < 31 && (parentPlaneMask & flag) === 0) {
- // boundingVolume is known to be INSIDE this plane.
- continue;
- }
- const result = boundingVolume.intersectPlane(
- Plane.fromCartesian4(planes[k], scratchPlane)
- );
- if (result === Intersect.OUTSIDE) {
- return CullingVolume.MASK_OUTSIDE;
- } else if (result === Intersect.INTERSECTING) {
- mask |= flag;
- }
- }
- return mask;
- };
- /**
- * For plane masks (as used in {@link CullingVolume#computeVisibilityWithPlaneMask}), this special value
- * represents the case where the object bounding volume is entirely outside the culling volume.
- *
- * @type {number}
- * @private
- */
- CullingVolume.MASK_OUTSIDE = 0xffffffff;
- /**
- * For plane masks (as used in {@link CullingVolume.prototype.computeVisibilityWithPlaneMask}), this value
- * represents the case where the object bounding volume is entirely inside the culling volume.
- *
- * @type {number}
- * @private
- */
- CullingVolume.MASK_INSIDE = 0x00000000;
- /**
- * For plane masks (as used in {@link CullingVolume.prototype.computeVisibilityWithPlaneMask}), this value
- * represents the case where the object bounding volume (may) intersect all planes of the culling volume.
- *
- * @type {number}
- * @private
- */
- CullingVolume.MASK_INDETERMINATE = 0x7fffffff;
- export default CullingVolume;
|