TerrainEncoding-a8a1f120.js 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249
  1. /**
  2. * Cesium - https://github.com/CesiumGS/cesium
  3. *
  4. * Copyright 2011-2020 Cesium Contributors
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. *
  18. * Columbus View (Pat. Pend.)
  19. *
  20. * Portions licensed separately.
  21. * See https://github.com/CesiumGS/cesium/blob/main/LICENSE.md for full licensing details.
  22. */
  23. define(['exports', './Transforms-f0a54c7b', './Matrix2-d35cf4b5', './RuntimeError-8952249c', './defaultValue-81eec7ed', './AttributeCompression-d0b97a83', './ComponentDatatype-9e86ac8f'], (function (exports, Transforms, Matrix2, RuntimeError, defaultValue, AttributeCompression, ComponentDatatype) { 'use strict';
  24. /**
  25. * Determine whether or not other objects are visible or hidden behind the visible horizon defined by
  26. * an {@link Ellipsoid} and a camera position. The ellipsoid is assumed to be located at the
  27. * origin of the coordinate system. This class uses the algorithm described in the
  28. * {@link https://cesium.com/blog/2013/04/25/Horizon-culling/|Horizon Culling} blog post.
  29. *
  30. * @alias EllipsoidalOccluder
  31. *
  32. * @param {Ellipsoid} ellipsoid The ellipsoid to use as an occluder.
  33. * @param {Cartesian3} [cameraPosition] The coordinate of the viewer/camera. If this parameter is not
  34. * specified, {@link EllipsoidalOccluder#cameraPosition} must be called before
  35. * testing visibility.
  36. *
  37. * @constructor
  38. *
  39. * @example
  40. * // Construct an ellipsoidal occluder with radii 1.0, 1.1, and 0.9.
  41. * const cameraPosition = new Cesium.Cartesian3(5.0, 6.0, 7.0);
  42. * const occluderEllipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9);
  43. * const occluder = new Cesium.EllipsoidalOccluder(occluderEllipsoid, cameraPosition);
  44. *
  45. * @private
  46. */
  47. function EllipsoidalOccluder(ellipsoid, cameraPosition) {
  48. //>>includeStart('debug', pragmas.debug);
  49. RuntimeError.Check.typeOf.object("ellipsoid", ellipsoid);
  50. //>>includeEnd('debug');
  51. this._ellipsoid = ellipsoid;
  52. this._cameraPosition = new Matrix2.Cartesian3();
  53. this._cameraPositionInScaledSpace = new Matrix2.Cartesian3();
  54. this._distanceToLimbInScaledSpaceSquared = 0.0;
  55. // cameraPosition fills in the above values
  56. if (defaultValue.defined(cameraPosition)) {
  57. this.cameraPosition = cameraPosition;
  58. }
  59. }
  60. Object.defineProperties(EllipsoidalOccluder.prototype, {
  61. /**
  62. * Gets the occluding ellipsoid.
  63. * @memberof EllipsoidalOccluder.prototype
  64. * @type {Ellipsoid}
  65. */
  66. ellipsoid: {
  67. get: function () {
  68. return this._ellipsoid;
  69. },
  70. },
  71. /**
  72. * Gets or sets the position of the camera.
  73. * @memberof EllipsoidalOccluder.prototype
  74. * @type {Cartesian3}
  75. */
  76. cameraPosition: {
  77. get: function () {
  78. return this._cameraPosition;
  79. },
  80. set: function (cameraPosition) {
  81. // See https://cesium.com/blog/2013/04/25/Horizon-culling/
  82. const ellipsoid = this._ellipsoid;
  83. const cv = ellipsoid.transformPositionToScaledSpace(
  84. cameraPosition,
  85. this._cameraPositionInScaledSpace
  86. );
  87. const vhMagnitudeSquared = Matrix2.Cartesian3.magnitudeSquared(cv) - 1.0;
  88. Matrix2.Cartesian3.clone(cameraPosition, this._cameraPosition);
  89. this._cameraPositionInScaledSpace = cv;
  90. this._distanceToLimbInScaledSpaceSquared = vhMagnitudeSquared;
  91. },
  92. },
  93. });
  94. const scratchCartesian = new Matrix2.Cartesian3();
  95. /**
  96. * Determines whether or not a point, the <code>occludee</code>, is hidden from view by the occluder.
  97. *
  98. * @param {Cartesian3} occludee The point to test for visibility.
  99. * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.
  100. *
  101. * @example
  102. * const cameraPosition = new Cesium.Cartesian3(0, 0, 2.5);
  103. * const ellipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9);
  104. * const occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition);
  105. * const point = new Cesium.Cartesian3(0, -3, -3);
  106. * occluder.isPointVisible(point); //returns true
  107. */
  108. EllipsoidalOccluder.prototype.isPointVisible = function (occludee) {
  109. const ellipsoid = this._ellipsoid;
  110. const occludeeScaledSpacePosition = ellipsoid.transformPositionToScaledSpace(
  111. occludee,
  112. scratchCartesian
  113. );
  114. return isScaledSpacePointVisible(
  115. occludeeScaledSpacePosition,
  116. this._cameraPositionInScaledSpace,
  117. this._distanceToLimbInScaledSpaceSquared
  118. );
  119. };
  120. /**
  121. * Determines whether or not a point expressed in the ellipsoid scaled space, is hidden from view by the
  122. * occluder. To transform a Cartesian X, Y, Z position in the coordinate system aligned with the ellipsoid
  123. * into the scaled space, call {@link Ellipsoid#transformPositionToScaledSpace}.
  124. *
  125. * @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space.
  126. * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.
  127. *
  128. * @example
  129. * const cameraPosition = new Cesium.Cartesian3(0, 0, 2.5);
  130. * const ellipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9);
  131. * const occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition);
  132. * const point = new Cesium.Cartesian3(0, -3, -3);
  133. * const scaledSpacePoint = ellipsoid.transformPositionToScaledSpace(point);
  134. * occluder.isScaledSpacePointVisible(scaledSpacePoint); //returns true
  135. */
  136. EllipsoidalOccluder.prototype.isScaledSpacePointVisible = function (
  137. occludeeScaledSpacePosition
  138. ) {
  139. return isScaledSpacePointVisible(
  140. occludeeScaledSpacePosition,
  141. this._cameraPositionInScaledSpace,
  142. this._distanceToLimbInScaledSpaceSquared
  143. );
  144. };
  145. const scratchCameraPositionInScaledSpaceShrunk = new Matrix2.Cartesian3();
  146. /**
  147. * Similar to {@link EllipsoidalOccluder#isScaledSpacePointVisible} except tests against an
  148. * ellipsoid that has been shrunk by the minimum height when the minimum height is below
  149. * the ellipsoid. This is intended to be used with points generated by
  150. * {@link EllipsoidalOccluder#computeHorizonCullingPointPossiblyUnderEllipsoid} or
  151. * {@link EllipsoidalOccluder#computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid}.
  152. *
  153. * @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space of the possibly-shrunk ellipsoid.
  154. * @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.
  155. */
  156. EllipsoidalOccluder.prototype.isScaledSpacePointVisiblePossiblyUnderEllipsoid = function (
  157. occludeeScaledSpacePosition,
  158. minimumHeight
  159. ) {
  160. const ellipsoid = this._ellipsoid;
  161. let vhMagnitudeSquared;
  162. let cv;
  163. if (
  164. defaultValue.defined(minimumHeight) &&
  165. minimumHeight < 0.0 &&
  166. ellipsoid.minimumRadius > -minimumHeight
  167. ) {
  168. // This code is similar to the cameraPosition setter, but unrolled for performance because it will be called a lot.
  169. cv = scratchCameraPositionInScaledSpaceShrunk;
  170. cv.x = this._cameraPosition.x / (ellipsoid.radii.x + minimumHeight);
  171. cv.y = this._cameraPosition.y / (ellipsoid.radii.y + minimumHeight);
  172. cv.z = this._cameraPosition.z / (ellipsoid.radii.z + minimumHeight);
  173. vhMagnitudeSquared = cv.x * cv.x + cv.y * cv.y + cv.z * cv.z - 1.0;
  174. } else {
  175. cv = this._cameraPositionInScaledSpace;
  176. vhMagnitudeSquared = this._distanceToLimbInScaledSpaceSquared;
  177. }
  178. return isScaledSpacePointVisible(
  179. occludeeScaledSpacePosition,
  180. cv,
  181. vhMagnitudeSquared
  182. );
  183. };
  184. /**
  185. * Computes a point that can be used for horizon culling from a list of positions. If the point is below
  186. * the horizon, all of the positions are guaranteed to be below the horizon as well. The returned point
  187. * is expressed in the ellipsoid-scaled space and is suitable for use with
  188. * {@link EllipsoidalOccluder#isScaledSpacePointVisible}.
  189. *
  190. * @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
  191. * A reasonable direction to use is the direction from the center of the ellipsoid to
  192. * the center of the bounding sphere computed from the positions. The direction need not
  193. * be normalized.
  194. * @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions
  195. * must be expressed in a reference frame centered at the ellipsoid and aligned with the
  196. * ellipsoid's axes.
  197. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
  198. * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space.
  199. */
  200. EllipsoidalOccluder.prototype.computeHorizonCullingPoint = function (
  201. directionToPoint,
  202. positions,
  203. result
  204. ) {
  205. return computeHorizonCullingPointFromPositions(
  206. this._ellipsoid,
  207. directionToPoint,
  208. positions,
  209. result
  210. );
  211. };
  212. const scratchEllipsoidShrunk = Matrix2.Ellipsoid.clone(Matrix2.Ellipsoid.UNIT_SPHERE);
  213. /**
  214. * Similar to {@link EllipsoidalOccluder#computeHorizonCullingPoint} except computes the culling
  215. * point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below
  216. * the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable
  217. * for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}.
  218. *
  219. * @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
  220. * A reasonable direction to use is the direction from the center of the ellipsoid to
  221. * the center of the bounding sphere computed from the positions. The direction need not
  222. * be normalized.
  223. * @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions
  224. * must be expressed in a reference frame centered at the ellipsoid and aligned with the
  225. * ellipsoid's axes.
  226. * @param {Number} [minimumHeight] The minimum height of all positions. If this value is undefined, all positions are assumed to be above the ellipsoid.
  227. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
  228. * @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space.
  229. */
  230. EllipsoidalOccluder.prototype.computeHorizonCullingPointPossiblyUnderEllipsoid = function (
  231. directionToPoint,
  232. positions,
  233. minimumHeight,
  234. result
  235. ) {
  236. const possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(
  237. this._ellipsoid,
  238. minimumHeight,
  239. scratchEllipsoidShrunk
  240. );
  241. return computeHorizonCullingPointFromPositions(
  242. possiblyShrunkEllipsoid,
  243. directionToPoint,
  244. positions,
  245. result
  246. );
  247. };
  248. /**
  249. * Computes a point that can be used for horizon culling from a list of positions. If the point is below
  250. * the horizon, all of the positions are guaranteed to be below the horizon as well. The returned point
  251. * is expressed in the ellipsoid-scaled space and is suitable for use with
  252. * {@link EllipsoidalOccluder#isScaledSpacePointVisible}.
  253. *
  254. * @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
  255. * A reasonable direction to use is the direction from the center of the ellipsoid to
  256. * the center of the bounding sphere computed from the positions. The direction need not
  257. * be normalized.
  258. * @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions
  259. * must be expressed in a reference frame centered at the ellipsoid and aligned with the
  260. * ellipsoid's axes.
  261. * @param {Number} [stride=3]
  262. * @param {Cartesian3} [center=Cartesian3.ZERO]
  263. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
  264. * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space.
  265. */
  266. EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVertices = function (
  267. directionToPoint,
  268. vertices,
  269. stride,
  270. center,
  271. result
  272. ) {
  273. return computeHorizonCullingPointFromVertices(
  274. this._ellipsoid,
  275. directionToPoint,
  276. vertices,
  277. stride,
  278. center,
  279. result
  280. );
  281. };
  282. /**
  283. * Similar to {@link EllipsoidalOccluder#computeHorizonCullingPointFromVertices} except computes the culling
  284. * point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below
  285. * the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable
  286. * for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}.
  287. *
  288. * @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
  289. * A reasonable direction to use is the direction from the center of the ellipsoid to
  290. * the center of the bounding sphere computed from the positions. The direction need not
  291. * be normalized.
  292. * @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions
  293. * must be expressed in a reference frame centered at the ellipsoid and aligned with the
  294. * ellipsoid's axes.
  295. * @param {Number} [stride=3]
  296. * @param {Cartesian3} [center=Cartesian3.ZERO]
  297. * @param {Number} [minimumHeight] The minimum height of all vertices. If this value is undefined, all vertices are assumed to be above the ellipsoid.
  298. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
  299. * @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space.
  300. */
  301. EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid = function (
  302. directionToPoint,
  303. vertices,
  304. stride,
  305. center,
  306. minimumHeight,
  307. result
  308. ) {
  309. const possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid(
  310. this._ellipsoid,
  311. minimumHeight,
  312. scratchEllipsoidShrunk
  313. );
  314. return computeHorizonCullingPointFromVertices(
  315. possiblyShrunkEllipsoid,
  316. directionToPoint,
  317. vertices,
  318. stride,
  319. center,
  320. result
  321. );
  322. };
  323. const subsampleScratch = [];
  324. /**
  325. * Computes a point that can be used for horizon culling of a rectangle. If the point is below
  326. * the horizon, the ellipsoid-conforming rectangle is guaranteed to be below the horizon as well.
  327. * The returned point is expressed in the ellipsoid-scaled space and is suitable for use with
  328. * {@link EllipsoidalOccluder#isScaledSpacePointVisible}.
  329. *
  330. * @param {Rectangle} rectangle The rectangle for which to compute the horizon culling point.
  331. * @param {Ellipsoid} ellipsoid The ellipsoid on which the rectangle is defined. This may be different from
  332. * the ellipsoid used by this instance for occlusion testing.
  333. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
  334. * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space.
  335. */
  336. EllipsoidalOccluder.prototype.computeHorizonCullingPointFromRectangle = function (
  337. rectangle,
  338. ellipsoid,
  339. result
  340. ) {
  341. //>>includeStart('debug', pragmas.debug);
  342. RuntimeError.Check.typeOf.object("rectangle", rectangle);
  343. //>>includeEnd('debug');
  344. const positions = Matrix2.Rectangle.subsample(
  345. rectangle,
  346. ellipsoid,
  347. 0.0,
  348. subsampleScratch
  349. );
  350. const bs = Transforms.BoundingSphere.fromPoints(positions);
  351. // If the bounding sphere center is too close to the center of the occluder, it doesn't make
  352. // sense to try to horizon cull it.
  353. if (Matrix2.Cartesian3.magnitude(bs.center) < 0.1 * ellipsoid.minimumRadius) {
  354. return undefined;
  355. }
  356. return this.computeHorizonCullingPoint(bs.center, positions, result);
  357. };
  358. const scratchEllipsoidShrunkRadii = new Matrix2.Cartesian3();
  359. function getPossiblyShrunkEllipsoid(ellipsoid, minimumHeight, result) {
  360. if (
  361. defaultValue.defined(minimumHeight) &&
  362. minimumHeight < 0.0 &&
  363. ellipsoid.minimumRadius > -minimumHeight
  364. ) {
  365. const ellipsoidShrunkRadii = Matrix2.Cartesian3.fromElements(
  366. ellipsoid.radii.x + minimumHeight,
  367. ellipsoid.radii.y + minimumHeight,
  368. ellipsoid.radii.z + minimumHeight,
  369. scratchEllipsoidShrunkRadii
  370. );
  371. ellipsoid = Matrix2.Ellipsoid.fromCartesian3(ellipsoidShrunkRadii, result);
  372. }
  373. return ellipsoid;
  374. }
  375. function computeHorizonCullingPointFromPositions(
  376. ellipsoid,
  377. directionToPoint,
  378. positions,
  379. result
  380. ) {
  381. //>>includeStart('debug', pragmas.debug);
  382. RuntimeError.Check.typeOf.object("directionToPoint", directionToPoint);
  383. RuntimeError.Check.defined("positions", positions);
  384. //>>includeEnd('debug');
  385. if (!defaultValue.defined(result)) {
  386. result = new Matrix2.Cartesian3();
  387. }
  388. const scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(
  389. ellipsoid,
  390. directionToPoint
  391. );
  392. let resultMagnitude = 0.0;
  393. for (let i = 0, len = positions.length; i < len; ++i) {
  394. const position = positions[i];
  395. const candidateMagnitude = computeMagnitude(
  396. ellipsoid,
  397. position,
  398. scaledSpaceDirectionToPoint
  399. );
  400. if (candidateMagnitude < 0.0) {
  401. // all points should face the same direction, but this one doesn't, so return undefined
  402. return undefined;
  403. }
  404. resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);
  405. }
  406. return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
  407. }
  408. const positionScratch = new Matrix2.Cartesian3();
  409. function computeHorizonCullingPointFromVertices(
  410. ellipsoid,
  411. directionToPoint,
  412. vertices,
  413. stride,
  414. center,
  415. result
  416. ) {
  417. //>>includeStart('debug', pragmas.debug);
  418. RuntimeError.Check.typeOf.object("directionToPoint", directionToPoint);
  419. RuntimeError.Check.defined("vertices", vertices);
  420. RuntimeError.Check.typeOf.number("stride", stride);
  421. //>>includeEnd('debug');
  422. if (!defaultValue.defined(result)) {
  423. result = new Matrix2.Cartesian3();
  424. }
  425. stride = defaultValue.defaultValue(stride, 3);
  426. center = defaultValue.defaultValue(center, Matrix2.Cartesian3.ZERO);
  427. const scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint(
  428. ellipsoid,
  429. directionToPoint
  430. );
  431. let resultMagnitude = 0.0;
  432. for (let i = 0, len = vertices.length; i < len; i += stride) {
  433. positionScratch.x = vertices[i] + center.x;
  434. positionScratch.y = vertices[i + 1] + center.y;
  435. positionScratch.z = vertices[i + 2] + center.z;
  436. const candidateMagnitude = computeMagnitude(
  437. ellipsoid,
  438. positionScratch,
  439. scaledSpaceDirectionToPoint
  440. );
  441. if (candidateMagnitude < 0.0) {
  442. // all points should face the same direction, but this one doesn't, so return undefined
  443. return undefined;
  444. }
  445. resultMagnitude = Math.max(resultMagnitude, candidateMagnitude);
  446. }
  447. return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
  448. }
  449. function isScaledSpacePointVisible(
  450. occludeeScaledSpacePosition,
  451. cameraPositionInScaledSpace,
  452. distanceToLimbInScaledSpaceSquared
  453. ) {
  454. // See https://cesium.com/blog/2013/04/25/Horizon-culling/
  455. const cv = cameraPositionInScaledSpace;
  456. const vhMagnitudeSquared = distanceToLimbInScaledSpaceSquared;
  457. const vt = Matrix2.Cartesian3.subtract(
  458. occludeeScaledSpacePosition,
  459. cv,
  460. scratchCartesian
  461. );
  462. const vtDotVc = -Matrix2.Cartesian3.dot(vt, cv);
  463. // If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and
  464. // in this case, set the culling plane to be on V.
  465. const isOccluded =
  466. vhMagnitudeSquared < 0
  467. ? vtDotVc > 0
  468. : vtDotVc > vhMagnitudeSquared &&
  469. (vtDotVc * vtDotVc) / Matrix2.Cartesian3.magnitudeSquared(vt) >
  470. vhMagnitudeSquared;
  471. return !isOccluded;
  472. }
  473. const scaledSpaceScratch = new Matrix2.Cartesian3();
  474. const directionScratch = new Matrix2.Cartesian3();
  475. function computeMagnitude(ellipsoid, position, scaledSpaceDirectionToPoint) {
  476. const scaledSpacePosition = ellipsoid.transformPositionToScaledSpace(
  477. position,
  478. scaledSpaceScratch
  479. );
  480. let magnitudeSquared = Matrix2.Cartesian3.magnitudeSquared(scaledSpacePosition);
  481. let magnitude = Math.sqrt(magnitudeSquared);
  482. const direction = Matrix2.Cartesian3.divideByScalar(
  483. scaledSpacePosition,
  484. magnitude,
  485. directionScratch
  486. );
  487. // For the purpose of this computation, points below the ellipsoid are consider to be on it instead.
  488. magnitudeSquared = Math.max(1.0, magnitudeSquared);
  489. magnitude = Math.max(1.0, magnitude);
  490. const cosAlpha = Matrix2.Cartesian3.dot(direction, scaledSpaceDirectionToPoint);
  491. const sinAlpha = Matrix2.Cartesian3.magnitude(
  492. Matrix2.Cartesian3.cross(direction, scaledSpaceDirectionToPoint, direction)
  493. );
  494. const cosBeta = 1.0 / magnitude;
  495. const sinBeta = Math.sqrt(magnitudeSquared - 1.0) * cosBeta;
  496. return 1.0 / (cosAlpha * cosBeta - sinAlpha * sinBeta);
  497. }
  498. function magnitudeToPoint(
  499. scaledSpaceDirectionToPoint,
  500. resultMagnitude,
  501. result
  502. ) {
  503. // The horizon culling point is undefined if there were no positions from which to compute it,
  504. // the directionToPoint is pointing opposite all of the positions, or if we computed NaN or infinity.
  505. if (
  506. resultMagnitude <= 0.0 ||
  507. resultMagnitude === 1.0 / 0.0 ||
  508. resultMagnitude !== resultMagnitude
  509. ) {
  510. return undefined;
  511. }
  512. return Matrix2.Cartesian3.multiplyByScalar(
  513. scaledSpaceDirectionToPoint,
  514. resultMagnitude,
  515. result
  516. );
  517. }
  518. const directionToPointScratch = new Matrix2.Cartesian3();
  519. function computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint) {
  520. if (Matrix2.Cartesian3.equals(directionToPoint, Matrix2.Cartesian3.ZERO)) {
  521. return directionToPoint;
  522. }
  523. ellipsoid.transformPositionToScaledSpace(
  524. directionToPoint,
  525. directionToPointScratch
  526. );
  527. return Matrix2.Cartesian3.normalize(directionToPointScratch, directionToPointScratch);
  528. }
  529. /**
  530. * @private
  531. */
  532. const TerrainExaggeration = {};
  533. /**
  534. * Scales a height relative to an offset.
  535. *
  536. * @param {Number} height The height.
  537. * @param {Number} scale A scalar used to exaggerate the terrain. If the value is 1.0 there will be no effect.
  538. * @param {Number} relativeHeight The height relative to which terrain is exaggerated. If the value is 0.0 terrain will be exaggerated relative to the ellipsoid surface.
  539. */
  540. TerrainExaggeration.getHeight = function (height, scale, relativeHeight) {
  541. return (height - relativeHeight) * scale + relativeHeight;
  542. };
  543. const scratchCartographic = new Matrix2.Cartesian3();
  544. /**
  545. * Scales a position by exaggeration.
  546. */
  547. TerrainExaggeration.getPosition = function (
  548. position,
  549. ellipsoid,
  550. terrainExaggeration,
  551. terrainExaggerationRelativeHeight,
  552. result
  553. ) {
  554. const cartographic = ellipsoid.cartesianToCartographic(
  555. position,
  556. scratchCartographic
  557. );
  558. const newHeight = TerrainExaggeration.getHeight(
  559. cartographic.height,
  560. terrainExaggeration,
  561. terrainExaggerationRelativeHeight
  562. );
  563. return Matrix2.Cartesian3.fromRadians(
  564. cartographic.longitude,
  565. cartographic.latitude,
  566. newHeight,
  567. ellipsoid,
  568. result
  569. );
  570. };
  571. /**
  572. * This enumerated type is used to determine how the vertices of the terrain mesh are compressed.
  573. *
  574. * @enum {Number}
  575. *
  576. * @private
  577. */
  578. const TerrainQuantization = {
  579. /**
  580. * The vertices are not compressed.
  581. *
  582. * @type {Number}
  583. * @constant
  584. */
  585. NONE: 0,
  586. /**
  587. * The vertices are compressed to 12 bits.
  588. *
  589. * @type {Number}
  590. * @constant
  591. */
  592. BITS12: 1,
  593. };
  594. var TerrainQuantization$1 = Object.freeze(TerrainQuantization);
  595. const cartesian3Scratch = new Matrix2.Cartesian3();
  596. const cartesian3DimScratch = new Matrix2.Cartesian3();
  597. const cartesian2Scratch = new Matrix2.Cartesian2();
  598. const matrix4Scratch = new Matrix2.Matrix4();
  599. const matrix4Scratch2 = new Matrix2.Matrix4();
  600. const SHIFT_LEFT_12 = Math.pow(2.0, 12.0);
  601. /**
  602. * Data used to quantize and pack the terrain mesh. The position can be unpacked for picking and all attributes
  603. * are unpacked in the vertex shader.
  604. *
  605. * @alias TerrainEncoding
  606. * @constructor
  607. *
  608. * @param {Cartesian3} center The center point of the vertices.
  609. * @param {AxisAlignedBoundingBox} axisAlignedBoundingBox The bounds of the tile in the east-north-up coordinates at the tiles center.
  610. * @param {Number} minimumHeight The minimum height.
  611. * @param {Number} maximumHeight The maximum height.
  612. * @param {Matrix4} fromENU The east-north-up to fixed frame matrix at the center of the terrain mesh.
  613. * @param {Boolean} hasVertexNormals If the mesh has vertex normals.
  614. * @param {Boolean} [hasWebMercatorT=false] true if the terrain data includes a Web Mercator texture coordinate; otherwise, false.
  615. * @param {Boolean} [hasGeodeticSurfaceNormals=false] true if the terrain data includes geodetic surface normals; otherwise, false.
  616. * @param {Number} [exaggeration=1.0] A scalar used to exaggerate terrain.
  617. * @param {Number} [exaggerationRelativeHeight=0.0] The relative height from which terrain is exaggerated.
  618. *
  619. * @private
  620. */
  621. function TerrainEncoding(
  622. center,
  623. axisAlignedBoundingBox,
  624. minimumHeight,
  625. maximumHeight,
  626. fromENU,
  627. hasVertexNormals,
  628. hasWebMercatorT,
  629. hasGeodeticSurfaceNormals,
  630. exaggeration,
  631. exaggerationRelativeHeight
  632. ) {
  633. let quantization = TerrainQuantization$1.NONE;
  634. let toENU;
  635. let matrix;
  636. if (
  637. defaultValue.defined(axisAlignedBoundingBox) &&
  638. defaultValue.defined(minimumHeight) &&
  639. defaultValue.defined(maximumHeight) &&
  640. defaultValue.defined(fromENU)
  641. ) {
  642. const minimum = axisAlignedBoundingBox.minimum;
  643. const maximum = axisAlignedBoundingBox.maximum;
  644. const dimensions = Matrix2.Cartesian3.subtract(
  645. maximum,
  646. minimum,
  647. cartesian3DimScratch
  648. );
  649. const hDim = maximumHeight - minimumHeight;
  650. const maxDim = Math.max(Matrix2.Cartesian3.maximumComponent(dimensions), hDim);
  651. if (maxDim < SHIFT_LEFT_12 - 1.0) {
  652. quantization = TerrainQuantization$1.BITS12;
  653. } else {
  654. quantization = TerrainQuantization$1.NONE;
  655. }
  656. toENU = Matrix2.Matrix4.inverseTransformation(fromENU, new Matrix2.Matrix4());
  657. const translation = Matrix2.Cartesian3.negate(minimum, cartesian3Scratch);
  658. Matrix2.Matrix4.multiply(
  659. Matrix2.Matrix4.fromTranslation(translation, matrix4Scratch),
  660. toENU,
  661. toENU
  662. );
  663. const scale = cartesian3Scratch;
  664. scale.x = 1.0 / dimensions.x;
  665. scale.y = 1.0 / dimensions.y;
  666. scale.z = 1.0 / dimensions.z;
  667. Matrix2.Matrix4.multiply(Matrix2.Matrix4.fromScale(scale, matrix4Scratch), toENU, toENU);
  668. matrix = Matrix2.Matrix4.clone(fromENU);
  669. Matrix2.Matrix4.setTranslation(matrix, Matrix2.Cartesian3.ZERO, matrix);
  670. fromENU = Matrix2.Matrix4.clone(fromENU, new Matrix2.Matrix4());
  671. const translationMatrix = Matrix2.Matrix4.fromTranslation(minimum, matrix4Scratch);
  672. const scaleMatrix = Matrix2.Matrix4.fromScale(dimensions, matrix4Scratch2);
  673. const st = Matrix2.Matrix4.multiply(translationMatrix, scaleMatrix, matrix4Scratch);
  674. Matrix2.Matrix4.multiply(fromENU, st, fromENU);
  675. Matrix2.Matrix4.multiply(matrix, st, matrix);
  676. }
  677. /**
  678. * How the vertices of the mesh were compressed.
  679. * @type {TerrainQuantization}
  680. */
  681. this.quantization = quantization;
  682. /**
  683. * The minimum height of the tile including the skirts.
  684. * @type {Number}
  685. */
  686. this.minimumHeight = minimumHeight;
  687. /**
  688. * The maximum height of the tile.
  689. * @type {Number}
  690. */
  691. this.maximumHeight = maximumHeight;
  692. /**
  693. * The center of the tile.
  694. * @type {Cartesian3}
  695. */
  696. this.center = Matrix2.Cartesian3.clone(center);
  697. /**
  698. * A matrix that takes a vertex from the tile, transforms it to east-north-up at the center and scales
  699. * it so each component is in the [0, 1] range.
  700. * @type {Matrix4}
  701. */
  702. this.toScaledENU = toENU;
  703. /**
  704. * A matrix that restores a vertex transformed with toScaledENU back to the earth fixed reference frame
  705. * @type {Matrix4}
  706. */
  707. this.fromScaledENU = fromENU;
  708. /**
  709. * The matrix used to decompress the terrain vertices in the shader for RTE rendering.
  710. * @type {Matrix4}
  711. */
  712. this.matrix = matrix;
  713. /**
  714. * The terrain mesh contains normals.
  715. * @type {Boolean}
  716. */
  717. this.hasVertexNormals = hasVertexNormals;
  718. /**
  719. * The terrain mesh contains a vertical texture coordinate following the Web Mercator projection.
  720. * @type {Boolean}
  721. */
  722. this.hasWebMercatorT = defaultValue.defaultValue(hasWebMercatorT, false);
  723. /**
  724. * The terrain mesh contains geodetic surface normals, used for terrain exaggeration.
  725. * @type {Boolean}
  726. */
  727. this.hasGeodeticSurfaceNormals = defaultValue.defaultValue(
  728. hasGeodeticSurfaceNormals,
  729. false
  730. );
  731. /**
  732. * A scalar used to exaggerate terrain.
  733. * @type {Number}
  734. */
  735. this.exaggeration = defaultValue.defaultValue(exaggeration, 1.0);
  736. /**
  737. * The relative height from which terrain is exaggerated.
  738. */
  739. this.exaggerationRelativeHeight = defaultValue.defaultValue(
  740. exaggerationRelativeHeight,
  741. 0.0
  742. );
  743. /**
  744. * The number of components in each vertex. This value can differ with different quantizations.
  745. * @type {Number}
  746. */
  747. this.stride = 0;
  748. this._offsetGeodeticSurfaceNormal = 0;
  749. this._offsetVertexNormal = 0;
  750. // Calculate the stride and offsets declared above
  751. this._calculateStrideAndOffsets();
  752. }
  753. TerrainEncoding.prototype.encode = function (
  754. vertexBuffer,
  755. bufferIndex,
  756. position,
  757. uv,
  758. height,
  759. normalToPack,
  760. webMercatorT,
  761. geodeticSurfaceNormal
  762. ) {
  763. const u = uv.x;
  764. const v = uv.y;
  765. if (this.quantization === TerrainQuantization$1.BITS12) {
  766. position = Matrix2.Matrix4.multiplyByPoint(
  767. this.toScaledENU,
  768. position,
  769. cartesian3Scratch
  770. );
  771. position.x = ComponentDatatype.CesiumMath.clamp(position.x, 0.0, 1.0);
  772. position.y = ComponentDatatype.CesiumMath.clamp(position.y, 0.0, 1.0);
  773. position.z = ComponentDatatype.CesiumMath.clamp(position.z, 0.0, 1.0);
  774. const hDim = this.maximumHeight - this.minimumHeight;
  775. const h = ComponentDatatype.CesiumMath.clamp((height - this.minimumHeight) / hDim, 0.0, 1.0);
  776. Matrix2.Cartesian2.fromElements(position.x, position.y, cartesian2Scratch);
  777. const compressed0 = AttributeCompression.AttributeCompression.compressTextureCoordinates(
  778. cartesian2Scratch
  779. );
  780. Matrix2.Cartesian2.fromElements(position.z, h, cartesian2Scratch);
  781. const compressed1 = AttributeCompression.AttributeCompression.compressTextureCoordinates(
  782. cartesian2Scratch
  783. );
  784. Matrix2.Cartesian2.fromElements(u, v, cartesian2Scratch);
  785. const compressed2 = AttributeCompression.AttributeCompression.compressTextureCoordinates(
  786. cartesian2Scratch
  787. );
  788. vertexBuffer[bufferIndex++] = compressed0;
  789. vertexBuffer[bufferIndex++] = compressed1;
  790. vertexBuffer[bufferIndex++] = compressed2;
  791. if (this.hasWebMercatorT) {
  792. Matrix2.Cartesian2.fromElements(webMercatorT, 0.0, cartesian2Scratch);
  793. const compressed3 = AttributeCompression.AttributeCompression.compressTextureCoordinates(
  794. cartesian2Scratch
  795. );
  796. vertexBuffer[bufferIndex++] = compressed3;
  797. }
  798. } else {
  799. Matrix2.Cartesian3.subtract(position, this.center, cartesian3Scratch);
  800. vertexBuffer[bufferIndex++] = cartesian3Scratch.x;
  801. vertexBuffer[bufferIndex++] = cartesian3Scratch.y;
  802. vertexBuffer[bufferIndex++] = cartesian3Scratch.z;
  803. vertexBuffer[bufferIndex++] = height;
  804. vertexBuffer[bufferIndex++] = u;
  805. vertexBuffer[bufferIndex++] = v;
  806. if (this.hasWebMercatorT) {
  807. vertexBuffer[bufferIndex++] = webMercatorT;
  808. }
  809. }
  810. if (this.hasVertexNormals) {
  811. vertexBuffer[bufferIndex++] = AttributeCompression.AttributeCompression.octPackFloat(
  812. normalToPack
  813. );
  814. }
  815. if (this.hasGeodeticSurfaceNormals) {
  816. vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.x;
  817. vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.y;
  818. vertexBuffer[bufferIndex++] = geodeticSurfaceNormal.z;
  819. }
  820. return bufferIndex;
  821. };
  822. const scratchPosition = new Matrix2.Cartesian3();
  823. const scratchGeodeticSurfaceNormal = new Matrix2.Cartesian3();
  824. TerrainEncoding.prototype.addGeodeticSurfaceNormals = function (
  825. oldBuffer,
  826. newBuffer,
  827. ellipsoid
  828. ) {
  829. if (this.hasGeodeticSurfaceNormals) {
  830. return;
  831. }
  832. const oldStride = this.stride;
  833. const vertexCount = oldBuffer.length / oldStride;
  834. this.hasGeodeticSurfaceNormals = true;
  835. this._calculateStrideAndOffsets();
  836. const newStride = this.stride;
  837. for (let index = 0; index < vertexCount; index++) {
  838. for (let offset = 0; offset < oldStride; offset++) {
  839. const oldIndex = index * oldStride + offset;
  840. const newIndex = index * newStride + offset;
  841. newBuffer[newIndex] = oldBuffer[oldIndex];
  842. }
  843. const position = this.decodePosition(newBuffer, index, scratchPosition);
  844. const geodeticSurfaceNormal = ellipsoid.geodeticSurfaceNormal(
  845. position,
  846. scratchGeodeticSurfaceNormal
  847. );
  848. const bufferIndex = index * newStride + this._offsetGeodeticSurfaceNormal;
  849. newBuffer[bufferIndex] = geodeticSurfaceNormal.x;
  850. newBuffer[bufferIndex + 1] = geodeticSurfaceNormal.y;
  851. newBuffer[bufferIndex + 2] = geodeticSurfaceNormal.z;
  852. }
  853. };
  854. TerrainEncoding.prototype.removeGeodeticSurfaceNormals = function (
  855. oldBuffer,
  856. newBuffer
  857. ) {
  858. if (!this.hasGeodeticSurfaceNormals) {
  859. return;
  860. }
  861. const oldStride = this.stride;
  862. const vertexCount = oldBuffer.length / oldStride;
  863. this.hasGeodeticSurfaceNormals = false;
  864. this._calculateStrideAndOffsets();
  865. const newStride = this.stride;
  866. for (let index = 0; index < vertexCount; index++) {
  867. for (let offset = 0; offset < newStride; offset++) {
  868. const oldIndex = index * oldStride + offset;
  869. const newIndex = index * newStride + offset;
  870. newBuffer[newIndex] = oldBuffer[oldIndex];
  871. }
  872. }
  873. };
  874. TerrainEncoding.prototype.decodePosition = function (buffer, index, result) {
  875. if (!defaultValue.defined(result)) {
  876. result = new Matrix2.Cartesian3();
  877. }
  878. index *= this.stride;
  879. if (this.quantization === TerrainQuantization$1.BITS12) {
  880. const xy = AttributeCompression.AttributeCompression.decompressTextureCoordinates(
  881. buffer[index],
  882. cartesian2Scratch
  883. );
  884. result.x = xy.x;
  885. result.y = xy.y;
  886. const zh = AttributeCompression.AttributeCompression.decompressTextureCoordinates(
  887. buffer[index + 1],
  888. cartesian2Scratch
  889. );
  890. result.z = zh.x;
  891. return Matrix2.Matrix4.multiplyByPoint(this.fromScaledENU, result, result);
  892. }
  893. result.x = buffer[index];
  894. result.y = buffer[index + 1];
  895. result.z = buffer[index + 2];
  896. return Matrix2.Cartesian3.add(result, this.center, result);
  897. };
  898. TerrainEncoding.prototype.getExaggeratedPosition = function (
  899. buffer,
  900. index,
  901. result
  902. ) {
  903. result = this.decodePosition(buffer, index, result);
  904. const exaggeration = this.exaggeration;
  905. const exaggerationRelativeHeight = this.exaggerationRelativeHeight;
  906. const hasExaggeration = exaggeration !== 1.0;
  907. if (hasExaggeration && this.hasGeodeticSurfaceNormals) {
  908. const geodeticSurfaceNormal = this.decodeGeodeticSurfaceNormal(
  909. buffer,
  910. index,
  911. scratchGeodeticSurfaceNormal
  912. );
  913. const rawHeight = this.decodeHeight(buffer, index);
  914. const heightDifference =
  915. TerrainExaggeration.getHeight(
  916. rawHeight,
  917. exaggeration,
  918. exaggerationRelativeHeight
  919. ) - rawHeight;
  920. // some math is unrolled for better performance
  921. result.x += geodeticSurfaceNormal.x * heightDifference;
  922. result.y += geodeticSurfaceNormal.y * heightDifference;
  923. result.z += geodeticSurfaceNormal.z * heightDifference;
  924. }
  925. return result;
  926. };
  927. TerrainEncoding.prototype.decodeTextureCoordinates = function (
  928. buffer,
  929. index,
  930. result
  931. ) {
  932. if (!defaultValue.defined(result)) {
  933. result = new Matrix2.Cartesian2();
  934. }
  935. index *= this.stride;
  936. if (this.quantization === TerrainQuantization$1.BITS12) {
  937. return AttributeCompression.AttributeCompression.decompressTextureCoordinates(
  938. buffer[index + 2],
  939. result
  940. );
  941. }
  942. return Matrix2.Cartesian2.fromElements(buffer[index + 4], buffer[index + 5], result);
  943. };
  944. TerrainEncoding.prototype.decodeHeight = function (buffer, index) {
  945. index *= this.stride;
  946. if (this.quantization === TerrainQuantization$1.BITS12) {
  947. const zh = AttributeCompression.AttributeCompression.decompressTextureCoordinates(
  948. buffer[index + 1],
  949. cartesian2Scratch
  950. );
  951. return (
  952. zh.y * (this.maximumHeight - this.minimumHeight) + this.minimumHeight
  953. );
  954. }
  955. return buffer[index + 3];
  956. };
  957. TerrainEncoding.prototype.decodeWebMercatorT = function (buffer, index) {
  958. index *= this.stride;
  959. if (this.quantization === TerrainQuantization$1.BITS12) {
  960. return AttributeCompression.AttributeCompression.decompressTextureCoordinates(
  961. buffer[index + 3],
  962. cartesian2Scratch
  963. ).x;
  964. }
  965. return buffer[index + 6];
  966. };
  967. TerrainEncoding.prototype.getOctEncodedNormal = function (
  968. buffer,
  969. index,
  970. result
  971. ) {
  972. index = index * this.stride + this._offsetVertexNormal;
  973. const temp = buffer[index] / 256.0;
  974. const x = Math.floor(temp);
  975. const y = (temp - x) * 256.0;
  976. return Matrix2.Cartesian2.fromElements(x, y, result);
  977. };
  978. TerrainEncoding.prototype.decodeGeodeticSurfaceNormal = function (
  979. buffer,
  980. index,
  981. result
  982. ) {
  983. index = index * this.stride + this._offsetGeodeticSurfaceNormal;
  984. result.x = buffer[index];
  985. result.y = buffer[index + 1];
  986. result.z = buffer[index + 2];
  987. return result;
  988. };
  989. TerrainEncoding.prototype._calculateStrideAndOffsets = function () {
  990. let vertexStride = 0;
  991. switch (this.quantization) {
  992. case TerrainQuantization$1.BITS12:
  993. vertexStride += 3;
  994. break;
  995. default:
  996. vertexStride += 6;
  997. }
  998. if (this.hasWebMercatorT) {
  999. vertexStride += 1;
  1000. }
  1001. if (this.hasVertexNormals) {
  1002. this._offsetVertexNormal = vertexStride;
  1003. vertexStride += 1;
  1004. }
  1005. if (this.hasGeodeticSurfaceNormals) {
  1006. this._offsetGeodeticSurfaceNormal = vertexStride;
  1007. vertexStride += 3;
  1008. }
  1009. this.stride = vertexStride;
  1010. };
  1011. const attributesIndicesNone = {
  1012. position3DAndHeight: 0,
  1013. textureCoordAndEncodedNormals: 1,
  1014. geodeticSurfaceNormal: 2,
  1015. };
  1016. const attributesIndicesBits12 = {
  1017. compressed0: 0,
  1018. compressed1: 1,
  1019. geodeticSurfaceNormal: 2,
  1020. };
  1021. TerrainEncoding.prototype.getAttributes = function (buffer) {
  1022. const datatype = ComponentDatatype.ComponentDatatype.FLOAT;
  1023. const sizeInBytes = ComponentDatatype.ComponentDatatype.getSizeInBytes(datatype);
  1024. const strideInBytes = this.stride * sizeInBytes;
  1025. let offsetInBytes = 0;
  1026. const attributes = [];
  1027. function addAttribute(index, componentsPerAttribute) {
  1028. attributes.push({
  1029. index: index,
  1030. vertexBuffer: buffer,
  1031. componentDatatype: datatype,
  1032. componentsPerAttribute: componentsPerAttribute,
  1033. offsetInBytes: offsetInBytes,
  1034. strideInBytes: strideInBytes,
  1035. });
  1036. offsetInBytes += componentsPerAttribute * sizeInBytes;
  1037. }
  1038. if (this.quantization === TerrainQuantization$1.NONE) {
  1039. addAttribute(attributesIndicesNone.position3DAndHeight, 4);
  1040. let componentsTexCoordAndNormals = 2;
  1041. componentsTexCoordAndNormals += this.hasWebMercatorT ? 1 : 0;
  1042. componentsTexCoordAndNormals += this.hasVertexNormals ? 1 : 0;
  1043. addAttribute(
  1044. attributesIndicesNone.textureCoordAndEncodedNormals,
  1045. componentsTexCoordAndNormals
  1046. );
  1047. if (this.hasGeodeticSurfaceNormals) {
  1048. addAttribute(attributesIndicesNone.geodeticSurfaceNormal, 3);
  1049. }
  1050. } else {
  1051. // When there is no webMercatorT or vertex normals, the attribute only needs 3 components: x/y, z/h, u/v.
  1052. // WebMercatorT and vertex normals each take up one component, so if only one of them is present the first
  1053. // attribute gets a 4th component. If both are present, we need an additional attribute that has 1 component.
  1054. const usingAttribute0Component4 =
  1055. this.hasWebMercatorT || this.hasVertexNormals;
  1056. const usingAttribute1Component1 =
  1057. this.hasWebMercatorT && this.hasVertexNormals;
  1058. addAttribute(
  1059. attributesIndicesBits12.compressed0,
  1060. usingAttribute0Component4 ? 4 : 3
  1061. );
  1062. if (usingAttribute1Component1) {
  1063. addAttribute(attributesIndicesBits12.compressed1, 1);
  1064. }
  1065. if (this.hasGeodeticSurfaceNormals) {
  1066. addAttribute(attributesIndicesBits12.geodeticSurfaceNormal, 3);
  1067. }
  1068. }
  1069. return attributes;
  1070. };
  1071. TerrainEncoding.prototype.getAttributeLocations = function () {
  1072. if (this.quantization === TerrainQuantization$1.NONE) {
  1073. return attributesIndicesNone;
  1074. }
  1075. return attributesIndicesBits12;
  1076. };
  1077. TerrainEncoding.clone = function (encoding, result) {
  1078. if (!defaultValue.defined(encoding)) {
  1079. return undefined;
  1080. }
  1081. if (!defaultValue.defined(result)) {
  1082. result = new TerrainEncoding();
  1083. }
  1084. result.quantization = encoding.quantization;
  1085. result.minimumHeight = encoding.minimumHeight;
  1086. result.maximumHeight = encoding.maximumHeight;
  1087. result.center = Matrix2.Cartesian3.clone(encoding.center);
  1088. result.toScaledENU = Matrix2.Matrix4.clone(encoding.toScaledENU);
  1089. result.fromScaledENU = Matrix2.Matrix4.clone(encoding.fromScaledENU);
  1090. result.matrix = Matrix2.Matrix4.clone(encoding.matrix);
  1091. result.hasVertexNormals = encoding.hasVertexNormals;
  1092. result.hasWebMercatorT = encoding.hasWebMercatorT;
  1093. result.hasGeodeticSurfaceNormals = encoding.hasGeodeticSurfaceNormals;
  1094. result.exaggeration = encoding.exaggeration;
  1095. result.exaggerationRelativeHeight = encoding.exaggerationRelativeHeight;
  1096. result._calculateStrideAndOffsets();
  1097. return result;
  1098. };
  1099. exports.EllipsoidalOccluder = EllipsoidalOccluder;
  1100. exports.TerrainEncoding = TerrainEncoding;
  1101. }));
  1102. //# sourceMappingURL=TerrainEncoding-a8a1f120.js.map