EllipsoidGeometry.js 21 KB

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