智慧水务管理系统 - 精河县供水工程综合管理平台

BufferPrimitiveCollection.js 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. // @ts-check
  2. import BoundingSphere from "../Core/BoundingSphere.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import DeveloperError from "../Core/DeveloperError.js";
  5. import Frozen from "../Core/Frozen.js";
  6. import Matrix4 from "../Core/Matrix4.js";
  7. import assert from "../Core/assert.js";
  8. import ComponentDatatype from "../Core/ComponentDatatype.js";
  9. import defined from "../Core/defined.js";
  10. import Check from "../Core/Check.js";
  11. import AttributeCompression from "../Core/AttributeCompression.js";
  12. import SceneMode from "./SceneMode.js";
  13. import AttributeType from "./AttributeType.js";
  14. import oneTimeWarning from "../Core/oneTimeWarning.js";
  15. import BlendOption from "../Scene/BlendOption.js";
  16. /** @import { Destroyable, TypedArray, TypedArrayConstructor } from "../Core/globalTypes.js"; */
  17. /** @import Context from "../Renderer/Context.js"; */
  18. /** @import FrameState from "./FrameState.js"; */
  19. /** @import BufferPrimitive from "./BufferPrimitive.js"; */
  20. /** @import BufferPrimitiveMaterial from "./BufferPrimitiveMaterial.js"; */
  21. /** @import PickId from "../Renderer/PickId.js"; */
  22. /**
  23. * @typedef {object} BufferPrimitiveOptions
  24. * @property {Matrix4} [modelMatrix=Matrix4.IDENTITY] Transforms geometry from model to world coordinates.
  25. * @property {boolean} [show=true]
  26. * @property {BufferPrimitiveMaterial} [material]
  27. * @property {number} [featureId]
  28. * @property {object} [pickObject]
  29. * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
  30. */
  31. /**
  32. * Collection of primitives held in ArrayBuffer storage for performance and memory optimization.
  33. *
  34. * <p>To get the full performance benefit of using a BufferPrimitiveCollection containing "N" primitives,
  35. * be careful to avoid allocating "N" instances of any related JavaScript object. {@link BufferPrimitive},
  36. * {@link Color}, {@link Cartesian3}, and other objects can all be reused when working with large collections,
  37. * using the {@link https://en.wikipedia.org/wiki/Flyweight_pattern|flyweight pattern}.</p>
  38. *
  39. * @abstract
  40. * @template T extends BufferPrimitive
  41. * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
  42. *
  43. * @see BufferPrimitive
  44. * @see BufferPrimitiveMaterial
  45. * @see BufferPointCollection
  46. * @see BufferPolylineCollection
  47. * @see BufferPolygonCollection
  48. */
  49. class BufferPrimitiveCollection {
  50. /** @ignore */
  51. static Error = {
  52. ERR_RESIZE: "BufferPrimitive range cannot be resized after initialization.",
  53. ERR_CAPACITY: "BufferPrimitiveCollection capacity exceeded.",
  54. ERR_MULTIPLE_OF_FOUR:
  55. "BufferPrimitive byte length must be a multiple of 4.",
  56. ERR_OUT_OF_RANGE: "BufferPrimitive buffer access out of range.",
  57. };
  58. /**
  59. * Resources managed by the collection's renderer. Collections may have multiple renderer
  60. * implementations, so the collection should be ignorant of the renderer's implementation
  61. * and context data. A collection only has one renderer active at a time.
  62. *
  63. * @type {Destroyable|null}
  64. * @ignore
  65. */
  66. _renderContext = null;
  67. /**
  68. * @param {object} options
  69. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] Transforms geometry from model to world coordinates.
  70. * @param {number} [options.primitiveCountMax=BufferPrimitiveCollection.DEFAULT_CAPACITY]
  71. * @param {number} [options.vertexCountMax=BufferPrimitiveCollection.DEFAULT_CAPACITY]
  72. * @param {boolean} [options.show=true]
  73. * @param {ComponentDatatype} [options.positionDatatype=ComponentDatatype.DOUBLE]
  74. * @param {boolean} [options.positionNormalized=false] When <code>true</code>, integer position values are treated as normalized,
  75. * where the full integer range maps to [-1, 1] (signed) or [0, 1] (unsigned). Only relevant for integer position datatypes
  76. * (BYTE, UNSIGNED_BYTE, SHORT, UNSIGNED_SHORT).
  77. * @param {boolean} [options.allowPicking=false] When <code>true</code>, primitives are pickable with {@link Scene#pick}. When <code>false</code>, memory and initialization cost are lower.
  78. * @param {BoundingSphere} [options.boundingVolume] Bounding volume, in world space, for the collection. When
  79. * unspecified, a bounding volume is computed automatically and updated when primitive positions change. When
  80. * specified, users are responsible for updating bounding volume as needed. Pre-computing the bounding volume
  81. * manually, and updating it only as needed, will improve performance for larger dynamic collections.
  82. * @param {boolean} [options.debugShowBoundingVolume=false]
  83. * @param {BlendOption} [options.blendOption=BlendOption.TRANSLUCENT]
  84. */
  85. constructor(options = Frozen.EMPTY_OBJECT) {
  86. /**
  87. * Determines if primitives in this collection will be shown.
  88. * @type {boolean}
  89. * @default true
  90. */
  91. this.show = options.show ?? true;
  92. /**
  93. * Collection blend option; must be OPAQUE or TRANSLUCENT.
  94. * @type {BlendOption}
  95. * @readonly
  96. * @ignore
  97. */
  98. this._blendOption = options.blendOption ?? BlendOption.TRANSLUCENT;
  99. /**
  100. * Transforms geometry from model to world coordinates.
  101. * @type {Matrix4}
  102. * @default Matrix4.IDENTITY
  103. * @readonly
  104. * @protected
  105. */
  106. this._modelMatrix = Matrix4.clone(options.modelMatrix ?? Matrix4.IDENTITY);
  107. /**
  108. * @type {BoundingSphere}
  109. * @readonly
  110. * @protected
  111. */
  112. this._boundingVolume = BoundingSphere.clone(
  113. options.boundingVolume ?? new BoundingSphere(),
  114. new BoundingSphere(),
  115. );
  116. /**
  117. * @type {boolean}
  118. * @readonly
  119. * @protected
  120. */
  121. this._boundingVolumeAutoUpdate = !defined(options.boundingVolume);
  122. /**
  123. * When <code>true</code>, primitives are pickable with {@link Scene#pick}.
  124. * When <code>false</code>, memory and initialization cost are lower.
  125. * @type {boolean}
  126. * @readonly
  127. * @ignore
  128. * @default false
  129. */
  130. this._allowPicking = options.allowPicking ?? false;
  131. /**
  132. * @type {Map<Context, PickId[]>}
  133. * @readonly
  134. * @ignore
  135. */
  136. this._pickIds = new Map();
  137. /**
  138. * @type {object[]}
  139. * @readonly
  140. * @ignore
  141. */
  142. this._pickObjects = [];
  143. /**
  144. * This property is for debugging only; it is not for production use nor is it optimized.
  145. * <p>
  146. * Draws the bounding sphere for each draw command in the primitive.
  147. * </p>
  148. *
  149. * @type {boolean}
  150. * @default false
  151. */
  152. this.debugShowBoundingVolume = options.debugShowBoundingVolume ?? false;
  153. /**
  154. * @type {number}
  155. * @protected
  156. * @ignore
  157. */
  158. this._primitiveCount = 0;
  159. /**
  160. * @type {number}
  161. * @protected
  162. * @ignore
  163. */
  164. this._primitiveCountMax =
  165. options.primitiveCountMax ?? BufferPrimitiveCollection.DEFAULT_CAPACITY;
  166. /**
  167. * @type {DataView<ArrayBuffer>}
  168. * @ignore
  169. */
  170. this._primitiveView = null;
  171. /**
  172. * @type {number}
  173. * @ignore
  174. */
  175. this._positionCount = 0;
  176. /**
  177. * @type {number}
  178. * @protected
  179. * @ignore
  180. */
  181. this._positionCountMax =
  182. options.vertexCountMax ?? BufferPrimitiveCollection.DEFAULT_CAPACITY;
  183. /**
  184. * @type {TypedArray}
  185. * @ignore
  186. */
  187. this._positionView = null;
  188. /**
  189. * @type {ComponentDatatype}
  190. * @ignore
  191. */
  192. this._positionDatatype =
  193. options.positionDatatype ?? ComponentDatatype.DOUBLE;
  194. /**
  195. * When <code>true</code>, integer position values represent normalized floats
  196. * in [-1, 1] (signed) or [0, 1] (unsigned). Only applicable to integer datatypes.
  197. * @type {boolean}
  198. * @ignore
  199. */
  200. this._positionNormalized = options.positionNormalized ?? false;
  201. /**
  202. * @type {DataView<ArrayBuffer>}
  203. * @ignore
  204. */
  205. this._materialView = null;
  206. // Potentially-dirty primitives are tracked as a contiguous range, with
  207. // 'clean' primitives potentially within the range. Individual primitive
  208. // 'dirty' flags are source-of-truth.
  209. /**
  210. * @type {number}
  211. * @ignore
  212. */
  213. this._dirtyOffset = 0;
  214. /**
  215. * @type {number}
  216. * @ignore
  217. */
  218. this._dirtyCount = 0;
  219. /**
  220. * @type {boolean}
  221. * @ignore
  222. */
  223. this._dirtyBoundingVolume = false;
  224. this._allocatePrimitiveBuffer();
  225. this._allocatePositionBuffer();
  226. this._allocateMaterialBuffer();
  227. }
  228. /**
  229. * Accessing `this.constructor` can cause JSDoc builds to fail, so use this
  230. * protected getter function instead.
  231. * @protected
  232. * @return {*}
  233. * @ignore
  234. */
  235. _getCollectionClass() {
  236. DeveloperError.throwInstantiationError();
  237. }
  238. /**
  239. * @protected
  240. * @return {*}
  241. * @ignore
  242. */
  243. _getPrimitiveClass() {
  244. DeveloperError.throwInstantiationError();
  245. }
  246. /**
  247. * @return {*}
  248. * @ignore
  249. */
  250. _getMaterialClass() {
  251. DeveloperError.throwInstantiationError();
  252. }
  253. /////////////////////////////////////////////////////////////////////////////
  254. // COLLECTION LIFECYCLE
  255. /**
  256. * @private
  257. * @ignore
  258. */
  259. _allocatePrimitiveBuffer() {
  260. const layout = this._getPrimitiveClass().Layout;
  261. //>>includeStart('debug', pragmas.debug);
  262. const { ERR_MULTIPLE_OF_FOUR } = BufferPrimitiveCollection.Error;
  263. assert(layout.__BYTE_LENGTH % 4 === 0, ERR_MULTIPLE_OF_FOUR);
  264. //>>includeEnd('debug');
  265. this._primitiveView = new DataView(
  266. new ArrayBuffer(this._primitiveCountMax * layout.__BYTE_LENGTH),
  267. );
  268. }
  269. /**
  270. * @private
  271. * @ignore
  272. */
  273. _allocatePositionBuffer() {
  274. // @ts-expect-error https://github.com/CesiumGS/cesium/issues/13420
  275. this._positionView = ComponentDatatype.createTypedArray(
  276. this._positionDatatype,
  277. this._positionCountMax * 3,
  278. );
  279. }
  280. /**
  281. * @private
  282. * @ignore
  283. */
  284. _allocateMaterialBuffer() {
  285. const MaterialClass = this._getMaterialClass();
  286. this._materialView = new DataView(
  287. new ArrayBuffer(this._primitiveCountMax * MaterialClass.packedLength),
  288. );
  289. }
  290. /**
  291. * Returns true if this object was destroyed; otherwise, false.
  292. *
  293. * @returns {boolean} True if this object was destroyed; otherwise, false.
  294. */
  295. isDestroyed() {
  296. return false;
  297. }
  298. /** Destroys collection and its GPU resources. */
  299. destroy() {
  300. this._pickObjects.length = 0;
  301. for (const contextPickIds of this._pickIds.values()) {
  302. for (const pickId of contextPickIds) {
  303. pickId.destroy();
  304. }
  305. }
  306. if (defined(this._renderContext)) {
  307. this._renderContext.destroy();
  308. this._renderContext = undefined;
  309. this._dirtyOffset = 0;
  310. this._dirtyCount = this.primitiveCount;
  311. }
  312. }
  313. /**
  314. * Sorts primitives of the collection.
  315. *
  316. * Because sorting changes the indices (but not the feature IDs) of primitives
  317. * in the collection, the function also returns an array mapping from previous
  318. * index to new index. When sorting repeatedly, the array can be reused and
  319. * passed as the 'result' argument for each call.
  320. *
  321. * @param {Function} sortFn
  322. * @param {Uint32Array} result
  323. * @returns {Uint32Array} Mapping from previous index to new index.
  324. */
  325. sort(sortFn, result = new Uint32Array(this.primitiveCount)) {
  326. const PrimitiveClass = this._getPrimitiveClass();
  327. const CollectionClass = this._getCollectionClass();
  328. const { primitiveCount } = this;
  329. const a = new PrimitiveClass();
  330. const b = new PrimitiveClass();
  331. // Mapping from NEW index to PREVIOUS index.
  332. const dstSrcMap = new Uint32Array(primitiveCount);
  333. for (let i = 0; i < primitiveCount; i++) {
  334. dstSrcMap[i] = i;
  335. }
  336. dstSrcMap.sort((indexA, indexB) =>
  337. sortFn(this.get(indexA, a), this.get(indexB, b)),
  338. );
  339. // Mapping from PREVIOUS index to NEW index.
  340. for (let i = 0; i < primitiveCount; i++) {
  341. result[dstSrcMap[i]] = i;
  342. }
  343. // Copy primitives to temporary collection, in sort order.
  344. const tmp = CollectionClass._cloneEmpty(this);
  345. for (let i = 0; i < primitiveCount; i++) {
  346. const src = this.get(dstSrcMap[i], a);
  347. const dst = tmp.add({}, b);
  348. PrimitiveClass.clone(src, dst);
  349. }
  350. // Assign buffers from temporary collection onto this one.
  351. CollectionClass._replaceBuffers(tmp, this);
  352. this._dirtyOffset = 0;
  353. this._dirtyCount = primitiveCount;
  354. return result;
  355. }
  356. /**
  357. * Duplicates the contents of this collection into the result collection.
  358. * Result collection is not resized, and must contain enough space for all
  359. * primitives in the source collection. Existing primitives in the result
  360. * collection will be overwritten.
  361. *
  362. * <p>Useful when allocating more space for a collection that has reached its
  363. * capacity, and efficiently transferring features to the new collection.</p>
  364. *
  365. * @example
  366. * const result = new BufferPrimitiveCollection({ ... }); // allocate larger 'result' collection
  367. * BufferPrimitiveCollection.clone(collection, result); // copy primitives from 'collection' into 'result'
  368. *
  369. * @param {BufferPrimitiveCollection<T>} collection
  370. * @param {BufferPrimitiveCollection<T>} result
  371. * @template T extends BufferPrimitive
  372. */
  373. static clone(collection, result) {
  374. //>>includeStart('debug', pragmas.debug);
  375. const { ERR_CAPACITY } = BufferPrimitiveCollection.Error;
  376. assert(collection.primitiveCount <= result.primitiveCountMax, ERR_CAPACITY);
  377. assert(collection.vertexCount <= result.vertexCountMax, ERR_CAPACITY);
  378. //>>includeEnd('debug');
  379. const layout = collection._getPrimitiveClass().Layout;
  380. const MaterialClass = collection._getMaterialClass();
  381. const PrimitiveClass = collection._getPrimitiveClass();
  382. this._copySubDataView(
  383. collection._primitiveView,
  384. result._primitiveView,
  385. collection.primitiveCount * layout.__BYTE_LENGTH,
  386. );
  387. this._copySubArray(
  388. collection._positionView,
  389. result._positionView,
  390. collection.vertexCount * 3,
  391. );
  392. this._copySubDataView(
  393. collection._materialView,
  394. result._materialView,
  395. collection.primitiveCount * MaterialClass.packedLength,
  396. );
  397. result.show = collection.show;
  398. result.debugShowBoundingVolume = collection.debugShowBoundingVolume;
  399. result._primitiveCount = collection._primitiveCount;
  400. result._positionCount = collection._positionCount;
  401. // Unset PickIds.
  402. const primitive = new PrimitiveClass();
  403. for (let i = 0, il = result.primitiveCount; i < il; i++) {
  404. result.get(i, primitive)._pickId = 0;
  405. }
  406. result._dirtyOffset = 0;
  407. result._dirtyCount = result.primitiveCount;
  408. collection.boundingVolume.clone(result.boundingVolume);
  409. return result;
  410. }
  411. /**
  412. * Returns an empty collection with the same buffer sizes as this collection.
  413. * Internal utility for operations requiring a working copy of memory.
  414. *
  415. * @param {BufferPrimitiveCollection<T>} collection
  416. * @returns {BufferPrimitiveCollection<T>}
  417. * @template T extends BufferPrimitive
  418. * @protected
  419. * @abstract
  420. * @ignore
  421. */
  422. static _cloneEmpty(collection) {
  423. DeveloperError.throwInstantiationError();
  424. }
  425. /**
  426. * Assigns buffers from source collection to target collection, without
  427. * validation or side effects. Callers must handle any validation, dirty
  428. * flag updates, etc.
  429. *
  430. * @param {BufferPrimitiveCollection<T>} src
  431. * @param {BufferPrimitiveCollection<T>} dst
  432. * @template T extends BufferPrimitive
  433. * @protected
  434. * @ignore
  435. */
  436. static _replaceBuffers(src, dst) {
  437. dst._primitiveView = src._primitiveView;
  438. dst._positionView = src._positionView;
  439. dst._materialView = src._materialView;
  440. }
  441. /**
  442. * Rebuilds collection bounding volume.
  443. * @protected
  444. * @ignore
  445. */
  446. _updateBoundingVolume() {
  447. // Exclude unused space in the position buffer.
  448. let vertices = this._positionView.subarray(0, this._positionCount * 3);
  449. if (this._positionNormalized) {
  450. vertices = AttributeCompression.dequantize(
  451. /** @type {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array} */ (
  452. vertices
  453. ),
  454. this._positionDatatype,
  455. AttributeType.VEC3,
  456. this._positionCount,
  457. );
  458. }
  459. BoundingSphere.fromVertices(
  460. vertices,
  461. Cartesian3.ZERO,
  462. 3,
  463. this.boundingVolume,
  464. );
  465. BoundingSphere.transform(
  466. this.boundingVolume,
  467. this.modelMatrix,
  468. this.boundingVolume,
  469. );
  470. this._dirtyBoundingVolume = false;
  471. }
  472. /**
  473. * Updates PickIds for the given context.
  474. * @param {Context} context
  475. * @protected
  476. * @ignore
  477. */
  478. _updatePickIds(context) {
  479. let pickIds = this._pickIds.get(context);
  480. if (pickIds && pickIds.length === this._primitiveCount) {
  481. return;
  482. }
  483. if (!pickIds) {
  484. pickIds = [];
  485. this._pickIds.set(context, pickIds);
  486. }
  487. const collection = this;
  488. const PrimitiveClass = this._getPrimitiveClass();
  489. const primitive = new PrimitiveClass();
  490. // Fill in missing PickIDs for recently-added primitives.
  491. for (let i = pickIds.length, il = this._primitiveCount; i < il; i++) {
  492. this.get(i, primitive);
  493. const pickObject = this._pickObjects[i] || {
  494. collection: this,
  495. index: i,
  496. get primitive() {
  497. // Cannot reuse primitives; scene.drillPick() appends to a list.
  498. return collection.get(i, new PrimitiveClass());
  499. },
  500. };
  501. const pickId = context.createPickId(pickObject);
  502. primitive._pickId = pickId.key;
  503. pickIds.push(pickId);
  504. }
  505. }
  506. /////////////////////////////////////////////////////////////////////////////
  507. // PRIMITIVE LIFECYCLE
  508. /**
  509. * Makes the given {@link BufferPrimitive} a view onto this collection's
  510. * primitive at the given index, for use when reading/writing primitive
  511. * properties. When iterating over a large collection, prefer to reuse
  512. * the same BufferPrimitive instance throughout the loop — rebinding
  513. * an existing instance to a different primitive is cheap, and avoids
  514. * allocating in-memory objects for every object.
  515. *
  516. * @example
  517. * const primitive = new BufferPrimitive();
  518. * for (let i = 0; i < collection.primitiveCount; i++) {
  519. * collection.get(i, primitive);
  520. * primitive.setColor(Color.RED);
  521. * }
  522. *
  523. * @param {number} index
  524. * @param {BufferPrimitive} result
  525. * @returns {BufferPrimitive} The BufferPrimitive instance passed as the
  526. * 'result' argument, now bound to the specified primitive index.
  527. */
  528. get(index, result) {
  529. //>>includeStart('debug', pragmas.debug);
  530. Check.typeOf.number.greaterThanOrEquals("index", index, 0);
  531. Check.typeOf.number.lessThan("index", index, this._primitiveCount);
  532. //>>includeEnd('debug');
  533. result._collection = this;
  534. result._index = index;
  535. result._byteOffset = index * this._getPrimitiveClass().Layout.__BYTE_LENGTH;
  536. return result;
  537. }
  538. /**
  539. * Adds a new primitive to the collection, with the specified options. A
  540. * {@link BufferPrimitive} instance is linked to the new primitive, using
  541. * the 'result' argument if given, or a new instance if not. For repeated
  542. * calls, prefer to reuse a single BufferPrimitive instance rather than
  543. * allocating a new instance on each call.
  544. *
  545. * @param {BufferPrimitiveOptions} options
  546. * @param {BufferPrimitive} result
  547. * @returns {BufferPrimitive}
  548. */
  549. add(options = Frozen.EMPTY_OBJECT, result) {
  550. //>>includeStart('debug', pragmas.debug);
  551. const { ERR_CAPACITY } = BufferPrimitiveCollection.Error;
  552. assert(this.primitiveCount < this.primitiveCountMax, ERR_CAPACITY);
  553. //>>includeEnd('debug');
  554. const MaterialClass = this._getMaterialClass();
  555. const index = this._primitiveCount++;
  556. result = this.get(index, result);
  557. result.featureId = options.featureId ?? index;
  558. result.show = options.show ?? true;
  559. result.setMaterial(options.material ?? MaterialClass.DEFAULT_MATERIAL);
  560. result._pickId = 0; // unset
  561. result._dirty = true;
  562. if (defined(options.pickObject)) {
  563. this._pickObjects[index] = options.pickObject;
  564. }
  565. return result;
  566. }
  567. /**
  568. * Marks primitive at given index as 'dirty', to be updated on next render.
  569. * @param {number} index
  570. * @ignore
  571. */
  572. _makeDirty(index) {
  573. if (this._dirtyCount === 0) {
  574. this._dirtyCount = 1;
  575. this._dirtyOffset = index;
  576. } else if (index < this._dirtyOffset) {
  577. this._dirtyCount += this._dirtyOffset - index;
  578. this._dirtyOffset = index;
  579. } else if (index + 1 > this._dirtyOffset + this._dirtyCount) {
  580. this._dirtyCount = index + 1 - this._dirtyOffset;
  581. }
  582. }
  583. /**
  584. * Marks collection bounding volume as 'dirty', to be updated on next render,
  585. * if automatic bounding volume updates are enabled.
  586. * @ignore
  587. */
  588. _makeDirtyBoundingVolume() {
  589. if (this._boundingVolumeAutoUpdate) {
  590. this._dirtyBoundingVolume = true;
  591. }
  592. }
  593. /////////////////////////////////////////////////////////////////////////////
  594. // RENDER
  595. /** @param {object} frameState */
  596. update(frameState) {
  597. if (/** @type {FrameState} */ (frameState).mode !== SceneMode.SCENE3D) {
  598. oneTimeWarning(
  599. "bufferprim-scenemode",
  600. "BufferPrimitiveCollection requires SceneMode.SCENE3D.",
  601. );
  602. }
  603. if (this._dirtyBoundingVolume) {
  604. this._updateBoundingVolume();
  605. }
  606. if (this._allowPicking && this._dirtyCount > 0) {
  607. this._updatePickIds(/** @type {FrameState} */ (frameState).context);
  608. }
  609. }
  610. /////////////////////////////////////////////////////////////////////////////
  611. // ACCESSORS
  612. /**
  613. * Number of primitives in collection. Must be <= {@link primitiveCountMax}.
  614. *
  615. * @type {number}
  616. * @readonly
  617. */
  618. get primitiveCount() {
  619. return this._primitiveCount;
  620. }
  621. /**
  622. * Maximum number of primitives this collection can contain. Must be >=
  623. * {@link primitiveCount}.
  624. *
  625. * @type {number}
  626. * @readonly
  627. * @default {@link BufferPrimitiveCollection.DEFAULT_CAPACITY}
  628. */
  629. get primitiveCountMax() {
  630. return this._primitiveCountMax;
  631. }
  632. /**
  633. * Total byte length of buffers owned by this collection. Includes any unused
  634. * space allocated by {@link primitiveCountMax}, even if no primitives have
  635. * yet been added in that space.
  636. *
  637. * @type {number}
  638. * @readonly
  639. */
  640. get byteLength() {
  641. return (
  642. this._primitiveView.byteLength +
  643. this._positionView.byteLength +
  644. this._materialView.byteLength
  645. );
  646. }
  647. /**
  648. * Number of vertices in collection. Must be <= {@link vertexCountMax}.
  649. *
  650. * @type {number}
  651. * @readonly
  652. */
  653. get vertexCount() {
  654. return this._positionCount;
  655. }
  656. /**
  657. * Maximum number of vertices this collection can contain. Must be >=
  658. * {@link vertexCount}.
  659. *
  660. * @type {number}
  661. * @readonly
  662. * @default {@link BufferPrimitiveCollection.DEFAULT_CAPACITY}
  663. */
  664. get vertexCountMax() {
  665. return this._positionCountMax;
  666. }
  667. /**
  668. * Transforms geometry from model to world coordinates.
  669. * @type {Matrix4}
  670. * @default Matrix4.IDENTITY
  671. * @readonly
  672. */
  673. get modelMatrix() {
  674. return this._modelMatrix;
  675. }
  676. /**
  677. * World-space bounding volume for all primitives in the collection, including both
  678. * shown and hidden primitives.
  679. * @type {BoundingSphere}
  680. * @readonly
  681. */
  682. get boundingVolume() {
  683. return this._boundingVolume;
  684. }
  685. /**
  686. * The component datatype used to store position values.
  687. * @type {ComponentDatatype}
  688. * @readonly
  689. */
  690. get positionDatatype() {
  691. return this._positionDatatype;
  692. }
  693. /**
  694. * When <code>true</code>, integer position values are treated as normalized
  695. * values, where the full integer range maps to [-1, 1] (signed) or [0, 1]
  696. * (unsigned).
  697. * @type {boolean}
  698. * @readonly
  699. */
  700. get positionNormalized() {
  701. return this._positionNormalized;
  702. }
  703. /////////////////////////////////////////////////////////////////////////////
  704. // UTILS
  705. /**
  706. * @param {TypedArray} src
  707. * @param {TypedArray} dst
  708. * @param {number} count
  709. * @protected
  710. * @ignore
  711. */
  712. static _copySubArray(src, dst, count) {
  713. for (let i = 0; i < count; i++) {
  714. dst[i] = src[i];
  715. }
  716. }
  717. /**
  718. * @param {DataView} src
  719. * @param {DataView} dst
  720. * @param {number} byteLength
  721. * @protected
  722. * @ignore
  723. */
  724. static _copySubDataView(src, dst, byteLength) {
  725. // No need to match the original array type, just copy in 4-byte chunks.
  726. this._copySubArray(
  727. new Uint32Array(src.buffer, src.byteOffset, src.byteLength / 4),
  728. new Uint32Array(dst.buffer, dst.byteOffset, dst.byteLength / 4),
  729. byteLength / 4,
  730. );
  731. }
  732. /////////////////////////////////////////////////////////////////////////////
  733. // DEBUG
  734. /**
  735. * Returns a JSON-serializable array representing the collection. This encoding
  736. * is not memory-efficient, and should generally be used for debugging and
  737. * testing.
  738. *
  739. * @example
  740. * console.table(collection.toJSON());
  741. *
  742. * @returns {Array<Object>} List of JSON-serializable objects, one for each
  743. * primitive in the collection.
  744. */
  745. toJSON() {
  746. const PrimitiveClass = this._getPrimitiveClass();
  747. const primitive = new PrimitiveClass();
  748. const results = [];
  749. for (let i = 0, il = this.primitiveCount; i < il; i++) {
  750. results.push(this.get(i, primitive).toJSON());
  751. }
  752. return results;
  753. }
  754. }
  755. /**
  756. * Default capacity of buffers on new collections. A quantity of elements:
  757. * number of vertices in the vertex buffer, primitives in the primitive
  758. * buffer, etc. This value is arbitrary, and collections cannot be resized,
  759. * so specific per-buffer capacities should be provided in the collection
  760. * constructor when available.
  761. *
  762. * @type {number}
  763. * @static
  764. * @constant
  765. */
  766. BufferPrimitiveCollection.DEFAULT_CAPACITY = 1024;
  767. export default BufferPrimitiveCollection;