VoxelEllipsoidShape.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian2 from "../Core/Cartesian2.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Check from "../Core/Check.js";
  5. import Ellipsoid from "../Core/Ellipsoid.js";
  6. import CesiumMath from "../Core/Math.js";
  7. import Matrix3 from "../Core/Matrix3.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  10. import Rectangle from "../Core/Rectangle.js";
  11. import defaultValue from "../Core/defaultValue.js";
  12. /**
  13. * An ellipsoid {@link VoxelShape}.
  14. *
  15. * @alias VoxelEllipsoidShape
  16. * @constructor
  17. *
  18. * @see VoxelShape
  19. * @see VoxelBoxShape
  20. * @see VoxelCylinderShape
  21. * @see VoxelShapeType
  22. *
  23. * @private
  24. */
  25. function VoxelEllipsoidShape() {
  26. /**
  27. * An oriented bounding box containing the bounded shape.
  28. * The update function must be called before accessing this value.
  29. * @type {OrientedBoundingBox}
  30. * @readonly
  31. */
  32. this.orientedBoundingBox = new OrientedBoundingBox();
  33. /**
  34. * A bounding sphere containing the bounded shape.
  35. * The update function must be called before accessing this value.
  36. * @type {BoundingSphere}
  37. * @readonly
  38. */
  39. this.boundingSphere = new BoundingSphere();
  40. /**
  41. * A transformation matrix containing the bounded shape.
  42. * The update function must be called before accessing this value.
  43. * @type {Matrix4}
  44. * @readonly
  45. */
  46. this.boundTransform = new Matrix4();
  47. /**
  48. * A transformation matrix containing the shape, ignoring the bounds.
  49. * The update function must be called before accessing this value.
  50. * @type {Matrix4}
  51. * @readonly
  52. */
  53. this.shapeTransform = new Matrix4();
  54. /**
  55. * @type {Rectangle}
  56. */
  57. this._rectangle = new Rectangle();
  58. /**
  59. * @type {number}
  60. * @private
  61. */
  62. this._minimumHeight = VoxelEllipsoidShape.DefaultMinBounds.z;
  63. /**
  64. * @type {number}
  65. * @private
  66. */
  67. this._maximumHeight = VoxelEllipsoidShape.DefaultMaxBounds.z;
  68. /**
  69. * @type {Ellipsoid}
  70. * @private
  71. */
  72. this._ellipsoid = new Ellipsoid();
  73. /**
  74. * @type {Cartesian3}
  75. */
  76. this._translation = new Cartesian3();
  77. /**
  78. * @type {Matrix3}
  79. */
  80. this._rotation = new Matrix3();
  81. /**
  82. * @type {Object<string, any>}
  83. * @readonly
  84. */
  85. this.shaderUniforms = {
  86. ellipsoidRadiiUv: new Cartesian3(),
  87. ellipsoidInverseRadiiSquaredUv: new Cartesian3(),
  88. ellipsoidRenderLongitudeMinMax: new Cartesian2(),
  89. ellipsoidShapeUvLongitudeMinMaxMid: new Cartesian3(),
  90. ellipsoidUvToShapeUvLongitude: new Cartesian2(),
  91. ellipsoidUvToShapeUvLatitude: new Cartesian2(),
  92. ellipsoidRenderLatitudeCosSqrHalfMinMax: new Cartesian2(),
  93. ellipsoidInverseHeightDifferenceUv: 0.0,
  94. ellipseInnerRadiiUv: new Cartesian2(),
  95. ellipsoidInverseInnerScaleUv: 0.0,
  96. ellipsoidInverseOuterScaleUv: 0.0,
  97. };
  98. /**
  99. * @type {Object<string, any>}
  100. * @readonly
  101. */
  102. this.shaderDefines = {
  103. ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE: undefined,
  104. ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO: undefined,
  105. ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_UNDER_HALF: undefined,
  106. ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_HALF: undefined,
  107. ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_OVER_HALF: undefined,
  108. ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY: undefined,
  109. ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY: undefined,
  110. ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE: undefined,
  111. ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO: undefined,
  112. ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED: undefined,
  113. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE: undefined,
  114. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF: undefined,
  115. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF: undefined,
  116. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF: undefined,
  117. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF: undefined,
  118. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF: undefined,
  119. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF: undefined,
  120. ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO: undefined,
  121. ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE: undefined,
  122. ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO: undefined,
  123. ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX: undefined,
  124. ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN: undefined,
  125. ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_FLAT: undefined,
  126. ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN: undefined,
  127. ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT: undefined,
  128. ELLIPSOID_IS_SPHERE: undefined,
  129. ELLIPSOID_INTERSECTION_INDEX_LONGITUDE: undefined,
  130. ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX: undefined,
  131. ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN: undefined,
  132. ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX: undefined,
  133. ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN: undefined,
  134. };
  135. /**
  136. * The maximum number of intersections against the shape for any ray direction.
  137. * @type {number}
  138. * @readonly
  139. */
  140. this.shaderMaximumIntersectionsLength = 0; // not known until update
  141. }
  142. const scratchScale = new Cartesian3();
  143. const scratchRotationScale = new Matrix3();
  144. const scratchShapeOuterExtent = new Cartesian3();
  145. const scratchShapeInnerExtent = new Cartesian3();
  146. const scratchRenderOuterExtent = new Cartesian3();
  147. const scratchRenderInnerExtent = new Cartesian3();
  148. const scratchRenderRectangle = new Rectangle();
  149. /**
  150. * Update the shape's state.
  151. *
  152. * @param {Matrix4} modelMatrix The model matrix.
  153. * @param {Cartesian3} minBounds The minimum bounds.
  154. * @param {Cartesian3} maxBounds The maximum bounds.
  155. * @param {Cartesian3} [clipMinBounds=VoxelEllipsoidShape.DefaultMinBounds] The minimum clip bounds.
  156. * @param {Cartesian3} [clipMaxBounds=VoxelEllipsoidShape.DefaultMaxBounds] The maximum clip bounds.
  157. * @returns {boolean} Whether the shape is visible.
  158. */
  159. VoxelEllipsoidShape.prototype.update = function (
  160. modelMatrix,
  161. minBounds,
  162. maxBounds,
  163. clipMinBounds,
  164. clipMaxBounds
  165. ) {
  166. clipMinBounds = defaultValue(
  167. clipMinBounds,
  168. VoxelEllipsoidShape.DefaultMinBounds
  169. );
  170. clipMaxBounds = defaultValue(
  171. clipMaxBounds,
  172. VoxelEllipsoidShape.DefaultMaxBounds
  173. );
  174. //>>includeStart('debug', pragmas.debug);
  175. Check.typeOf.object("modelMatrix", modelMatrix);
  176. Check.typeOf.object("minBounds", minBounds);
  177. Check.typeOf.object("maxBounds", maxBounds);
  178. //>>includeEnd('debug');
  179. const defaultMinLongitude = VoxelEllipsoidShape.DefaultMinBounds.x;
  180. const defaultMaxLongitude = VoxelEllipsoidShape.DefaultMaxBounds.x;
  181. const defaultLongitudeRange = defaultMaxLongitude - defaultMinLongitude;
  182. const defaultLongitudeRangeHalf = 0.5 * defaultLongitudeRange;
  183. const defaultMinLatitude = VoxelEllipsoidShape.DefaultMinBounds.y;
  184. const defaultMaxLatitude = VoxelEllipsoidShape.DefaultMaxBounds.y;
  185. const defaultLatitudeRange = defaultMaxLatitude - defaultMinLatitude;
  186. const epsilonZeroScale = CesiumMath.EPSILON10;
  187. const epsilonLongitudeDiscontinuity = CesiumMath.EPSILON3; // 0.001 radians = 0.05729578 degrees
  188. const epsilonLongitude = CesiumMath.EPSILON10;
  189. const epsilonLatitude = CesiumMath.EPSILON10;
  190. const epsilonLatitudeFlat = CesiumMath.EPSILON3; // 0.001 radians = 0.05729578 degrees
  191. // Clamp the longitude / latitude to the valid range
  192. const shapeMinLongitude = CesiumMath.clamp(
  193. minBounds.x,
  194. defaultMinLongitude,
  195. defaultMaxLongitude
  196. );
  197. const shapeMaxLongitude = CesiumMath.clamp(
  198. maxBounds.x,
  199. defaultMinLongitude,
  200. defaultMaxLongitude
  201. );
  202. const clipMinLongitude = CesiumMath.clamp(
  203. clipMinBounds.x,
  204. defaultMinLongitude,
  205. defaultMaxLongitude
  206. );
  207. const clipMaxLongitude = CesiumMath.clamp(
  208. clipMaxBounds.x,
  209. defaultMinLongitude,
  210. defaultMaxLongitude
  211. );
  212. const renderMinLongitude = Math.max(shapeMinLongitude, clipMinLongitude);
  213. const renderMaxLongitude = Math.min(shapeMaxLongitude, clipMaxLongitude);
  214. const shapeMinLatitude = CesiumMath.clamp(
  215. minBounds.y,
  216. defaultMinLatitude,
  217. defaultMaxLatitude
  218. );
  219. const shapeMaxLatitude = CesiumMath.clamp(
  220. maxBounds.y,
  221. defaultMinLatitude,
  222. defaultMaxLatitude
  223. );
  224. const clipMinLatitude = CesiumMath.clamp(
  225. clipMinBounds.y,
  226. defaultMinLatitude,
  227. defaultMaxLatitude
  228. );
  229. const clipMaxLatitude = CesiumMath.clamp(
  230. clipMaxBounds.y,
  231. defaultMinLatitude,
  232. defaultMaxLatitude
  233. );
  234. const renderMinLatitude = Math.max(shapeMinLatitude, clipMinLatitude);
  235. const renderMaxLatitude = Math.min(shapeMaxLatitude, clipMaxLatitude);
  236. // Don't let the height go below the center of the ellipsoid.
  237. const radii = Matrix4.getScale(modelMatrix, scratchScale);
  238. const isSphere = radii.x === radii.y && radii.y === radii.z;
  239. const minRadius = Cartesian3.minimumComponent(radii);
  240. const shapeMinHeight = Math.max(minBounds.z, -minRadius);
  241. const shapeMaxHeight = Math.max(maxBounds.z, -minRadius);
  242. const clipMinHeight = Math.max(clipMinBounds.z, -minRadius);
  243. const clipMaxHeight = Math.max(clipMaxBounds.z, -minRadius);
  244. const renderMinHeight = Math.max(shapeMinHeight, clipMinHeight);
  245. const renderMaxHeight = Math.min(shapeMaxHeight, clipMaxHeight);
  246. // Compute the closest and farthest a point can be from the center of the ellipsoid.
  247. const shapeInnerExtent = Cartesian3.add(
  248. radii,
  249. Cartesian3.fromElements(
  250. shapeMinHeight,
  251. shapeMinHeight,
  252. shapeMinHeight,
  253. scratchShapeInnerExtent
  254. ),
  255. scratchShapeInnerExtent
  256. );
  257. const shapeOuterExtent = Cartesian3.add(
  258. radii,
  259. Cartesian3.fromElements(
  260. shapeMaxHeight,
  261. shapeMaxHeight,
  262. shapeMaxHeight,
  263. scratchShapeOuterExtent
  264. ),
  265. scratchShapeOuterExtent
  266. );
  267. const shapeMaxExtent = Cartesian3.maximumComponent(shapeOuterExtent);
  268. const renderInnerExtent = Cartesian3.add(
  269. radii,
  270. Cartesian3.fromElements(
  271. renderMinHeight,
  272. renderMinHeight,
  273. renderMinHeight,
  274. scratchRenderInnerExtent
  275. ),
  276. scratchRenderInnerExtent
  277. );
  278. const renderOuterExtent = Cartesian3.add(
  279. radii,
  280. Cartesian3.fromElements(
  281. renderMaxHeight,
  282. renderMaxHeight,
  283. renderMaxHeight,
  284. scratchRenderOuterExtent
  285. ),
  286. scratchRenderOuterExtent
  287. );
  288. // Exit early if the shape is not visible.
  289. // Note that minLongitude may be greater than maxLongitude when crossing the 180th meridian.
  290. if (
  291. renderMinLatitude > renderMaxLatitude ||
  292. renderMinLatitude === defaultMaxLatitude ||
  293. renderMaxLatitude === defaultMinLatitude ||
  294. renderMinHeight > renderMaxHeight ||
  295. CesiumMath.equalsEpsilon(
  296. renderOuterExtent,
  297. Cartesian3.ZERO,
  298. undefined,
  299. epsilonZeroScale
  300. )
  301. ) {
  302. return false;
  303. }
  304. this._rectangle = Rectangle.fromRadians(
  305. shapeMinLongitude,
  306. shapeMinLatitude,
  307. shapeMaxLongitude,
  308. shapeMaxLatitude
  309. );
  310. this._translation = Matrix4.getTranslation(modelMatrix, this._translation);
  311. this._rotation = Matrix4.getRotation(modelMatrix, this._rotation);
  312. this._ellipsoid = Ellipsoid.fromCartesian3(radii, this._ellipsoid);
  313. this._minimumHeight = shapeMinHeight;
  314. this._maximumHeight = shapeMaxHeight;
  315. const renderRectangle = Rectangle.fromRadians(
  316. renderMinLongitude,
  317. renderMinLatitude,
  318. renderMaxLongitude,
  319. renderMaxLatitude,
  320. scratchRenderRectangle
  321. );
  322. this.orientedBoundingBox = getEllipsoidChunkObb(
  323. renderRectangle,
  324. renderMinHeight,
  325. renderMaxHeight,
  326. this._ellipsoid,
  327. this._translation,
  328. this._rotation,
  329. this.orientedBoundingBox
  330. );
  331. this.shapeTransform = Matrix4.fromRotationTranslation(
  332. Matrix3.setScale(this._rotation, shapeOuterExtent, scratchRotationScale),
  333. this._translation,
  334. this.shapeTransform
  335. );
  336. this.boundTransform = Matrix4.fromRotationTranslation(
  337. this.orientedBoundingBox.halfAxes,
  338. this.orientedBoundingBox.center,
  339. this.boundTransform
  340. );
  341. this.boundingSphere = BoundingSphere.fromOrientedBoundingBox(
  342. this.orientedBoundingBox,
  343. this.boundingSphere
  344. );
  345. // Longitude
  346. const renderIsLongitudeReversed = renderMaxLongitude < renderMinLongitude;
  347. const renderLongitudeRange =
  348. renderMaxLongitude -
  349. renderMinLongitude +
  350. renderIsLongitudeReversed * defaultLongitudeRange;
  351. const renderIsLongitudeRangeZero = renderLongitudeRange <= epsilonLongitude;
  352. const renderIsLongitudeRangeUnderHalf =
  353. renderLongitudeRange > defaultLongitudeRangeHalf + epsilonLongitude &&
  354. renderLongitudeRange < defaultLongitudeRange - epsilonLongitude;
  355. const renderIsLongitudeRangeHalf =
  356. renderLongitudeRange >= defaultLongitudeRangeHalf - epsilonLongitude &&
  357. renderLongitudeRange <= defaultLongitudeRangeHalf + epsilonLongitude;
  358. const renderIsLongitudeRangeOverHalf =
  359. renderLongitudeRange > epsilonLongitude &&
  360. renderLongitudeRange < defaultLongitudeRangeHalf - epsilonLongitude;
  361. const renderHasLongitude =
  362. renderIsLongitudeRangeZero ||
  363. renderIsLongitudeRangeUnderHalf ||
  364. renderIsLongitudeRangeHalf ||
  365. renderIsLongitudeRangeOverHalf;
  366. const shapeIsLongitudeReversed = shapeMaxLongitude < shapeMinLongitude;
  367. const shapeLongitudeRange =
  368. shapeMaxLongitude -
  369. shapeMinLongitude +
  370. shapeIsLongitudeReversed * defaultLongitudeRange;
  371. const shapeIsLongitudeRangeZero = shapeLongitudeRange <= epsilonLongitude;
  372. const shapeIsLongitudeRangeUnderHalf =
  373. shapeLongitudeRange > defaultLongitudeRangeHalf + epsilonLongitude &&
  374. shapeLongitudeRange < defaultLongitudeRange - epsilonLongitude;
  375. const shapeIsLongitudeRangeHalf =
  376. shapeLongitudeRange >= defaultLongitudeRangeHalf - epsilonLongitude &&
  377. shapeLongitudeRange <= defaultLongitudeRangeHalf + epsilonLongitude;
  378. const shapeIsLongitudeRangeOverHalf =
  379. shapeLongitudeRange > epsilonLongitude &&
  380. shapeLongitudeRange < defaultLongitudeRangeHalf - epsilonLongitude;
  381. const shapeHasLongitude =
  382. shapeIsLongitudeRangeZero ||
  383. shapeIsLongitudeRangeUnderHalf ||
  384. shapeIsLongitudeRangeHalf ||
  385. shapeIsLongitudeRangeOverHalf;
  386. // Latitude
  387. const renderIsLatitudeMaxUnderHalf = renderMaxLatitude < -epsilonLatitudeFlat;
  388. const renderIsLatitudeMaxHalf =
  389. renderMaxLatitude >= -epsilonLatitudeFlat &&
  390. renderMaxLatitude <= +epsilonLatitudeFlat;
  391. const renderIsLatitudeMaxOverHalf =
  392. renderMaxLatitude > +epsilonLatitudeFlat &&
  393. renderMaxLatitude < defaultMaxLatitude - epsilonLatitude;
  394. const renderHasLatitudeMax =
  395. renderIsLatitudeMaxUnderHalf ||
  396. renderIsLatitudeMaxHalf ||
  397. renderIsLatitudeMaxOverHalf;
  398. const renderIsLatitudeMinUnderHalf =
  399. renderMinLatitude > defaultMinLatitude + epsilonLatitude &&
  400. renderMinLatitude < -epsilonLatitudeFlat;
  401. const renderIsLatitudeMinHalf =
  402. renderMinLatitude >= -epsilonLatitudeFlat &&
  403. renderMinLatitude <= +epsilonLatitudeFlat;
  404. const renderIsLatitudeMinOverHalf = renderMinLatitude > +epsilonLatitudeFlat;
  405. const renderHasLatitudeMin =
  406. renderIsLatitudeMinUnderHalf ||
  407. renderIsLatitudeMinHalf ||
  408. renderIsLatitudeMinOverHalf;
  409. const renderHasLatitude = renderHasLatitudeMax || renderHasLatitudeMin;
  410. const shapeLatitudeRange = shapeMaxLatitude - shapeMinLatitude;
  411. const shapeIsLatitudeMaxUnderHalf = shapeMaxLatitude < -epsilonLatitudeFlat;
  412. const shapeIsLatitudeMaxHalf =
  413. shapeMaxLatitude >= -epsilonLatitudeFlat &&
  414. shapeMaxLatitude <= +epsilonLatitudeFlat;
  415. const shapeIsLatitudeMaxOverHalf =
  416. shapeMaxLatitude > +epsilonLatitudeFlat &&
  417. shapeMaxLatitude < defaultMaxLatitude - epsilonLatitude;
  418. const shapeHasLatitudeMax =
  419. shapeIsLatitudeMaxUnderHalf ||
  420. shapeIsLatitudeMaxHalf ||
  421. shapeIsLatitudeMaxOverHalf;
  422. const shapeIsLatitudeMinUnderHalf =
  423. shapeMinLatitude > defaultMinLatitude + epsilonLatitude &&
  424. shapeMinLatitude < -epsilonLatitudeFlat;
  425. const shapeIsLatitudeMinHalf =
  426. shapeMinLatitude >= -epsilonLatitudeFlat &&
  427. shapeMinLatitude <= +epsilonLatitudeFlat;
  428. const shapeIsLatitudeMinOverHalf = shapeMinLatitude > +epsilonLatitudeFlat;
  429. const shapeHasLatitudeMin =
  430. shapeIsLatitudeMinUnderHalf ||
  431. shapeIsLatitudeMinHalf ||
  432. shapeIsLatitudeMinOverHalf;
  433. const shapeHasLatitude = shapeHasLatitudeMax || shapeHasLatitudeMin;
  434. // Height
  435. const renderHasMinHeight = !Cartesian3.equals(
  436. renderInnerExtent,
  437. Cartesian3.ZERO
  438. );
  439. const renderHasMaxHeight = !Cartesian3.equals(
  440. renderOuterExtent,
  441. Cartesian3.ZERO
  442. );
  443. const renderHasHeight = renderHasMinHeight || renderHasMaxHeight;
  444. const renderHeightRange = renderMaxHeight - renderMinHeight;
  445. const shapeHasMinHeight = !Cartesian3.equals(
  446. shapeInnerExtent,
  447. Cartesian3.ZERO
  448. );
  449. const shapeHasMaxHeight = !Cartesian3.equals(
  450. shapeOuterExtent,
  451. Cartesian3.ZERO
  452. );
  453. const shapeHasHeight = shapeHasMinHeight || shapeHasMaxHeight;
  454. const shaderUniforms = this.shaderUniforms;
  455. const shaderDefines = this.shaderDefines;
  456. // To keep things simple, clear the defines every time
  457. for (const key in shaderDefines) {
  458. if (shaderDefines.hasOwnProperty(key)) {
  459. shaderDefines[key] = undefined;
  460. }
  461. }
  462. // The ellipsoid radii scaled to [0,1]. The max ellipsoid radius will be 1.0 and others will be less.
  463. shaderUniforms.ellipsoidRadiiUv = Cartesian3.divideByScalar(
  464. shapeOuterExtent,
  465. shapeMaxExtent,
  466. shaderUniforms.ellipsoidRadiiUv
  467. );
  468. // Used to compute geodetic surface normal.
  469. shaderUniforms.ellipsoidInverseRadiiSquaredUv = Cartesian3.divideComponents(
  470. Cartesian3.ONE,
  471. Cartesian3.multiplyComponents(
  472. shaderUniforms.ellipsoidRadiiUv,
  473. shaderUniforms.ellipsoidRadiiUv,
  474. shaderUniforms.ellipsoidInverseRadiiSquaredUv
  475. ),
  476. shaderUniforms.ellipsoidInverseRadiiSquaredUv
  477. );
  478. // Keep track of how many intersections there are going to be.
  479. let intersectionCount = 0;
  480. // Intersects an outer ellipsoid for the max height.
  481. shaderDefines["ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX"] = intersectionCount;
  482. intersectionCount += 1;
  483. if (renderHasHeight) {
  484. if (renderHeightRange === 0.0) {
  485. shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_FLAT"] = true;
  486. }
  487. if (renderHasMinHeight) {
  488. shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN"] = true;
  489. shaderDefines[
  490. "ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN"
  491. ] = intersectionCount;
  492. intersectionCount += 1;
  493. // The inverse of the percent of space that is taken up by the inner ellipsoid, relative to the shape bounds
  494. // 1.0 / (1.0 - thickness) // thickness = percent of space that is between the min and max height.
  495. // 1.0 / (1.0 - (shapeMaxHeight - renderMinHeight) / shapeMaxExtent)
  496. // shapeMaxExtent / (shapeMaxExtent - (shapeMaxHeight - renderMinHeight))
  497. shaderUniforms.ellipsoidInverseInnerScaleUv =
  498. shapeMaxExtent / (shapeMaxExtent - (shapeMaxHeight - renderMinHeight));
  499. }
  500. if (renderHasMaxHeight) {
  501. shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX"] = true;
  502. shaderUniforms.ellipsoidInverseOuterScaleUv =
  503. shapeMaxExtent / (shapeMaxExtent - (shapeMaxHeight - renderMaxHeight));
  504. }
  505. }
  506. if (shapeHasHeight) {
  507. if (shapeHasMinHeight) {
  508. shaderDefines["ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN"] = true;
  509. // The percent of space that is between the inner and outer ellipsoid.
  510. const thickness = (shapeMaxHeight - shapeMinHeight) / shapeMaxExtent;
  511. shaderUniforms.ellipsoidInverseHeightDifferenceUv = 1.0 / thickness;
  512. shaderUniforms.ellipseInnerRadiiUv = Cartesian2.fromElements(
  513. shaderUniforms.ellipsoidRadiiUv.x * (1.0 - thickness),
  514. shaderUniforms.ellipsoidRadiiUv.z * (1.0 - thickness),
  515. shaderUniforms.ellipseInnerRadiiUv
  516. );
  517. }
  518. if (shapeMinHeight === shapeMaxHeight) {
  519. shaderDefines["ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT"] = true;
  520. }
  521. }
  522. // Intersects a wedge for the min and max longitude.
  523. if (renderHasLongitude) {
  524. shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE"] = true;
  525. shaderDefines["ELLIPSOID_INTERSECTION_INDEX_LONGITUDE"] = intersectionCount;
  526. if (renderIsLongitudeRangeUnderHalf) {
  527. shaderDefines[
  528. "ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_UNDER_HALF"
  529. ] = true;
  530. intersectionCount += 1;
  531. } else if (renderIsLongitudeRangeOverHalf) {
  532. shaderDefines[
  533. "ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_OVER_HALF"
  534. ] = true;
  535. intersectionCount += 2;
  536. } else if (renderIsLongitudeRangeHalf) {
  537. shaderDefines[
  538. "ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_HALF"
  539. ] = true;
  540. intersectionCount += 1;
  541. } else if (renderIsLongitudeRangeZero) {
  542. shaderDefines[
  543. "ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO"
  544. ] = true;
  545. intersectionCount += 2;
  546. }
  547. shaderUniforms.ellipsoidRenderLongitudeMinMax = Cartesian2.fromElements(
  548. renderMinLongitude,
  549. renderMaxLongitude,
  550. shaderUniforms.ellipsoidRenderLongitudeMinMax
  551. );
  552. }
  553. if (shapeHasLongitude) {
  554. shaderDefines["ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE"] = true;
  555. const shapeIsLongitudeReversed = shapeMaxLongitude < shapeMinLongitude;
  556. if (shapeIsLongitudeReversed) {
  557. shaderDefines[
  558. "ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED"
  559. ] = true;
  560. }
  561. // delerp(longitudeUv, minLongitudeUv, maxLongitudeUv)
  562. // (longitudeUv - minLongitudeUv) / (maxLongitudeUv - minLongitudeUv)
  563. // longitudeUv / (maxLongitudeUv - minLongitudeUv) - minLongitudeUv / (maxLongitudeUv - minLongitudeUv)
  564. // scale = 1.0 / (maxLongitudeUv - minLongitudeUv)
  565. // scale = 1.0 / (((maxLongitude - pi) / (2.0 * pi)) - ((minLongitude - pi) / (2.0 * pi)))
  566. // scale = 2.0 * pi / (maxLongitude - minLongitude)
  567. // offset = -minLongitudeUv / (maxLongitudeUv - minLongitudeUv)
  568. // offset = -((minLongitude - pi) / (2.0 * pi)) / (((maxLongitude - pi) / (2.0 * pi)) - ((minLongitude - pi) / (2.0 * pi)))
  569. // offset = -(minLongitude - pi) / (maxLongitude - minLongitude)
  570. const scale = defaultLongitudeRange / shapeLongitudeRange;
  571. const offset =
  572. -(shapeMinLongitude - defaultMinLongitude) / shapeLongitudeRange;
  573. shaderUniforms.ellipsoidUvToShapeUvLongitude = Cartesian2.fromElements(
  574. scale,
  575. offset,
  576. shaderUniforms.ellipsoidUvToShapeUvLongitude
  577. );
  578. }
  579. if (renderHasLongitude) {
  580. const renderIsMinLongitudeDiscontinuity = CesiumMath.equalsEpsilon(
  581. renderMinLongitude,
  582. defaultMinLongitude,
  583. undefined,
  584. epsilonLongitudeDiscontinuity
  585. );
  586. const renderIsMaxLongitudeDiscontinuity = CesiumMath.equalsEpsilon(
  587. renderMaxLongitude,
  588. defaultMaxLongitude,
  589. undefined,
  590. epsilonLongitudeDiscontinuity
  591. );
  592. if (renderIsMinLongitudeDiscontinuity) {
  593. shaderDefines[
  594. "ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY"
  595. ] = true;
  596. }
  597. if (renderIsMaxLongitudeDiscontinuity) {
  598. shaderDefines[
  599. "ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY"
  600. ] = true;
  601. }
  602. const uvShapeMinLongitude =
  603. (shapeMinLongitude - defaultMinLongitude) / defaultLongitudeRange;
  604. const uvShapeMaxLongitude =
  605. (shapeMaxLongitude - defaultMinLongitude) / defaultLongitudeRange;
  606. const uvRenderMaxLongitude =
  607. (renderMaxLongitude - defaultMinLongitude) / defaultLongitudeRange;
  608. const uvRenderLongitudeRangeZero =
  609. 1.0 - renderLongitudeRange / defaultLongitudeRange;
  610. const uvRenderLongitudeRangeZeroMid =
  611. (uvRenderMaxLongitude + 0.5 * uvRenderLongitudeRangeZero) % 1.0;
  612. shaderUniforms.ellipsoidShapeUvLongitudeMinMaxMid = Cartesian3.fromElements(
  613. uvShapeMinLongitude,
  614. uvShapeMaxLongitude,
  615. uvRenderLongitudeRangeZeroMid,
  616. shaderUniforms.ellipsoidShapeUvLongitudeMinMaxMid
  617. );
  618. }
  619. if (renderHasLatitude) {
  620. shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE"] = true;
  621. // Intersects a cone for min latitude
  622. if (renderHasLatitudeMin) {
  623. shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN"] = true;
  624. shaderDefines[
  625. "ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN"
  626. ] = intersectionCount;
  627. if (renderIsLatitudeMinUnderHalf) {
  628. shaderDefines[
  629. "ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF"
  630. ] = true;
  631. intersectionCount += 1;
  632. } else if (renderIsLatitudeMinHalf) {
  633. shaderDefines[
  634. "ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF"
  635. ] = true;
  636. intersectionCount += 1;
  637. } else if (renderIsLatitudeMinOverHalf) {
  638. shaderDefines[
  639. "ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF"
  640. ] = true;
  641. intersectionCount += 2;
  642. }
  643. }
  644. // Intersects a cone for max latitude
  645. if (renderHasLatitudeMax) {
  646. shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX"] = true;
  647. shaderDefines[
  648. "ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX"
  649. ] = intersectionCount;
  650. if (renderIsLatitudeMaxUnderHalf) {
  651. shaderDefines[
  652. "ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF"
  653. ] = true;
  654. intersectionCount += 2;
  655. } else if (renderIsLatitudeMaxHalf) {
  656. shaderDefines[
  657. "ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF"
  658. ] = true;
  659. intersectionCount += 1;
  660. } else if (renderIsLatitudeMaxOverHalf) {
  661. shaderDefines[
  662. "ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF"
  663. ] = true;
  664. intersectionCount += 1;
  665. }
  666. }
  667. if (renderMinLatitude === renderMaxLatitude) {
  668. shaderDefines[
  669. "ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO"
  670. ] = true;
  671. }
  672. const minCosHalfAngleSqr = Math.pow(
  673. Math.cos(CesiumMath.PI_OVER_TWO - Math.abs(renderMinLatitude)),
  674. 2.0
  675. );
  676. const maxCosHalfAngleSqr = Math.pow(
  677. Math.cos(CesiumMath.PI_OVER_TWO - Math.abs(renderMaxLatitude)),
  678. 2.0
  679. );
  680. shaderUniforms.ellipsoidRenderLatitudeCosSqrHalfMinMax = Cartesian2.fromElements(
  681. minCosHalfAngleSqr,
  682. maxCosHalfAngleSqr,
  683. shaderUniforms.ellipsoidRenderLatitudeCosSqrHalfMinMax
  684. );
  685. }
  686. if (shapeHasLatitude) {
  687. shaderDefines["ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE"] = true;
  688. if (shapeMinLatitude === shapeMaxLatitude) {
  689. shaderDefines[
  690. "ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO"
  691. ] = true;
  692. }
  693. // delerp(latitudeUv, minLatitudeUv, maxLatitudeUv)
  694. // (latitudeUv - minLatitudeUv) / (maxLatitudeUv - minLatitudeUv)
  695. // latitudeUv / (maxLatitudeUv - minLatitudeUv) - minLatitudeUv / (maxLatitudeUv - minLatitudeUv)
  696. // scale = 1.0 / (maxLatitudeUv - minLatitudeUv)
  697. // scale = 1.0 / (((maxLatitude - pi) / (2.0 * pi)) - ((minLatitude - pi) / (2.0 * pi)))
  698. // scale = 2.0 * pi / (maxLatitude - minLatitude)
  699. // offset = -minLatitudeUv / (maxLatitudeUv - minLatitudeUv)
  700. // offset = -((minLatitude - -pi) / (2.0 * pi)) / (((maxLatitude - pi) / (2.0 * pi)) - ((minLatitude - pi) / (2.0 * pi)))
  701. // offset = -(minLatitude - -pi) / (maxLatitude - minLatitude)
  702. // offset = (-pi - minLatitude) / (maxLatitude - minLatitude)
  703. const scale = defaultLatitudeRange / shapeLatitudeRange;
  704. const offset = (defaultMinLatitude - shapeMinLatitude) / shapeLatitudeRange;
  705. shaderUniforms.ellipsoidUvToShapeUvLatitude = Cartesian2.fromElements(
  706. scale,
  707. offset,
  708. shaderUniforms.ellipsoidUvToShapeUvLatitude
  709. );
  710. }
  711. if (isSphere) {
  712. shaderDefines["ELLIPSOID_IS_SPHERE"] = true;
  713. }
  714. this.shaderMaximumIntersectionsLength = intersectionCount;
  715. return true;
  716. };
  717. const scratchRectangle = new Rectangle();
  718. /**
  719. * Computes an oriented bounding box for a specified tile.
  720. * The update function must be called before calling this function.
  721. *
  722. * @param {number} tileLevel The tile's level.
  723. * @param {number} tileX The tile's x coordinate.
  724. * @param {number} tileY The tile's y coordinate.
  725. * @param {number} tileZ The tile's z coordinate.
  726. * @param {OrientedBoundingBox} result The oriented bounding box that will be set to enclose the specified tile
  727. * @returns {OrientedBoundingBox} The oriented bounding box.
  728. */
  729. VoxelEllipsoidShape.prototype.computeOrientedBoundingBoxForTile = function (
  730. tileLevel,
  731. tileX,
  732. tileY,
  733. tileZ,
  734. result
  735. ) {
  736. //>>includeStart('debug', pragmas.debug);
  737. Check.typeOf.number("tileLevel", tileLevel);
  738. Check.typeOf.number("tileX", tileX);
  739. Check.typeOf.number("tileY", tileY);
  740. Check.typeOf.number("tileZ", tileZ);
  741. Check.typeOf.object("result", result);
  742. //>>includeEnd('debug');
  743. const sizeAtLevel = 1.0 / Math.pow(2.0, tileLevel);
  744. const minLongitudeLerp = tileX * sizeAtLevel;
  745. const maxLongitudeLerp = (tileX + 1) * sizeAtLevel;
  746. const minLatitudeLerp = tileY * sizeAtLevel;
  747. const maxLatitudeLerp = (tileY + 1) * sizeAtLevel;
  748. const minHeightLerp = tileZ * sizeAtLevel;
  749. const maxHeightLerp = (tileZ + 1) * sizeAtLevel;
  750. const rectangle = Rectangle.subsection(
  751. this._rectangle,
  752. minLongitudeLerp,
  753. minLatitudeLerp,
  754. maxLongitudeLerp,
  755. maxLatitudeLerp,
  756. scratchRectangle
  757. );
  758. const minHeight = CesiumMath.lerp(
  759. this._minimumHeight,
  760. this._maximumHeight,
  761. minHeightLerp
  762. );
  763. const maxHeight = CesiumMath.lerp(
  764. this._minimumHeight,
  765. this._maximumHeight,
  766. maxHeightLerp
  767. );
  768. return getEllipsoidChunkObb(
  769. rectangle,
  770. minHeight,
  771. maxHeight,
  772. this._ellipsoid,
  773. this._translation,
  774. this._rotation,
  775. result
  776. );
  777. };
  778. /**
  779. * Computes an approximate step size for raymarching the root tile of a voxel grid.
  780. * The update function must be called before calling this function.
  781. *
  782. * @param {Cartesian3} dimensions The voxel grid dimensions for a tile.
  783. * @returns {number} The step size.
  784. */
  785. VoxelEllipsoidShape.prototype.computeApproximateStepSize = function (
  786. dimensions
  787. ) {
  788. //>>includeStart('debug', pragmas.debug);
  789. Check.typeOf.object("dimensions", dimensions);
  790. //>>includeEnd('debug');
  791. const ellipsoid = this._ellipsoid;
  792. const ellipsoidMaximumRadius = ellipsoid.maximumRadius;
  793. const minimumHeight = this._minimumHeight;
  794. const maximumHeight = this._maximumHeight;
  795. const shellToEllipsoidRatio =
  796. (maximumHeight - minimumHeight) / (ellipsoidMaximumRadius + maximumHeight);
  797. const stepSize = (0.5 * shellToEllipsoidRatio) / dimensions.z;
  798. return stepSize;
  799. };
  800. /**
  801. * Computes an {@link OrientedBoundingBox} for a subregion of the shape.
  802. *
  803. * @function
  804. *
  805. * @param {Rectangle} rectangle The rectangle.
  806. * @param {number} minHeight The minimumZ.
  807. * @param {number} maxHeight The maximumZ.
  808. * @param {Ellipsoid} ellipsoid The ellipsoid.
  809. * @param {Cartesian3} translation The translation applied to the shape
  810. * @param {Matrix3} rotation The rotation applied to the shape
  811. * @param {OrientedBoundingBox} result The object onto which to store the result.
  812. * @returns {OrientedBoundingBox} The oriented bounding box that contains this subregion.
  813. *
  814. * @private
  815. */
  816. function getEllipsoidChunkObb(
  817. rectangle,
  818. minHeight,
  819. maxHeight,
  820. ellipsoid,
  821. translation,
  822. rotation,
  823. result
  824. ) {
  825. result = OrientedBoundingBox.fromRectangle(
  826. rectangle,
  827. minHeight,
  828. maxHeight,
  829. ellipsoid,
  830. result
  831. );
  832. result.center = Cartesian3.add(result.center, translation, result.center);
  833. result.halfAxes = Matrix3.multiply(
  834. result.halfAxes,
  835. rotation,
  836. result.halfAxes
  837. );
  838. return result;
  839. }
  840. /**
  841. * Defines the minimum bounds of the shape. Corresponds to minimum longitude, latitude, height.
  842. *
  843. * @type {Cartesian3}
  844. * @constant
  845. * @readonly
  846. */
  847. VoxelEllipsoidShape.DefaultMinBounds = Object.freeze(
  848. new Cartesian3(-CesiumMath.PI, -CesiumMath.PI_OVER_TWO, -Number.MAX_VALUE)
  849. );
  850. /**
  851. * Defines the maximum bounds of the shape. Corresponds to maximum longitude, latitude, height.
  852. *
  853. * @type {Cartesian3}
  854. * @constant
  855. * @readonly
  856. */
  857. VoxelEllipsoidShape.DefaultMaxBounds = Object.freeze(
  858. new Cartesian3(+CesiumMath.PI, +CesiumMath.PI_OVER_TWO, +Number.MAX_VALUE)
  859. );
  860. export default VoxelEllipsoidShape;