TerrainEncoding-f29761bd.js 42 KB

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