123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937 |
- import defaultValue from "../Core/defaultValue.js";
- import BoundingSphere from "../Core/BoundingSphere.js";
- import Cartesian2 from "../Core/Cartesian2.js";
- import Cartesian3 from "../Core/Cartesian3.js";
- import Check from "../Core/Check.js";
- import CesiumMath from "../Core/Math.js";
- import Matrix3 from "../Core/Matrix3.js";
- import Matrix4 from "../Core/Matrix4.js";
- import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
- import Cartesian4 from "../Core/Cartesian4.js";
- /**
- * A cylinder {@link VoxelShape}.
- *
- * @alias VoxelCylinderShape
- * @constructor
- *
- * @see VoxelShape
- * @see VoxelBoxShape
- * @see VoxelEllipsoidShape
- * @see VoxelShapeType
- *
- * @private
- */
- function VoxelCylinderShape() {
- /**
- * An oriented bounding box containing the bounded shape.
- * The update function must be called before accessing this value.
- * @type {OrientedBoundingBox}
- * @readonly
- */
- this.orientedBoundingBox = new OrientedBoundingBox();
- /**
- * A bounding sphere containing the bounded shape.
- * The update function must be called before accessing this value.
- * @type {BoundingSphere}
- * @readonly
- */
- this.boundingSphere = new BoundingSphere();
- /**
- * A transformation matrix containing the bounded shape.
- * The update function must be called before accessing this value.
- * @type {Matrix4}
- * @readonly
- */
- this.boundTransform = new Matrix4();
- /**
- * A transformation matrix containing the shape, ignoring the bounds.
- * The update function must be called before accessing this value.
- * @type {Matrix4}
- * @readonly
- */
- this.shapeTransform = new Matrix4();
- /**
- * @type {number}
- * @private
- */
- this._minimumRadius = VoxelCylinderShape.DefaultMinBounds.x;
- /**
- * @type {number}
- * @private
- */
- this._maximumRadius = VoxelCylinderShape.DefaultMaxBounds.x;
- /**
- * @type {number}
- * @private
- */
- this._minimumHeight = VoxelCylinderShape.DefaultMinBounds.y;
- /**
- * @type {number}
- * @private
- */
- this._maximumHeight = VoxelCylinderShape.DefaultMaxBounds.y;
- /**
- * @type {number}
- * @private
- */
- this._minimumAngle = VoxelCylinderShape.DefaultMinBounds.z;
- /**
- * @type {number}
- * @private
- */
- this._maximumAngle = VoxelCylinderShape.DefaultMaxBounds.z;
- /**
- * @type {Object<string, any>}
- * @readonly
- */
- this.shaderUniforms = {
- cylinderUvToRenderBoundsScale: new Cartesian3(),
- cylinderUvToRenderBoundsTranslate: new Cartesian3(),
- cylinderUvToRenderRadiusMin: 0.0,
- cylinderRenderAngleMinMax: new Cartesian2(),
- cylinderUvToShapeUvRadius: new Cartesian2(),
- cylinderUvToShapeUvHeight: new Cartesian2(),
- cylinderUvToShapeUvAngle: new Cartesian2(),
- cylinderShapeUvAngleMinMax: new Cartesian2(),
- cylinderShapeUvAngleRangeZeroMid: 0.0,
- };
- /**
- * @type {Object<string, any>}
- * @readonly
- */
- this.shaderDefines = {
- CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MIN: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MAX: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_RADIUS_FLAT: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_HEIGHT: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_HEIGHT_FLAT: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_ANGLE: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_EQUAL_ZERO: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_UNDER_HALF: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_EQUAL_HALF: undefined,
- CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_OVER_HALF: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_RADIUS: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_RADIUS_FLAT: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_HEIGHT: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_HEIGHT_FLAT: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_ANGLE: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_RANGE_EQUAL_ZERO: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_MIN_DISCONTINUITY: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_MAX_DISCONTINUITY: undefined,
- CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_MIN_MAX_REVERSED: undefined,
- CYLINDER_INTERSECTION_INDEX_RADIUS_MAX: undefined,
- CYLINDER_INTERSECTION_INDEX_RADIUS_MIN: undefined,
- CYLINDER_INTERSECTION_INDEX_ANGLE: undefined,
- };
- /**
- * The maximum number of intersections against the shape for any ray direction.
- * @type {number}
- * @readonly
- */
- this.shaderMaximumIntersectionsLength = 0; // not known until update
- }
- const scratchScale = new Cartesian3();
- const scratchBoundsTranslation = new Cartesian3();
- const scratchBoundsScale = new Cartesian3();
- const scratchBoundsScaleMatrix = new Matrix3();
- const scratchTransformLocalToBounds = new Matrix4();
- const scratchTransformUvToBounds = new Matrix4();
- const transformUvToLocal = Matrix4.fromRotationTranslation(
- Matrix3.fromUniformScale(2.0, new Matrix3()),
- new Cartesian3(-1.0, -1.0, -1.0),
- new Matrix4()
- );
- /**
- * Update the shape's state.
- *
- * @param {Matrix4} modelMatrix The model matrix.
- * @param {Cartesian3} minBounds The minimum bounds.
- * @param {Cartesian3} maxBounds The maximum bounds.
- * @param {Cartesian3} [clipMinBounds=VoxelCylinderShape.DefaultMinBounds] The minimum clip bounds.
- * @param {Cartesian3} [clipMaxBounds=VoxelCylinderShape.DefaultMaxBounds] The maximum clip bounds.
- * @returns {boolean} Whether the shape is visible.
- */
- VoxelCylinderShape.prototype.update = function (
- modelMatrix,
- minBounds,
- maxBounds,
- clipMinBounds,
- clipMaxBounds
- ) {
- clipMinBounds = defaultValue(
- clipMinBounds,
- VoxelCylinderShape.DefaultMinBounds
- );
- clipMaxBounds = defaultValue(
- clipMaxBounds,
- VoxelCylinderShape.DefaultMaxBounds
- );
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("modelMatrix", modelMatrix);
- Check.typeOf.object("minBounds", minBounds);
- Check.typeOf.object("maxBounds", maxBounds);
- //>>includeEnd('debug');
- const defaultMinRadius = VoxelCylinderShape.DefaultMinBounds.x;
- const defaultMaxRadius = VoxelCylinderShape.DefaultMaxBounds.x;
- const defaultMinHeight = VoxelCylinderShape.DefaultMinBounds.y;
- const defaultMaxHeight = VoxelCylinderShape.DefaultMaxBounds.y;
- const defaultMinAngle = VoxelCylinderShape.DefaultMinBounds.z;
- const defaultMaxAngle = VoxelCylinderShape.DefaultMaxBounds.z;
- const defaultAngleRange = defaultMaxAngle - defaultMinAngle;
- const defaultAngleRangeHalf = 0.5 * defaultAngleRange;
- const epsilonZeroScale = CesiumMath.EPSILON10;
- const epsilonAngleDiscontinuity = CesiumMath.EPSILON3; // 0.001 radians = 0.05729578 degrees
- const epsilonAngle = CesiumMath.EPSILON10;
- // Clamp the radii to the valid range
- const shapeMinRadius = CesiumMath.clamp(
- minBounds.x,
- defaultMinRadius,
- defaultMaxRadius
- );
- const shapeMaxRadius = CesiumMath.clamp(
- maxBounds.x,
- defaultMinRadius,
- defaultMaxRadius
- );
- const clipMinRadius = CesiumMath.clamp(
- clipMinBounds.x,
- defaultMinRadius,
- defaultMaxRadius
- );
- const clipMaxRadius = CesiumMath.clamp(
- clipMaxBounds.x,
- defaultMinRadius,
- defaultMaxRadius
- );
- const renderMinRadius = Math.max(shapeMinRadius, clipMinRadius);
- const renderMaxRadius = Math.min(shapeMaxRadius, clipMaxRadius);
- // Clamp the heights to the valid range
- const shapeMinHeight = CesiumMath.clamp(
- minBounds.y,
- defaultMinHeight,
- defaultMaxHeight
- );
- const shapeMaxHeight = CesiumMath.clamp(
- maxBounds.y,
- defaultMinHeight,
- defaultMaxHeight
- );
- const clipMinHeight = CesiumMath.clamp(
- clipMinBounds.y,
- defaultMinHeight,
- defaultMaxHeight
- );
- const clipMaxHeight = CesiumMath.clamp(
- clipMaxBounds.y,
- defaultMinHeight,
- defaultMaxHeight
- );
- const renderMinHeight = Math.max(shapeMinHeight, clipMinHeight);
- const renderMaxHeight = Math.min(shapeMaxHeight, clipMaxHeight);
- // Clamp the angles to the valid range
- const shapeMinAngle = CesiumMath.negativePiToPi(minBounds.z);
- const shapeMaxAngle = CesiumMath.negativePiToPi(maxBounds.z);
- const clipMinAngle = CesiumMath.negativePiToPi(clipMinBounds.z);
- const clipMaxAngle = CesiumMath.negativePiToPi(clipMaxBounds.z);
- const renderMinAngle = Math.max(shapeMinAngle, clipMinAngle);
- const renderMaxAngle = Math.min(shapeMaxAngle, clipMaxAngle);
- const scale = Matrix4.getScale(modelMatrix, scratchScale);
- // Exit early if the shape is not visible.
- // Note that minAngle may be greater than maxAngle when crossing the 180th meridian.
- // Cylinder is not visible if:
- // - maxRadius is zero (line)
- // - minRadius is greater than maxRadius
- // - minHeight is greater than maxHeight
- // - scale is 0 for any component (too annoying to reconstruct rotation matrix)
- if (
- renderMaxRadius === 0.0 ||
- renderMinRadius > renderMaxRadius ||
- renderMinHeight > renderMaxHeight ||
- CesiumMath.equalsEpsilon(scale.x, 0.0, undefined, epsilonZeroScale) ||
- CesiumMath.equalsEpsilon(scale.y, 0.0, undefined, epsilonZeroScale) ||
- CesiumMath.equalsEpsilon(scale.z, 0.0, undefined, epsilonZeroScale)
- ) {
- return false;
- }
- this._minimumRadius = shapeMinRadius; // [0,1]
- this._maximumRadius = shapeMaxRadius; // [0,1]
- this._minimumHeight = shapeMinHeight; // [-1,+1]
- this._maximumHeight = shapeMaxHeight; // [-1,+1]
- this._minimumAngle = shapeMinAngle; // [-pi,+pi]
- this._maximumAngle = shapeMaxAngle; // [-pi,+pi]
- this.shapeTransform = Matrix4.clone(modelMatrix, this.shapeTransform);
- this.orientedBoundingBox = getCylinderChunkObb(
- renderMinRadius,
- renderMaxRadius,
- renderMinHeight,
- renderMaxHeight,
- renderMinAngle,
- renderMaxAngle,
- this.shapeTransform,
- this.orientedBoundingBox
- );
- this.boundTransform = Matrix4.fromRotationTranslation(
- this.orientedBoundingBox.halfAxes,
- this.orientedBoundingBox.center,
- this.boundTransform
- );
- this.boundingSphere = BoundingSphere.fromOrientedBoundingBox(
- this.orientedBoundingBox,
- this.boundingSphere
- );
- const shapeIsDefaultMaxRadius = shapeMaxRadius === defaultMaxRadius;
- const shapeIsDefaultMinRadius = shapeMinRadius === defaultMinRadius;
- const shapeIsDefaultRadius =
- shapeIsDefaultMinRadius && shapeIsDefaultMaxRadius;
- const shapeIsDefaultHeight =
- shapeMinHeight === defaultMinHeight && shapeMaxHeight === defaultMaxHeight;
- const shapeIsAngleReversed = shapeMaxAngle < shapeMinAngle;
- const shapeAngleRange =
- shapeMaxAngle - shapeMinAngle + shapeIsAngleReversed * defaultAngleRange;
- const shapeIsAngleRegular =
- shapeAngleRange > defaultAngleRangeHalf + epsilonAngle &&
- shapeAngleRange < defaultAngleRange - epsilonAngle;
- const shapeIsAngleFlipped =
- shapeAngleRange > epsilonAngle &&
- shapeAngleRange < defaultAngleRangeHalf - epsilonAngle;
- const shapeIsAngleRangeHalf =
- shapeAngleRange >= defaultAngleRangeHalf - epsilonAngle &&
- shapeAngleRange <= defaultAngleRangeHalf + epsilonAngle;
- const shapeIsAngleRangeZero = shapeAngleRange <= epsilonAngle;
- const shapeHasAngle =
- shapeIsAngleRegular ||
- shapeIsAngleFlipped ||
- shapeIsAngleRangeHalf ||
- shapeIsAngleRangeZero;
- const shapeIsMinAngleDiscontinuity = CesiumMath.equalsEpsilon(
- shapeMinAngle,
- defaultMinAngle,
- undefined,
- epsilonAngleDiscontinuity
- );
- const shapeIsMaxAngleDiscontinuity = CesiumMath.equalsEpsilon(
- shapeMaxAngle,
- defaultMaxAngle,
- undefined,
- epsilonAngleDiscontinuity
- );
- const renderIsDefaultMaxRadius = renderMaxRadius === defaultMaxRadius;
- const renderIsDefaultMinRadius = renderMinRadius === defaultMinRadius;
- const renderIsDefaultHeight =
- renderMinHeight === defaultMinHeight &&
- renderMaxHeight === defaultMaxHeight;
- const renderIsAngleReversed = renderMaxAngle < renderMinAngle;
- const renderAngleRange =
- renderMaxAngle - renderMinAngle + renderIsAngleReversed * defaultAngleRange;
- const renderIsAngleRegular =
- renderAngleRange > defaultAngleRangeHalf + epsilonAngle &&
- renderAngleRange < defaultAngleRange - epsilonAngle;
- const renderIsAngleFlipped =
- renderAngleRange > epsilonAngle &&
- renderAngleRange < defaultAngleRangeHalf - epsilonAngle;
- const renderIsAngleRangeHalf =
- renderAngleRange >= defaultAngleRangeHalf - epsilonAngle &&
- renderAngleRange <= defaultAngleRangeHalf + epsilonAngle;
- const renderIsAngleRangeZero = renderAngleRange <= epsilonAngle;
- const renderHasAngle =
- renderIsAngleRegular ||
- renderIsAngleFlipped ||
- renderIsAngleRangeHalf ||
- renderIsAngleRangeZero;
- const shaderUniforms = this.shaderUniforms;
- const shaderDefines = this.shaderDefines;
- // To keep things simple, clear the defines every time
- for (const key in shaderDefines) {
- if (shaderDefines.hasOwnProperty(key)) {
- shaderDefines[key] = undefined;
- }
- }
- // Keep track of how many intersections there are going to be.
- let intersectionCount = 0;
- shaderDefines["CYLINDER_INTERSECTION_INDEX_RADIUS_MAX"] = intersectionCount;
- intersectionCount += 1;
- if (!renderIsDefaultMinRadius) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MIN"] = true;
- shaderDefines["CYLINDER_INTERSECTION_INDEX_RADIUS_MIN"] = intersectionCount;
- intersectionCount += 1;
- shaderUniforms.cylinderUvToRenderRadiusMin =
- renderMaxRadius / renderMinRadius;
- }
- if (!renderIsDefaultMaxRadius) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MAX"] = true;
- }
- if (renderMinRadius === renderMaxRadius) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_RADIUS_FLAT"] = true;
- }
- if (!renderIsDefaultHeight) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_HEIGHT"] = true;
- }
- if (renderMinHeight === renderMaxHeight) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_HEIGHT_FLAT"] = true;
- }
- if (shapeMinHeight === shapeMaxHeight) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_HEIGHT_FLAT"] = true;
- }
- if (shapeMinRadius === shapeMaxRadius) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_RADIUS_FLAT"] = true;
- }
- if (!shapeIsDefaultRadius) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_RADIUS"] = true;
- // delerp(radius, minRadius, maxRadius)
- // (radius - minRadius) / (maxRadius - minRadius)
- // radius / (maxRadius - minRadius) - minRadius / (maxRadius - minRadius)
- // scale = 1.0 / (maxRadius - minRadius)
- // offset = -minRadius / (maxRadius - minRadius)
- // offset = minRadius / (minRadius - maxRadius)
- const scale = 1.0 / (shapeMaxRadius - shapeMinRadius);
- const offset = shapeMinRadius / (shapeMinRadius - shapeMaxRadius);
- shaderUniforms.cylinderUvToShapeUvRadius = Cartesian2.fromElements(
- scale,
- offset,
- shaderUniforms.cylinderUvToShapeUvRadius
- );
- }
- if (!shapeIsDefaultHeight) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_HEIGHT"] = true;
- // delerp(heightUv, minHeightUv, maxHeightUv)
- // (heightUv - minHeightUv) / (maxHeightUv - minHeightUv)
- // heightUv / (maxHeightUv - minHeightUv) - minHeightUv / (maxHeightUv - minHeightUv)
- // scale = 1.0 / (maxHeightUv - minHeightUv)
- // scale = 1.0 / ((maxHeight * 0.5 + 0.5) - (minHeight * 0.5 + 0.5))
- // scale = 2.0 / (maxHeight - minHeight)
- // offset = -minHeightUv / (maxHeightUv - minHeightUv)
- // offset = -minHeightUv / ((maxHeight * 0.5 + 0.5) - (minHeight * 0.5 + 0.5))
- // offset = -2.0 * (minHeight * 0.5 + 0.5) / (maxHeight - minHeight)
- // offset = -(minHeight + 1.0) / (maxHeight - minHeight)
- // offset = (minHeight + 1.0) / (minHeight - maxHeight)
- const scale = 2.0 / (shapeMaxHeight - shapeMinHeight);
- const offset = (shapeMinHeight + 1.0) / (shapeMinHeight - shapeMaxHeight);
- shaderUniforms.cylinderUvToShapeUvHeight = Cartesian2.fromElements(
- scale,
- offset,
- shaderUniforms.cylinderUvToShapeUvHeight
- );
- }
- if (!renderIsDefaultMaxRadius || !renderIsDefaultHeight) {
- const heightScale = 0.5 * (renderMaxHeight - renderMinHeight);
- const scaleLocalToBounds = Cartesian3.fromElements(
- 1.0 / renderMaxRadius,
- 1.0 / renderMaxRadius,
- 1.0 / (heightScale === 0.0 ? 1.0 : heightScale),
- scratchBoundsScale
- );
- // -inverse(scale) * translation // affine inverse
- // -inverse(scale) * 0.5 * (minHeight + maxHeight)
- const translateLocalToBounds = Cartesian3.fromElements(
- 0.0,
- 0.0,
- -scaleLocalToBounds.z * 0.5 * (renderMinHeight + renderMaxHeight),
- scratchBoundsTranslation
- );
- const transformLocalToBounds = Matrix4.fromRotationTranslation(
- Matrix3.fromScale(scaleLocalToBounds, scratchBoundsScaleMatrix),
- translateLocalToBounds,
- scratchTransformLocalToBounds
- );
- const transformUvToBounds = Matrix4.multiplyTransformation(
- transformLocalToBounds,
- transformUvToLocal,
- scratchTransformUvToBounds
- );
- shaderUniforms.cylinderUvToRenderBoundsScale = Matrix4.getScale(
- transformUvToBounds,
- shaderUniforms.cylinderUvToRenderBoundsScale
- );
- shaderUniforms.cylinderUvToRenderBoundsTranslate = Matrix4.getTranslation(
- transformUvToBounds,
- shaderUniforms.cylinderUvToRenderBoundsTranslate
- );
- }
- if (shapeIsAngleReversed) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_MIN_MAX_REVERSED"] = true;
- }
- if (renderHasAngle) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_ANGLE"] = true;
- shaderDefines["CYLINDER_INTERSECTION_INDEX_ANGLE"] = intersectionCount;
- if (renderIsAngleRegular) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_UNDER_HALF"] = true;
- intersectionCount += 1;
- } else if (renderIsAngleFlipped) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_OVER_HALF"] = true;
- intersectionCount += 2;
- } else if (renderIsAngleRangeHalf) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_EQUAL_HALF"] = true;
- intersectionCount += 1;
- } else if (renderIsAngleRangeZero) {
- shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_EQUAL_ZERO"] = true;
- intersectionCount += 2;
- }
- shaderUniforms.cylinderRenderAngleMinMax = Cartesian2.fromElements(
- renderMinAngle,
- renderMaxAngle,
- shaderUniforms.cylinderAngleMinMax
- );
- }
- if (shapeHasAngle) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_ANGLE"] = true;
- if (shapeIsAngleRangeZero) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_RANGE_EQUAL_ZERO"] = true;
- }
- if (shapeIsMinAngleDiscontinuity) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_MIN_DISCONTINUITY"] = true;
- }
- if (shapeIsMaxAngleDiscontinuity) {
- shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_MAX_DISCONTINUITY"] = true;
- }
- const uvMinAngle = (shapeMinAngle - defaultMinAngle) / defaultAngleRange;
- const uvMaxAngle = (shapeMaxAngle - defaultMinAngle) / defaultAngleRange;
- const uvAngleRangeZero = 1.0 - shapeAngleRange / defaultAngleRange;
- shaderUniforms.cylinderShapeUvAngleMinMax = Cartesian2.fromElements(
- uvMinAngle,
- uvMaxAngle,
- shaderUniforms.cylinderShapeUvAngleMinMax
- );
- shaderUniforms.cylinderShapeUvAngleRangeZeroMid =
- (uvMaxAngle + 0.5 * uvAngleRangeZero) % 1.0;
- // delerp(angleUv, uvMinAngle, uvMaxAngle)
- // (angelUv - uvMinAngle) / (uvMaxAngle - uvMinAngle)
- // angleUv / (uvMaxAngle - uvMinAngle) - uvMinAngle / (uvMaxAngle - uvMinAngle)
- // scale = 1.0 / (uvMaxAngle - uvMinAngle)
- // scale = 1.0 / (((maxAngle - pi) / (2.0 * pi)) - ((minAngle - pi) / (2.0 * pi)))
- // scale = 2.0 * pi / (maxAngle - minAngle)
- // offset = -uvMinAngle / (uvMaxAngle - uvMinAngle)
- // offset = -((minAngle - pi) / (2.0 * pi)) / (((maxAngle - pi) / (2.0 * pi)) - ((minAngle - pi) / (2.0 * pi)))
- // offset = -(minAngle - pi) / (maxAngle - minAngle)
- const scale = defaultAngleRange / shapeAngleRange;
- const offset = -(shapeMinAngle - defaultMinAngle) / shapeAngleRange;
- shaderUniforms.cylinderUvToShapeUvAngle = Cartesian2.fromElements(
- scale,
- offset,
- shaderUniforms.cylinderUvToShapeUvAngle
- );
- }
- this.shaderMaximumIntersectionsLength = intersectionCount;
- return true;
- };
- /**
- * Computes an oriented bounding box for a specified tile.
- * The update function must be called before calling this function.
- *
- * @param {number} tileLevel The tile's level.
- * @param {number} tileX The tile's x coordinate.
- * @param {number} tileY The tile's y coordinate.
- * @param {number} tileZ The tile's z coordinate.
- * @param {OrientedBoundingBox} result The oriented bounding box that will be set to enclose the specified tile
- * @returns {OrientedBoundingBox} The oriented bounding box.
- */
- VoxelCylinderShape.prototype.computeOrientedBoundingBoxForTile = function (
- tileLevel,
- tileX,
- tileY,
- tileZ,
- result
- ) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number("tileLevel", tileLevel);
- Check.typeOf.number("tileX", tileX);
- Check.typeOf.number("tileY", tileY);
- Check.typeOf.number("tileZ", tileZ);
- Check.typeOf.object("result", result);
- //>>includeEnd('debug');
- const minimumRadius = this._minimumRadius;
- const maximumRadius = this._maximumRadius;
- const minimumHeight = this._minimumHeight;
- const maximumHeight = this._maximumHeight;
- const minimumAngle = this._minimumAngle;
- const maximumAngle = this._maximumAngle;
- const sizeAtLevel = 1.0 / Math.pow(2.0, tileLevel);
- const radiusStart = CesiumMath.lerp(
- minimumRadius,
- maximumRadius,
- tileX * sizeAtLevel
- );
- const radiusEnd = CesiumMath.lerp(
- minimumRadius,
- maximumRadius,
- (tileX + 1) * sizeAtLevel
- );
- const heightStart = CesiumMath.lerp(
- minimumHeight,
- maximumHeight,
- tileY * sizeAtLevel
- );
- const heightEnd = CesiumMath.lerp(
- minimumHeight,
- maximumHeight,
- (tileY + 1) * sizeAtLevel
- );
- const angleStart = CesiumMath.lerp(
- minimumAngle,
- maximumAngle,
- tileZ * sizeAtLevel
- );
- const angleEnd = CesiumMath.lerp(
- minimumAngle,
- maximumAngle,
- (tileZ + 1) * sizeAtLevel
- );
- return getCylinderChunkObb(
- radiusStart,
- radiusEnd,
- heightStart,
- heightEnd,
- angleStart,
- angleEnd,
- this.shapeTransform,
- result
- );
- };
- const scratchOrientedBoundingBox = new OrientedBoundingBox();
- const scratchVoxelScale = new Cartesian3();
- const scratchRootScale = new Cartesian3();
- const scratchScaleRatio = new Cartesian3();
- /**
- * Computes an approximate step size for raymarching the root tile of a voxel grid.
- * The update function must be called before calling this function.
- *
- * @param {Cartesian3} dimensions The voxel grid dimensions for a tile.
- * @returns {number} The step size.
- */
- VoxelCylinderShape.prototype.computeApproximateStepSize = function (
- dimensions
- ) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("dimensions", dimensions);
- //>>includeEnd('debug');
- const shapeTransform = this.shapeTransform;
- const minRadius = this._minimumRadius;
- const maxRadius = this._maximumRadius;
- const minHeight = this._minimumHeight;
- const maxHeight = this._maximumHeight;
- const minAngle = this._minimumAngle;
- const maxAngle = this._maximumAngle;
- const lerpRadius = 1.0 - 1.0 / dimensions.x;
- const lerpHeight = 1.0 - 1.0 / dimensions.y;
- const lerpAngle = 1.0 - 1.0 / dimensions.z;
- // Compare the size of an outermost cylinder voxel to the total cylinder
- const voxelMinimumRadius = CesiumMath.lerp(minRadius, maxRadius, lerpRadius);
- const voxelMinimumHeight = CesiumMath.lerp(minHeight, maxHeight, lerpHeight);
- const voxelMinimumAngle = CesiumMath.lerp(minAngle, maxAngle, lerpAngle);
- const voxelMaximumRadius = maxRadius;
- const voxelMaximumHeight = maxHeight;
- const voxelMaximumAngle = maxAngle;
- const voxelObb = getCylinderChunkObb(
- voxelMinimumRadius,
- voxelMaximumRadius,
- voxelMinimumHeight,
- voxelMaximumHeight,
- voxelMinimumAngle,
- voxelMaximumAngle,
- shapeTransform,
- scratchOrientedBoundingBox
- );
- const voxelScale = Matrix3.getScale(voxelObb.halfAxes, scratchVoxelScale);
- const rootScale = Matrix4.getScale(shapeTransform, scratchRootScale);
- const scaleRatio = Cartesian3.divideComponents(
- voxelScale,
- rootScale,
- scratchScaleRatio
- );
- const stepSize = Cartesian3.minimumComponent(scaleRatio);
- return stepSize;
- };
- /**
- * Defines the minimum bounds of the shape. Corresponds to minimum radius, height, angle.
- *
- * @type {Cartesian3}
- * @constant
- * @readonly
- *
- * @private
- */
- VoxelCylinderShape.DefaultMinBounds = Object.freeze(
- new Cartesian3(0.0, -1.0, -CesiumMath.PI)
- );
- /**
- * Defines the maximum bounds of the shape. Corresponds to maximum radius, height, angle.
- *
- * @type {Cartesian3}
- * @constant
- * @readonly
- *
- * @private
- */
- VoxelCylinderShape.DefaultMaxBounds = Object.freeze(
- new Cartesian3(1.0, +1.0, +CesiumMath.PI)
- );
- const maxTestAngles = 5;
- const scratchTestAngles = new Array(maxTestAngles);
- const scratchTranslation = new Cartesian3();
- const scratchRotation = new Matrix3();
- const scratchTranslationMatrix = new Matrix4();
- const scratchRotationMatrix = new Matrix4();
- const scratchScaleMatrix = new Matrix4();
- const scratchMatrix = new Matrix4();
- const scratchColumn0 = new Cartesian3();
- const scratchColumn1 = new Cartesian3();
- const scratchColumn2 = new Cartesian3();
- const scratchCorners = new Array(8);
- for (let i = 0; i < 8; i++) {
- scratchCorners[i] = new Cartesian3();
- }
- function orthogonal(a, b, epsilon) {
- return Math.abs(Cartesian4.dot(a, b)) < epsilon;
- }
- function isValidOrientedBoundingBoxTransformation(matrix) {
- const column0 = Matrix4.getColumn(matrix, 0, scratchColumn0);
- const column1 = Matrix4.getColumn(matrix, 1, scratchColumn1);
- const column2 = Matrix4.getColumn(matrix, 2, scratchColumn2);
- const epsilon = CesiumMath.EPSILON4;
- return (
- orthogonal(column0, column1, epsilon) &&
- orthogonal(column1, column2, epsilon)
- );
- }
- function computeLooseOrientedBoundingBox(matrix, result) {
- const corners = scratchCorners;
- Cartesian3.fromElements(-0.5, -0.5, -0.5, corners[0]);
- Cartesian3.fromElements(-0.5, -0.5, 0.5, corners[1]);
- Cartesian3.fromElements(-0.5, 0.5, -0.5, corners[2]);
- Cartesian3.fromElements(-0.5, 0.5, 0.5, corners[3]);
- Cartesian3.fromElements(0.5, -0.5, -0.5, corners[4]);
- Cartesian3.fromElements(0.5, -0.5, 0.5, corners[5]);
- Cartesian3.fromElements(0.5, 0.5, -0.5, corners[6]);
- Cartesian3.fromElements(0.5, 0.5, 0.5, corners[7]);
- for (let i = 0; i < 8; ++i) {
- Matrix4.multiplyByPoint(matrix, corners[i], corners[i]);
- }
- return OrientedBoundingBox.fromPoints(corners, result);
- }
- /**
- * Computes an {@link OrientedBoundingBox} for a subregion of the shape.
- *
- * @function
- *
- * @param {number} radiusStart The radiusStart.
- * @param {number} radiusEnd The radiusEnd.
- * @param {number} heightStart The heightStart.
- * @param {number} heightEnd The heightEnd.
- * @param {number} angleStart The angleStart.
- * @param {number} angleEnd The angleEnd.
- * @param {Matrix4} matrix The matrix to transform the points.
- * @param {OrientedBoundingBox} result The object onto which to store the result.
- * @returns {OrientedBoundingBox} The oriented bounding box that contains this subregion.
- *
- * @private
- */
- function getCylinderChunkObb(
- radiusStart,
- radiusEnd,
- heightStart,
- heightEnd,
- angleStart,
- angleEnd,
- matrix,
- result
- ) {
- const defaultMinBounds = VoxelCylinderShape.DefaultMinBounds;
- const defaultMaxBounds = VoxelCylinderShape.DefaultMaxBounds;
- const defaultMinRadius = defaultMinBounds.x; // 0
- const defaultMaxRadius = defaultMaxBounds.x; // 1
- const defaultMinHeight = defaultMinBounds.y; // -1
- const defaultMaxHeight = defaultMaxBounds.y; // +1
- const defaultMinAngle = defaultMinBounds.z; // -pi
- const defaultMaxAngle = defaultMaxBounds.z; // +pi
- // Return early if using the default bounds
- if (
- radiusStart === defaultMinRadius &&
- radiusEnd === defaultMaxRadius &&
- heightStart === defaultMinHeight &&
- heightEnd === defaultMaxHeight &&
- angleStart === defaultMinAngle &&
- angleEnd === defaultMaxAngle
- ) {
- result.center = Matrix4.getTranslation(matrix, result.center);
- result.halfAxes = Matrix4.getMatrix3(matrix, result.halfAxes);
- return result;
- }
- const isAngleReversed = angleEnd < angleStart;
- if (isAngleReversed) {
- angleEnd += CesiumMath.TWO_PI;
- }
- const angleRange = angleEnd - angleStart;
- const angleMid = angleStart + angleRange * 0.5;
- const testAngles = scratchTestAngles;
- let testAngleCount = 0;
- testAngles[testAngleCount++] = angleStart;
- testAngles[testAngleCount++] = angleEnd;
- testAngles[testAngleCount++] = angleMid;
- if (angleRange > CesiumMath.PI) {
- testAngles[testAngleCount++] = angleMid - CesiumMath.PI_OVER_TWO;
- testAngles[testAngleCount++] = angleMid + CesiumMath.PI_OVER_TWO;
- }
- // Find bounding box in shape space relative to angleMid
- let minX = 1.0;
- let minY = 1.0;
- let maxX = -1.0;
- let maxY = -1.0;
- for (let i = 0; i < testAngleCount; ++i) {
- const angle = testAngles[i] - angleMid;
- const cosAngle = Math.cos(angle);
- const sinAngle = Math.sin(angle);
- const x1 = cosAngle * radiusStart;
- const y1 = sinAngle * radiusStart;
- const x2 = cosAngle * radiusEnd;
- const y2 = sinAngle * radiusEnd;
- minX = Math.min(minX, x1);
- minY = Math.min(minY, y1);
- minX = Math.min(minX, x2);
- minY = Math.min(minY, y2);
- maxX = Math.max(maxX, x1);
- maxY = Math.max(maxY, y1);
- maxX = Math.max(maxX, x2);
- maxY = Math.max(maxY, y2);
- }
- const extentX = maxX - minX;
- const extentY = maxY - minY;
- const extentZ = heightEnd - heightStart;
- const centerX = (minX + maxX) * 0.5;
- const centerY = (minY + maxY) * 0.5;
- const centerZ = (heightStart + heightEnd) * 0.5;
- const translation = Cartesian3.fromElements(
- centerX,
- centerY,
- centerZ,
- scratchTranslation
- );
- const rotation = Matrix3.fromRotationZ(angleMid, scratchRotation);
- const scale = Cartesian3.fromElements(
- extentX,
- extentY,
- extentZ,
- scratchScale
- );
- const scaleMatrix = Matrix4.fromScale(scale, scratchScaleMatrix);
- const rotationMatrix = Matrix4.fromRotation(rotation, scratchRotationMatrix);
- const translationMatrix = Matrix4.fromTranslation(
- translation,
- scratchTranslationMatrix
- );
- // Shape space matrix = R * T * S
- const localMatrix = Matrix4.multiplyTransformation(
- rotationMatrix,
- Matrix4.multiplyTransformation(
- translationMatrix,
- scaleMatrix,
- scratchMatrix
- ),
- scratchMatrix
- );
- const globalMatrix = Matrix4.multiplyTransformation(
- matrix,
- localMatrix,
- scratchMatrix
- );
- if (!isValidOrientedBoundingBoxTransformation(globalMatrix)) {
- return computeLooseOrientedBoundingBox(globalMatrix, result);
- }
- return OrientedBoundingBox.fromTransformation(globalMatrix, result);
- }
- export default VoxelCylinderShape;
|