EllipsoidGeometry.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. import arrayFill from "./arrayFill.js";
  2. import BoundingSphere from "./BoundingSphere.js";
  3. import Cartesian2 from "./Cartesian2.js";
  4. import Cartesian3 from "./Cartesian3.js";
  5. import ComponentDatatype from "./ComponentDatatype.js";
  6. import defaultValue from "./defaultValue.js";
  7. import defined from "./defined.js";
  8. import DeveloperError from "./DeveloperError.js";
  9. import Ellipsoid from "./Ellipsoid.js";
  10. import Geometry from "./Geometry.js";
  11. import GeometryAttribute from "./GeometryAttribute.js";
  12. import GeometryAttributes from "./GeometryAttributes.js";
  13. import GeometryOffsetAttribute from "./GeometryOffsetAttribute.js";
  14. import IndexDatatype from "./IndexDatatype.js";
  15. import CesiumMath from "./Math.js";
  16. import PrimitiveType from "./PrimitiveType.js";
  17. import VertexFormat from "./VertexFormat.js";
  18. const scratchPosition = new Cartesian3();
  19. const scratchNormal = new Cartesian3();
  20. const scratchTangent = new Cartesian3();
  21. const scratchBitangent = new Cartesian3();
  22. const scratchNormalST = new Cartesian3();
  23. const defaultRadii = new Cartesian3(1.0, 1.0, 1.0);
  24. const cos = Math.cos;
  25. const sin = Math.sin;
  26. /**
  27. * A description of an ellipsoid centered at the origin.
  28. *
  29. * @alias EllipsoidGeometry
  30. * @constructor
  31. *
  32. * @param {Object} [options] Object with the following properties:
  33. * @param {Cartesian3} [options.radii=Cartesian3(1.0, 1.0, 1.0)] The radii of the ellipsoid in the x, y, and z directions.
  34. * @param {Cartesian3} [options.innerRadii=options.radii] The inner radii of the ellipsoid in the x, y, and z directions.
  35. * @param {Number} [options.minimumClock=0.0] The minimum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis.
  36. * @param {Number} [options.maximumClock=2*PI] The maximum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis.
  37. * @param {Number} [options.minimumCone=0.0] The minimum angle measured from the positive z-axis and toward the negative z-axis.
  38. * @param {Number} [options.maximumCone=PI] The maximum angle measured from the positive z-axis and toward the negative z-axis.
  39. * @param {Number} [options.stackPartitions=64] The number of times to partition the ellipsoid into stacks.
  40. * @param {Number} [options.slicePartitions=64] The number of times to partition the ellipsoid into radial slices.
  41. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed.
  42. *
  43. * @exception {DeveloperError} options.slicePartitions cannot be less than three.
  44. * @exception {DeveloperError} options.stackPartitions cannot be less than three.
  45. *
  46. * @see EllipsoidGeometry#createGeometry
  47. *
  48. * @example
  49. * const ellipsoid = new Cesium.EllipsoidGeometry({
  50. * vertexFormat : Cesium.VertexFormat.POSITION_ONLY,
  51. * radii : new Cesium.Cartesian3(1000000.0, 500000.0, 500000.0)
  52. * });
  53. * const geometry = Cesium.EllipsoidGeometry.createGeometry(ellipsoid);
  54. */
  55. function EllipsoidGeometry(options) {
  56. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  57. const radii = defaultValue(options.radii, defaultRadii);
  58. const innerRadii = defaultValue(options.innerRadii, radii);
  59. const minimumClock = defaultValue(options.minimumClock, 0.0);
  60. const maximumClock = defaultValue(options.maximumClock, CesiumMath.TWO_PI);
  61. const minimumCone = defaultValue(options.minimumCone, 0.0);
  62. const maximumCone = defaultValue(options.maximumCone, CesiumMath.PI);
  63. const stackPartitions = Math.round(defaultValue(options.stackPartitions, 64));
  64. const slicePartitions = Math.round(defaultValue(options.slicePartitions, 64));
  65. const vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
  66. //>>includeStart('debug', pragmas.debug);
  67. if (slicePartitions < 3) {
  68. throw new DeveloperError(
  69. "options.slicePartitions cannot be less than three."
  70. );
  71. }
  72. if (stackPartitions < 3) {
  73. throw new DeveloperError(
  74. "options.stackPartitions cannot be less than three."
  75. );
  76. }
  77. //>>includeEnd('debug');
  78. this._radii = Cartesian3.clone(radii);
  79. this._innerRadii = Cartesian3.clone(innerRadii);
  80. this._minimumClock = minimumClock;
  81. this._maximumClock = maximumClock;
  82. this._minimumCone = minimumCone;
  83. this._maximumCone = maximumCone;
  84. this._stackPartitions = stackPartitions;
  85. this._slicePartitions = slicePartitions;
  86. this._vertexFormat = VertexFormat.clone(vertexFormat);
  87. this._offsetAttribute = options.offsetAttribute;
  88. this._workerName = "createEllipsoidGeometry";
  89. }
  90. /**
  91. * The number of elements used to pack the object into an array.
  92. * @type {Number}
  93. */
  94. EllipsoidGeometry.packedLength =
  95. 2 * Cartesian3.packedLength + VertexFormat.packedLength + 7;
  96. /**
  97. * Stores the provided instance into the provided array.
  98. *
  99. * @param {EllipsoidGeometry} value The value to pack.
  100. * @param {Number[]} array The array to pack into.
  101. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  102. *
  103. * @returns {Number[]} The array that was packed into
  104. */
  105. EllipsoidGeometry.pack = function (value, array, startingIndex) {
  106. //>>includeStart('debug', pragmas.debug);
  107. if (!defined(value)) {
  108. throw new DeveloperError("value is required");
  109. }
  110. if (!defined(array)) {
  111. throw new DeveloperError("array is required");
  112. }
  113. //>>includeEnd('debug');
  114. startingIndex = defaultValue(startingIndex, 0);
  115. Cartesian3.pack(value._radii, array, startingIndex);
  116. startingIndex += Cartesian3.packedLength;
  117. Cartesian3.pack(value._innerRadii, array, startingIndex);
  118. startingIndex += Cartesian3.packedLength;
  119. VertexFormat.pack(value._vertexFormat, array, startingIndex);
  120. startingIndex += VertexFormat.packedLength;
  121. array[startingIndex++] = value._minimumClock;
  122. array[startingIndex++] = value._maximumClock;
  123. array[startingIndex++] = value._minimumCone;
  124. array[startingIndex++] = value._maximumCone;
  125. array[startingIndex++] = value._stackPartitions;
  126. array[startingIndex++] = value._slicePartitions;
  127. array[startingIndex] = defaultValue(value._offsetAttribute, -1);
  128. return array;
  129. };
  130. const scratchRadii = new Cartesian3();
  131. const scratchInnerRadii = new Cartesian3();
  132. const scratchVertexFormat = new VertexFormat();
  133. const scratchOptions = {
  134. radii: scratchRadii,
  135. innerRadii: scratchInnerRadii,
  136. vertexFormat: scratchVertexFormat,
  137. minimumClock: undefined,
  138. maximumClock: undefined,
  139. minimumCone: undefined,
  140. maximumCone: undefined,
  141. stackPartitions: undefined,
  142. slicePartitions: undefined,
  143. offsetAttribute: undefined,
  144. };
  145. /**
  146. * Retrieves an instance from a packed array.
  147. *
  148. * @param {Number[]} array The packed array.
  149. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  150. * @param {EllipsoidGeometry} [result] The object into which to store the result.
  151. * @returns {EllipsoidGeometry} The modified result parameter or a new EllipsoidGeometry instance if one was not provided.
  152. */
  153. EllipsoidGeometry.unpack = function (array, startingIndex, result) {
  154. //>>includeStart('debug', pragmas.debug);
  155. if (!defined(array)) {
  156. throw new DeveloperError("array is required");
  157. }
  158. //>>includeEnd('debug');
  159. startingIndex = defaultValue(startingIndex, 0);
  160. const radii = Cartesian3.unpack(array, startingIndex, scratchRadii);
  161. startingIndex += Cartesian3.packedLength;
  162. const innerRadii = Cartesian3.unpack(array, startingIndex, scratchInnerRadii);
  163. startingIndex += Cartesian3.packedLength;
  164. const vertexFormat = VertexFormat.unpack(
  165. array,
  166. startingIndex,
  167. scratchVertexFormat
  168. );
  169. startingIndex += VertexFormat.packedLength;
  170. const minimumClock = array[startingIndex++];
  171. const maximumClock = array[startingIndex++];
  172. const minimumCone = array[startingIndex++];
  173. const maximumCone = array[startingIndex++];
  174. const stackPartitions = array[startingIndex++];
  175. const slicePartitions = array[startingIndex++];
  176. const offsetAttribute = array[startingIndex];
  177. if (!defined(result)) {
  178. scratchOptions.minimumClock = minimumClock;
  179. scratchOptions.maximumClock = maximumClock;
  180. scratchOptions.minimumCone = minimumCone;
  181. scratchOptions.maximumCone = maximumCone;
  182. scratchOptions.stackPartitions = stackPartitions;
  183. scratchOptions.slicePartitions = slicePartitions;
  184. scratchOptions.offsetAttribute =
  185. offsetAttribute === -1 ? undefined : offsetAttribute;
  186. return new EllipsoidGeometry(scratchOptions);
  187. }
  188. result._radii = Cartesian3.clone(radii, result._radii);
  189. result._innerRadii = Cartesian3.clone(innerRadii, result._innerRadii);
  190. result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat);
  191. result._minimumClock = minimumClock;
  192. result._maximumClock = maximumClock;
  193. result._minimumCone = minimumCone;
  194. result._maximumCone = maximumCone;
  195. result._stackPartitions = stackPartitions;
  196. result._slicePartitions = slicePartitions;
  197. result._offsetAttribute =
  198. offsetAttribute === -1 ? undefined : offsetAttribute;
  199. return result;
  200. };
  201. /**
  202. * Computes the geometric representation of an ellipsoid, including its vertices, indices, and a bounding sphere.
  203. *
  204. * @param {EllipsoidGeometry} ellipsoidGeometry A description of the ellipsoid.
  205. * @returns {Geometry|undefined} The computed vertices and indices.
  206. */
  207. EllipsoidGeometry.createGeometry = function (ellipsoidGeometry) {
  208. const radii = ellipsoidGeometry._radii;
  209. if (radii.x <= 0 || radii.y <= 0 || radii.z <= 0) {
  210. return;
  211. }
  212. const innerRadii = ellipsoidGeometry._innerRadii;
  213. if (innerRadii.x <= 0 || innerRadii.y <= 0 || innerRadii.z <= 0) {
  214. return;
  215. }
  216. const minimumClock = ellipsoidGeometry._minimumClock;
  217. const maximumClock = ellipsoidGeometry._maximumClock;
  218. const minimumCone = ellipsoidGeometry._minimumCone;
  219. const maximumCone = ellipsoidGeometry._maximumCone;
  220. const vertexFormat = ellipsoidGeometry._vertexFormat;
  221. // Add an extra slice and stack so that the number of partitions is the
  222. // number of surfaces rather than the number of joints
  223. let slicePartitions = ellipsoidGeometry._slicePartitions + 1;
  224. let stackPartitions = ellipsoidGeometry._stackPartitions + 1;
  225. slicePartitions = Math.round(
  226. (slicePartitions * Math.abs(maximumClock - minimumClock)) /
  227. CesiumMath.TWO_PI
  228. );
  229. stackPartitions = Math.round(
  230. (stackPartitions * Math.abs(maximumCone - minimumCone)) / CesiumMath.PI
  231. );
  232. if (slicePartitions < 2) {
  233. slicePartitions = 2;
  234. }
  235. if (stackPartitions < 2) {
  236. stackPartitions = 2;
  237. }
  238. let i;
  239. let j;
  240. let index = 0;
  241. // Create arrays for theta and phi. Duplicate first and last angle to
  242. // allow different normals at the intersections.
  243. const phis = [minimumCone];
  244. const thetas = [minimumClock];
  245. for (i = 0; i < stackPartitions; i++) {
  246. phis.push(
  247. minimumCone + (i * (maximumCone - minimumCone)) / (stackPartitions - 1)
  248. );
  249. }
  250. phis.push(maximumCone);
  251. for (j = 0; j < slicePartitions; j++) {
  252. thetas.push(
  253. minimumClock + (j * (maximumClock - minimumClock)) / (slicePartitions - 1)
  254. );
  255. }
  256. thetas.push(maximumClock);
  257. const numPhis = phis.length;
  258. const numThetas = thetas.length;
  259. // Allow for extra indices if there is an inner surface and if we need
  260. // to close the sides if the clock range is not a full circle
  261. let extraIndices = 0;
  262. let vertexMultiplier = 1.0;
  263. const hasInnerSurface =
  264. innerRadii.x !== radii.x ||
  265. innerRadii.y !== radii.y ||
  266. innerRadii.z !== radii.z;
  267. let isTopOpen = false;
  268. let isBotOpen = false;
  269. let isClockOpen = false;
  270. if (hasInnerSurface) {
  271. vertexMultiplier = 2.0;
  272. if (minimumCone > 0.0) {
  273. isTopOpen = true;
  274. extraIndices += slicePartitions - 1;
  275. }
  276. if (maximumCone < Math.PI) {
  277. isBotOpen = true;
  278. extraIndices += slicePartitions - 1;
  279. }
  280. if ((maximumClock - minimumClock) % CesiumMath.TWO_PI) {
  281. isClockOpen = true;
  282. extraIndices += (stackPartitions - 1) * 2 + 1;
  283. } else {
  284. extraIndices += 1;
  285. }
  286. }
  287. const vertexCount = numThetas * numPhis * vertexMultiplier;
  288. const positions = new Float64Array(vertexCount * 3);
  289. const isInner = arrayFill(new Array(vertexCount), false);
  290. const negateNormal = arrayFill(new Array(vertexCount), false);
  291. // Multiply by 6 because there are two triangles per sector
  292. const indexCount = slicePartitions * stackPartitions * vertexMultiplier;
  293. const numIndices =
  294. 6 *
  295. (indexCount +
  296. extraIndices +
  297. 1 -
  298. (slicePartitions + stackPartitions) * vertexMultiplier);
  299. const indices = IndexDatatype.createTypedArray(indexCount, numIndices);
  300. const normals = vertexFormat.normal
  301. ? new Float32Array(vertexCount * 3)
  302. : undefined;
  303. const tangents = vertexFormat.tangent
  304. ? new Float32Array(vertexCount * 3)
  305. : undefined;
  306. const bitangents = vertexFormat.bitangent
  307. ? new Float32Array(vertexCount * 3)
  308. : undefined;
  309. const st = vertexFormat.st ? new Float32Array(vertexCount * 2) : undefined;
  310. // Calculate sin/cos phi
  311. const sinPhi = new Array(numPhis);
  312. const cosPhi = new Array(numPhis);
  313. for (i = 0; i < numPhis; i++) {
  314. sinPhi[i] = sin(phis[i]);
  315. cosPhi[i] = cos(phis[i]);
  316. }
  317. // Calculate sin/cos theta
  318. const sinTheta = new Array(numThetas);
  319. const cosTheta = new Array(numThetas);
  320. for (j = 0; j < numThetas; j++) {
  321. cosTheta[j] = cos(thetas[j]);
  322. sinTheta[j] = sin(thetas[j]);
  323. }
  324. // Create outer surface
  325. for (i = 0; i < numPhis; i++) {
  326. for (j = 0; j < numThetas; j++) {
  327. positions[index++] = radii.x * sinPhi[i] * cosTheta[j];
  328. positions[index++] = radii.y * sinPhi[i] * sinTheta[j];
  329. positions[index++] = radii.z * cosPhi[i];
  330. }
  331. }
  332. // Create inner surface
  333. let vertexIndex = vertexCount / 2.0;
  334. if (hasInnerSurface) {
  335. for (i = 0; i < numPhis; i++) {
  336. for (j = 0; j < numThetas; j++) {
  337. positions[index++] = innerRadii.x * sinPhi[i] * cosTheta[j];
  338. positions[index++] = innerRadii.y * sinPhi[i] * sinTheta[j];
  339. positions[index++] = innerRadii.z * cosPhi[i];
  340. // Keep track of which vertices are the inner and which ones
  341. // need the normal to be negated
  342. isInner[vertexIndex] = true;
  343. if (i > 0 && i !== numPhis - 1 && j !== 0 && j !== numThetas - 1) {
  344. negateNormal[vertexIndex] = true;
  345. }
  346. vertexIndex++;
  347. }
  348. }
  349. }
  350. // Create indices for outer surface
  351. index = 0;
  352. let topOffset;
  353. let bottomOffset;
  354. for (i = 1; i < numPhis - 2; i++) {
  355. topOffset = i * numThetas;
  356. bottomOffset = (i + 1) * numThetas;
  357. for (j = 1; j < numThetas - 2; j++) {
  358. indices[index++] = bottomOffset + j;
  359. indices[index++] = bottomOffset + j + 1;
  360. indices[index++] = topOffset + j + 1;
  361. indices[index++] = bottomOffset + j;
  362. indices[index++] = topOffset + j + 1;
  363. indices[index++] = topOffset + j;
  364. }
  365. }
  366. // Create indices for inner surface
  367. if (hasInnerSurface) {
  368. const offset = numPhis * numThetas;
  369. for (i = 1; i < numPhis - 2; i++) {
  370. topOffset = offset + i * numThetas;
  371. bottomOffset = offset + (i + 1) * numThetas;
  372. for (j = 1; j < numThetas - 2; j++) {
  373. indices[index++] = bottomOffset + j;
  374. indices[index++] = topOffset + j;
  375. indices[index++] = topOffset + j + 1;
  376. indices[index++] = bottomOffset + j;
  377. indices[index++] = topOffset + j + 1;
  378. indices[index++] = bottomOffset + j + 1;
  379. }
  380. }
  381. }
  382. let outerOffset;
  383. let innerOffset;
  384. if (hasInnerSurface) {
  385. if (isTopOpen) {
  386. // Connect the top of the inner surface to the top of the outer surface
  387. innerOffset = numPhis * numThetas;
  388. for (i = 1; i < numThetas - 2; i++) {
  389. indices[index++] = i;
  390. indices[index++] = i + 1;
  391. indices[index++] = innerOffset + i + 1;
  392. indices[index++] = i;
  393. indices[index++] = innerOffset + i + 1;
  394. indices[index++] = innerOffset + i;
  395. }
  396. }
  397. if (isBotOpen) {
  398. // Connect the bottom of the inner surface to the bottom of the outer surface
  399. outerOffset = numPhis * numThetas - numThetas;
  400. innerOffset = numPhis * numThetas * vertexMultiplier - numThetas;
  401. for (i = 1; i < numThetas - 2; i++) {
  402. indices[index++] = outerOffset + i + 1;
  403. indices[index++] = outerOffset + i;
  404. indices[index++] = innerOffset + i;
  405. indices[index++] = outerOffset + i + 1;
  406. indices[index++] = innerOffset + i;
  407. indices[index++] = innerOffset + i + 1;
  408. }
  409. }
  410. }
  411. // Connect the edges if clock is not closed
  412. if (isClockOpen) {
  413. for (i = 1; i < numPhis - 2; i++) {
  414. innerOffset = numThetas * numPhis + numThetas * i;
  415. outerOffset = numThetas * i;
  416. indices[index++] = innerOffset;
  417. indices[index++] = outerOffset + numThetas;
  418. indices[index++] = outerOffset;
  419. indices[index++] = innerOffset;
  420. indices[index++] = innerOffset + numThetas;
  421. indices[index++] = outerOffset + numThetas;
  422. }
  423. for (i = 1; i < numPhis - 2; i++) {
  424. innerOffset = numThetas * numPhis + numThetas * (i + 1) - 1;
  425. outerOffset = numThetas * (i + 1) - 1;
  426. indices[index++] = outerOffset + numThetas;
  427. indices[index++] = innerOffset;
  428. indices[index++] = outerOffset;
  429. indices[index++] = outerOffset + numThetas;
  430. indices[index++] = innerOffset + numThetas;
  431. indices[index++] = innerOffset;
  432. }
  433. }
  434. const attributes = new GeometryAttributes();
  435. if (vertexFormat.position) {
  436. attributes.position = new GeometryAttribute({
  437. componentDatatype: ComponentDatatype.DOUBLE,
  438. componentsPerAttribute: 3,
  439. values: positions,
  440. });
  441. }
  442. let stIndex = 0;
  443. let normalIndex = 0;
  444. let tangentIndex = 0;
  445. let bitangentIndex = 0;
  446. const vertexCountHalf = vertexCount / 2.0;
  447. let ellipsoid;
  448. const ellipsoidOuter = Ellipsoid.fromCartesian3(radii);
  449. const ellipsoidInner = Ellipsoid.fromCartesian3(innerRadii);
  450. if (
  451. vertexFormat.st ||
  452. vertexFormat.normal ||
  453. vertexFormat.tangent ||
  454. vertexFormat.bitangent
  455. ) {
  456. for (i = 0; i < vertexCount; i++) {
  457. ellipsoid = isInner[i] ? ellipsoidInner : ellipsoidOuter;
  458. const position = Cartesian3.fromArray(positions, i * 3, scratchPosition);
  459. const normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal);
  460. if (negateNormal[i]) {
  461. Cartesian3.negate(normal, normal);
  462. }
  463. if (vertexFormat.st) {
  464. const normalST = Cartesian2.negate(normal, scratchNormalST);
  465. st[stIndex++] =
  466. Math.atan2(normalST.y, normalST.x) / CesiumMath.TWO_PI + 0.5;
  467. st[stIndex++] = Math.asin(normal.z) / Math.PI + 0.5;
  468. }
  469. if (vertexFormat.normal) {
  470. normals[normalIndex++] = normal.x;
  471. normals[normalIndex++] = normal.y;
  472. normals[normalIndex++] = normal.z;
  473. }
  474. if (vertexFormat.tangent || vertexFormat.bitangent) {
  475. const tangent = scratchTangent;
  476. // Use UNIT_X for the poles
  477. let tangetOffset = 0;
  478. let unit;
  479. if (isInner[i]) {
  480. tangetOffset = vertexCountHalf;
  481. }
  482. if (
  483. !isTopOpen &&
  484. i >= tangetOffset &&
  485. i < tangetOffset + numThetas * 2
  486. ) {
  487. unit = Cartesian3.UNIT_X;
  488. } else {
  489. unit = Cartesian3.UNIT_Z;
  490. }
  491. Cartesian3.cross(unit, normal, tangent);
  492. Cartesian3.normalize(tangent, tangent);
  493. if (vertexFormat.tangent) {
  494. tangents[tangentIndex++] = tangent.x;
  495. tangents[tangentIndex++] = tangent.y;
  496. tangents[tangentIndex++] = tangent.z;
  497. }
  498. if (vertexFormat.bitangent) {
  499. const bitangent = Cartesian3.cross(normal, tangent, scratchBitangent);
  500. Cartesian3.normalize(bitangent, bitangent);
  501. bitangents[bitangentIndex++] = bitangent.x;
  502. bitangents[bitangentIndex++] = bitangent.y;
  503. bitangents[bitangentIndex++] = bitangent.z;
  504. }
  505. }
  506. }
  507. if (vertexFormat.st) {
  508. attributes.st = new GeometryAttribute({
  509. componentDatatype: ComponentDatatype.FLOAT,
  510. componentsPerAttribute: 2,
  511. values: st,
  512. });
  513. }
  514. if (vertexFormat.normal) {
  515. attributes.normal = new GeometryAttribute({
  516. componentDatatype: ComponentDatatype.FLOAT,
  517. componentsPerAttribute: 3,
  518. values: normals,
  519. });
  520. }
  521. if (vertexFormat.tangent) {
  522. attributes.tangent = new GeometryAttribute({
  523. componentDatatype: ComponentDatatype.FLOAT,
  524. componentsPerAttribute: 3,
  525. values: tangents,
  526. });
  527. }
  528. if (vertexFormat.bitangent) {
  529. attributes.bitangent = new GeometryAttribute({
  530. componentDatatype: ComponentDatatype.FLOAT,
  531. componentsPerAttribute: 3,
  532. values: bitangents,
  533. });
  534. }
  535. }
  536. if (defined(ellipsoidGeometry._offsetAttribute)) {
  537. const length = positions.length;
  538. const applyOffset = new Uint8Array(length / 3);
  539. const offsetValue =
  540. ellipsoidGeometry._offsetAttribute === GeometryOffsetAttribute.NONE
  541. ? 0
  542. : 1;
  543. arrayFill(applyOffset, offsetValue);
  544. attributes.applyOffset = new GeometryAttribute({
  545. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  546. componentsPerAttribute: 1,
  547. values: applyOffset,
  548. });
  549. }
  550. return new Geometry({
  551. attributes: attributes,
  552. indices: indices,
  553. primitiveType: PrimitiveType.TRIANGLES,
  554. boundingSphere: BoundingSphere.fromEllipsoid(ellipsoidOuter),
  555. offsetAttribute: ellipsoidGeometry._offsetAttribute,
  556. });
  557. };
  558. let unitEllipsoidGeometry;
  559. /**
  560. * Returns the geometric representation of a unit ellipsoid, including its vertices, indices, and a bounding sphere.
  561. * @returns {Geometry} The computed vertices and indices.
  562. *
  563. * @private
  564. */
  565. EllipsoidGeometry.getUnitEllipsoid = function () {
  566. if (!defined(unitEllipsoidGeometry)) {
  567. unitEllipsoidGeometry = EllipsoidGeometry.createGeometry(
  568. new EllipsoidGeometry({
  569. radii: new Cartesian3(1.0, 1.0, 1.0),
  570. vertexFormat: VertexFormat.POSITION_ONLY,
  571. })
  572. );
  573. }
  574. return unitEllipsoidGeometry;
  575. };
  576. export default EllipsoidGeometry;