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

VertexArray.js 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. import Check from "../Core/Check.js";
  2. import ComponentDatatype from "../Core/ComponentDatatype.js";
  3. import Frozen from "../Core/Frozen.js";
  4. import defined from "../Core/defined.js";
  5. import destroyObject from "../Core/destroyObject.js";
  6. import DeveloperError from "../Core/DeveloperError.js";
  7. import Geometry from "../Core/Geometry.js";
  8. import IndexDatatype from "../Core/IndexDatatype.js";
  9. import CesiumMath from "../Core/Math.js";
  10. import RuntimeError from "../Core/RuntimeError.js";
  11. import Buffer from "./Buffer.js";
  12. import BufferUsage from "./BufferUsage.js";
  13. import ContextLimits from "./ContextLimits.js";
  14. import AttributeType from "../Scene/AttributeType.js";
  15. import assert from "../Core/assert.js";
  16. /** @import {TypedArray, TypedArrayConstructor} from "../Core/globalTypes.js"; */
  17. function addAttribute(attributes, attribute, index, context) {
  18. const hasVertexBuffer = defined(attribute.vertexBuffer);
  19. const hasValue = defined(attribute.value);
  20. const componentsPerAttribute = attribute.value
  21. ? attribute.value.length
  22. : attribute.componentsPerAttribute;
  23. //>>includeStart('debug', pragmas.debug);
  24. if (!hasVertexBuffer && !hasValue) {
  25. throw new DeveloperError("attribute must have a vertexBuffer or a value.");
  26. }
  27. if (hasVertexBuffer && hasValue) {
  28. throw new DeveloperError(
  29. "attribute cannot have both a vertexBuffer and a value. It must have either a vertexBuffer property defining per-vertex data or a value property defining data for all vertices.",
  30. );
  31. }
  32. if (
  33. componentsPerAttribute !== 1 &&
  34. componentsPerAttribute !== 2 &&
  35. componentsPerAttribute !== 3 &&
  36. componentsPerAttribute !== 4
  37. ) {
  38. if (hasValue) {
  39. throw new DeveloperError(
  40. "attribute.value.length must be in the range [1, 4].",
  41. );
  42. }
  43. throw new DeveloperError(
  44. "attribute.componentsPerAttribute must be in the range [1, 4].",
  45. );
  46. }
  47. if (
  48. defined(attribute.componentDatatype) &&
  49. !ComponentDatatype.validate(attribute.componentDatatype)
  50. ) {
  51. throw new DeveloperError(
  52. "attribute must have a valid componentDatatype or not specify it.",
  53. );
  54. }
  55. if (defined(attribute.strideInBytes) && attribute.strideInBytes > 255) {
  56. // WebGL limit. Not in GL ES.
  57. throw new DeveloperError(
  58. "attribute must have a strideInBytes less than or equal to 255 or not specify it.",
  59. );
  60. }
  61. if (
  62. defined(attribute.instanceDivisor) &&
  63. attribute.instanceDivisor > 0 &&
  64. !context.instancedArrays
  65. ) {
  66. throw new DeveloperError("instanced arrays is not supported");
  67. }
  68. if (defined(attribute.instanceDivisor) && attribute.instanceDivisor < 0) {
  69. throw new DeveloperError(
  70. "attribute must have an instanceDivisor greater than or equal to zero",
  71. );
  72. }
  73. if (defined(attribute.instanceDivisor) && hasValue) {
  74. throw new DeveloperError(
  75. "attribute cannot have have an instanceDivisor if it is not backed by a buffer",
  76. );
  77. }
  78. if (
  79. defined(attribute.instanceDivisor) &&
  80. attribute.instanceDivisor > 0 &&
  81. attribute.index === 0
  82. ) {
  83. throw new DeveloperError(
  84. "attribute zero cannot have an instanceDivisor greater than 0",
  85. );
  86. }
  87. //>>includeEnd('debug');
  88. // Shallow copy the attribute; we do not want to copy the vertex buffer.
  89. const attr = {
  90. index: attribute.index ?? index,
  91. enabled: attribute.enabled ?? true,
  92. vertexBuffer: attribute.vertexBuffer,
  93. value: hasValue ? attribute.value.slice(0) : undefined,
  94. componentsPerAttribute: componentsPerAttribute,
  95. componentDatatype: attribute.componentDatatype ?? ComponentDatatype.FLOAT,
  96. normalize: attribute.normalize ?? false,
  97. offsetInBytes: attribute.offsetInBytes ?? 0,
  98. strideInBytes: attribute.strideInBytes ?? 0,
  99. instanceDivisor: attribute.instanceDivisor ?? 0,
  100. };
  101. if (hasVertexBuffer) {
  102. // Common case: vertex buffer for per-vertex data
  103. attr.vertexAttrib = function (gl) {
  104. const index = this.index;
  105. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer._getBuffer());
  106. gl.vertexAttribPointer(
  107. index,
  108. this.componentsPerAttribute,
  109. this.componentDatatype,
  110. this.normalize,
  111. this.strideInBytes,
  112. this.offsetInBytes,
  113. );
  114. gl.enableVertexAttribArray(index);
  115. if (this.instanceDivisor > 0) {
  116. context.glVertexAttribDivisor(index, this.instanceDivisor);
  117. context._vertexAttribDivisors[index] = this.instanceDivisor;
  118. context._previousDrawInstanced = true;
  119. }
  120. };
  121. attr.disableVertexAttribArray = function (gl) {
  122. gl.disableVertexAttribArray(this.index);
  123. if (this.instanceDivisor > 0) {
  124. context.glVertexAttribDivisor(index, 0);
  125. }
  126. };
  127. } else {
  128. // Less common case: value array for the same data for each vertex
  129. switch (attr.componentsPerAttribute) {
  130. case 1:
  131. attr.vertexAttrib = function (gl) {
  132. gl.vertexAttrib1fv(this.index, this.value);
  133. };
  134. break;
  135. case 2:
  136. attr.vertexAttrib = function (gl) {
  137. gl.vertexAttrib2fv(this.index, this.value);
  138. };
  139. break;
  140. case 3:
  141. attr.vertexAttrib = function (gl) {
  142. gl.vertexAttrib3fv(this.index, this.value);
  143. };
  144. break;
  145. case 4:
  146. attr.vertexAttrib = function (gl) {
  147. gl.vertexAttrib4fv(this.index, this.value);
  148. };
  149. break;
  150. }
  151. attr.disableVertexAttribArray = function (gl) {};
  152. }
  153. attributes.push(attr);
  154. }
  155. function bind(gl, attributes, indexBuffer) {
  156. for (let i = 0; i < attributes.length; ++i) {
  157. const attribute = attributes[i];
  158. if (attribute.enabled) {
  159. attribute.vertexAttrib(gl);
  160. }
  161. }
  162. if (defined(indexBuffer)) {
  163. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer._getBuffer());
  164. }
  165. }
  166. /**
  167. * Creates a vertex array, which defines the attributes making up a vertex, and contains an optional index buffer
  168. * to select vertices for rendering. Attributes are defined using object literals as shown in Example 1 below.
  169. *
  170. * @param {object} options Object with the following properties:
  171. * @param {Context} options.context The context in which the VertexArray gets created.
  172. * @param {object[]} options.attributes An array of attributes.
  173. * @param {IndexBuffer} [options.indexBuffer] An optional index buffer.
  174. *
  175. * @returns {VertexArray} The vertex array, ready for use with drawing.
  176. *
  177. * @exception {DeveloperError} Attribute must have a <code>vertexBuffer</code>.
  178. * @exception {DeveloperError} Attribute must have a <code>componentsPerAttribute</code>.
  179. * @exception {DeveloperError} Attribute must have a valid <code>componentDatatype</code> or not specify it.
  180. * @exception {DeveloperError} Attribute must have a <code>strideInBytes</code> less than or equal to 255 or not specify it.
  181. * @exception {DeveloperError} Index n is used by more than one attribute.
  182. *
  183. *
  184. * @example
  185. * // Example 1. Create a vertex array with vertices made up of three floating point
  186. * // values, e.g., a position, from a single vertex buffer. No index buffer is used.
  187. * const positionBuffer = Buffer.createVertexBuffer({
  188. * context : context,
  189. * sizeInBytes : 12,
  190. * usage : BufferUsage.STATIC_DRAW
  191. * });
  192. * const attributes = [
  193. * {
  194. * index : 0,
  195. * enabled : true,
  196. * vertexBuffer : positionBuffer,
  197. * componentsPerAttribute : 3,
  198. * componentDatatype : ComponentDatatype.FLOAT,
  199. * normalize : false,
  200. * offsetInBytes : 0,
  201. * strideInBytes : 0 // tightly packed
  202. * instanceDivisor : 0 // not instanced
  203. * }
  204. * ];
  205. * const va = new VertexArray({
  206. * context : context,
  207. * attributes : attributes
  208. * });
  209. *
  210. * @example
  211. * // Example 2. Create a vertex array with vertices from two different vertex buffers.
  212. * // Each vertex has a three-component position and three-component normal.
  213. * const positionBuffer = Buffer.createVertexBuffer({
  214. * context : context,
  215. * sizeInBytes : 12,
  216. * usage : BufferUsage.STATIC_DRAW
  217. * });
  218. * const normalBuffer = Buffer.createVertexBuffer({
  219. * context : context,
  220. * sizeInBytes : 12,
  221. * usage : BufferUsage.STATIC_DRAW
  222. * });
  223. * const attributes = [
  224. * {
  225. * index : 0,
  226. * vertexBuffer : positionBuffer,
  227. * componentsPerAttribute : 3,
  228. * componentDatatype : ComponentDatatype.FLOAT
  229. * },
  230. * {
  231. * index : 1,
  232. * vertexBuffer : normalBuffer,
  233. * componentsPerAttribute : 3,
  234. * componentDatatype : ComponentDatatype.FLOAT
  235. * }
  236. * ];
  237. * const va = new VertexArray({
  238. * context : context,
  239. * attributes : attributes
  240. * });
  241. *
  242. * @example
  243. * // Example 3. Creates the same vertex layout as Example 2 using a single
  244. * // vertex buffer, instead of two.
  245. * const buffer = Buffer.createVertexBuffer({
  246. * context : context,
  247. * sizeInBytes : 24,
  248. * usage : BufferUsage.STATIC_DRAW
  249. * });
  250. * const attributes = [
  251. * {
  252. * vertexBuffer : buffer,
  253. * componentsPerAttribute : 3,
  254. * componentDatatype : ComponentDatatype.FLOAT,
  255. * offsetInBytes : 0,
  256. * strideInBytes : 24
  257. * },
  258. * {
  259. * vertexBuffer : buffer,
  260. * componentsPerAttribute : 3,
  261. * componentDatatype : ComponentDatatype.FLOAT,
  262. * normalize : true,
  263. * offsetInBytes : 12,
  264. * strideInBytes : 24
  265. * }
  266. * ];
  267. * const va = new VertexArray({
  268. * context : context,
  269. * attributes : attributes
  270. * });
  271. *
  272. * @see Buffer#createVertexBuffer
  273. * @see Buffer#createIndexBuffer
  274. * @see Context#draw
  275. *
  276. * @private
  277. */
  278. function VertexArray(options) {
  279. options = options ?? Frozen.EMPTY_OBJECT;
  280. //>>includeStart('debug', pragmas.debug);
  281. Check.defined("options.context", options.context);
  282. Check.defined("options.attributes", options.attributes);
  283. //>>includeEnd('debug');
  284. const context = options.context;
  285. const gl = context._gl;
  286. const attributes = options.attributes;
  287. const indexBuffer = options.indexBuffer;
  288. let i;
  289. const vaAttributes = [];
  290. let numberOfVertices = 1; // if every attribute is backed by a single value
  291. let hasInstancedAttributes = false;
  292. let hasConstantAttributes = false;
  293. let length = attributes.length;
  294. for (i = 0; i < length; ++i) {
  295. addAttribute(vaAttributes, attributes[i], i, context);
  296. }
  297. length = vaAttributes.length;
  298. for (i = 0; i < length; ++i) {
  299. const attribute = vaAttributes[i];
  300. if (defined(attribute.vertexBuffer) && attribute.instanceDivisor === 0) {
  301. // This assumes that each vertex buffer in the vertex array has the same number of vertices.
  302. const bytes =
  303. attribute.strideInBytes ||
  304. attribute.componentsPerAttribute *
  305. ComponentDatatype.getSizeInBytes(attribute.componentDatatype);
  306. numberOfVertices = attribute.vertexBuffer.sizeInBytes / bytes;
  307. break;
  308. }
  309. }
  310. for (i = 0; i < length; ++i) {
  311. if (vaAttributes[i].instanceDivisor > 0) {
  312. hasInstancedAttributes = true;
  313. }
  314. if (defined(vaAttributes[i].value)) {
  315. hasConstantAttributes = true;
  316. }
  317. }
  318. //>>includeStart('debug', pragmas.debug);
  319. // Verify all attribute names are unique
  320. const uniqueIndices = {};
  321. for (i = 0; i < length; ++i) {
  322. const index = vaAttributes[i].index;
  323. if (uniqueIndices[index]) {
  324. throw new DeveloperError(
  325. `Index ${index} is used by more than one attribute.`,
  326. );
  327. }
  328. uniqueIndices[index] = true;
  329. }
  330. //>>includeEnd('debug');
  331. let vao;
  332. // Setup VAO if supported
  333. if (context.vertexArrayObject) {
  334. vao = context.glCreateVertexArray();
  335. context.glBindVertexArray(vao);
  336. bind(gl, vaAttributes, indexBuffer);
  337. context.glBindVertexArray(null);
  338. }
  339. this._numberOfVertices = numberOfVertices;
  340. this._hasInstancedAttributes = hasInstancedAttributes;
  341. this._hasConstantAttributes = hasConstantAttributes;
  342. this._context = context;
  343. this._gl = gl;
  344. this._vao = vao;
  345. this._attributes = vaAttributes;
  346. this._indexBuffer = indexBuffer;
  347. }
  348. function computeNumberOfVertices(attribute) {
  349. return attribute.values.length / attribute.componentsPerAttribute;
  350. }
  351. function computeAttributeSizeInBytes(attribute) {
  352. return (
  353. ComponentDatatype.getSizeInBytes(attribute.componentDatatype) *
  354. attribute.componentsPerAttribute
  355. );
  356. }
  357. function interleaveAttributes(attributes) {
  358. let j;
  359. let name;
  360. let attribute;
  361. // Extract attribute names.
  362. const names = [];
  363. for (name in attributes) {
  364. // Attribute needs to have per-vertex values; not a constant value for all vertices.
  365. if (
  366. attributes.hasOwnProperty(name) &&
  367. defined(attributes[name]) &&
  368. defined(attributes[name].values)
  369. ) {
  370. names.push(name);
  371. if (attributes[name].componentDatatype === ComponentDatatype.DOUBLE) {
  372. attributes[name].componentDatatype = ComponentDatatype.FLOAT;
  373. attributes[name].values = ComponentDatatype.createTypedArray(
  374. ComponentDatatype.FLOAT,
  375. attributes[name].values,
  376. );
  377. }
  378. }
  379. }
  380. // Validation. Compute number of vertices.
  381. let numberOfVertices;
  382. const namesLength = names.length;
  383. if (namesLength > 0) {
  384. numberOfVertices = computeNumberOfVertices(attributes[names[0]]);
  385. for (j = 1; j < namesLength; ++j) {
  386. const currentNumberOfVertices = computeNumberOfVertices(
  387. attributes[names[j]],
  388. );
  389. if (currentNumberOfVertices !== numberOfVertices) {
  390. throw new RuntimeError(
  391. `${
  392. "Each attribute list must have the same number of vertices. " +
  393. "Attribute "
  394. }${names[j]} has a different number of vertices ` +
  395. `(${currentNumberOfVertices.toString()})` +
  396. ` than attribute ${names[0]} (${numberOfVertices.toString()}).`,
  397. );
  398. }
  399. }
  400. }
  401. // Sort attributes by the size of their components. From left to right, a vertex stores floats, shorts, and then bytes.
  402. names.sort(function (left, right) {
  403. return (
  404. ComponentDatatype.getSizeInBytes(attributes[right].componentDatatype) -
  405. ComponentDatatype.getSizeInBytes(attributes[left].componentDatatype)
  406. );
  407. });
  408. // Compute sizes and strides.
  409. let vertexSizeInBytes = 0;
  410. const offsetsInBytes = {};
  411. for (j = 0; j < namesLength; ++j) {
  412. name = names[j];
  413. attribute = attributes[name];
  414. offsetsInBytes[name] = vertexSizeInBytes;
  415. vertexSizeInBytes += computeAttributeSizeInBytes(attribute);
  416. }
  417. if (vertexSizeInBytes > 0) {
  418. // Pad each vertex to be a multiple of the largest component datatype so each
  419. // attribute can be addressed using typed arrays.
  420. const maxComponentSizeInBytes = ComponentDatatype.getSizeInBytes(
  421. attributes[names[0]].componentDatatype,
  422. ); // Sorted large to small
  423. const remainder = vertexSizeInBytes % maxComponentSizeInBytes;
  424. if (remainder !== 0) {
  425. vertexSizeInBytes += maxComponentSizeInBytes - remainder;
  426. }
  427. // Total vertex buffer size in bytes, including per-vertex padding.
  428. const vertexBufferSizeInBytes = numberOfVertices * vertexSizeInBytes;
  429. // Create array for interleaved vertices. Each attribute has a different view (pointer) into the array.
  430. const buffer = new ArrayBuffer(vertexBufferSizeInBytes);
  431. const views = {};
  432. for (j = 0; j < namesLength; ++j) {
  433. name = names[j];
  434. const sizeInBytes = ComponentDatatype.getSizeInBytes(
  435. attributes[name].componentDatatype,
  436. );
  437. views[name] = {
  438. pointer: ComponentDatatype.createTypedArray(
  439. attributes[name].componentDatatype,
  440. buffer,
  441. ),
  442. index: offsetsInBytes[name] / sizeInBytes, // Offset in ComponentType
  443. strideInComponentType: vertexSizeInBytes / sizeInBytes,
  444. };
  445. }
  446. // Copy attributes into one interleaved array.
  447. // PERFORMANCE_IDEA: Can we optimize these loops?
  448. for (j = 0; j < numberOfVertices; ++j) {
  449. for (let n = 0; n < namesLength; ++n) {
  450. name = names[n];
  451. attribute = attributes[name];
  452. const values = attribute.values;
  453. const view = views[name];
  454. const pointer = view.pointer;
  455. const numberOfComponents = attribute.componentsPerAttribute;
  456. for (let k = 0; k < numberOfComponents; ++k) {
  457. pointer[view.index + k] = values[j * numberOfComponents + k];
  458. }
  459. view.index += view.strideInComponentType;
  460. }
  461. }
  462. return {
  463. buffer: buffer,
  464. offsetsInBytes: offsetsInBytes,
  465. vertexSizeInBytes: vertexSizeInBytes,
  466. };
  467. }
  468. // No attributes to interleave.
  469. return undefined;
  470. }
  471. /**
  472. * Creates a vertex array from a geometry. A geometry contains vertex attributes and optional index data
  473. * in system memory, whereas a vertex array contains vertex buffers and an optional index buffer in WebGL
  474. * memory for use with rendering.
  475. * <br /><br />
  476. * The <code>geometry</code> argument should use the standard layout like the geometry returned by {@link BoxGeometry}.
  477. * <br /><br />
  478. * <code>options</code> can have four properties:
  479. * <ul>
  480. * <li><code>geometry</code>: The source geometry containing data used to create the vertex array.</li>
  481. * <li><code>attributeLocations</code>: An object that maps geometry attribute names to vertex shader attribute locations.</li>
  482. * <li><code>bufferUsage</code>: The expected usage pattern of the vertex array's buffers. On some WebGL implementations, this can significantly affect performance. See {@link BufferUsage}. Default: <code>BufferUsage.DYNAMIC_DRAW</code>.</li>
  483. * <li><code>interleave</code>: Determines if all attributes are interleaved in a single vertex buffer or if each attribute is stored in a separate vertex buffer. Default: <code>false</code>.</li>
  484. * </ul>
  485. * <br />
  486. * If <code>options</code> is not specified or the <code>geometry</code> contains no data, the returned vertex array is empty.
  487. *
  488. * @param {object} options An object defining the geometry, attribute indices, buffer usage, and vertex layout used to create the vertex array.
  489. *
  490. * @exception {RuntimeError} Each attribute list must have the same number of vertices.
  491. * @exception {DeveloperError} The geometry must have zero or one index lists.
  492. * @exception {DeveloperError} Index n is used by more than one attribute.
  493. *
  494. *
  495. * @example
  496. * // Example 1. Creates a vertex array for rendering a box. The default dynamic draw
  497. * // usage is used for the created vertex and index buffer. The attributes are not
  498. * // interleaved by default.
  499. * const geometry = new BoxGeometry();
  500. * const va = VertexArray.fromGeometry({
  501. * context : context,
  502. * geometry : geometry,
  503. * attributeLocations : GeometryPipeline.createAttributeLocations(geometry),
  504. * });
  505. *
  506. * @example
  507. * // Example 2. Creates a vertex array with interleaved attributes in a
  508. * // single vertex buffer. The vertex and index buffer have static draw usage.
  509. * const va = VertexArray.fromGeometry({
  510. * context : context,
  511. * geometry : geometry,
  512. * attributeLocations : GeometryPipeline.createAttributeLocations(geometry),
  513. * bufferUsage : BufferUsage.STATIC_DRAW,
  514. * interleave : true
  515. * });
  516. *
  517. * @example
  518. * // Example 3. When the caller destroys the vertex array, it also destroys the
  519. * // attached vertex buffer(s) and index buffer.
  520. * va = va.destroy();
  521. *
  522. * @see Buffer#createVertexBuffer
  523. * @see Buffer#createIndexBuffer
  524. * @see GeometryPipeline.createAttributeLocations
  525. * @see ShaderProgram
  526. */
  527. VertexArray.fromGeometry = function (options) {
  528. options = options ?? Frozen.EMPTY_OBJECT;
  529. //>>includeStart('debug', pragmas.debug);
  530. Check.defined("options.context", options.context);
  531. //>>includeEnd('debug');
  532. const context = options.context;
  533. const geometry = options.geometry ?? Frozen.EMPTY_OBJECT;
  534. const bufferUsage = options.bufferUsage ?? BufferUsage.DYNAMIC_DRAW;
  535. const attributeLocations = options.attributeLocations ?? Frozen.EMPTY_OBJECT;
  536. const interleave = options.interleave ?? false;
  537. const createdVAAttributes = options.vertexArrayAttributes;
  538. let name;
  539. let attribute;
  540. let vertexBuffer;
  541. const vaAttributes = defined(createdVAAttributes) ? createdVAAttributes : [];
  542. const attributes = geometry.attributes;
  543. if (interleave) {
  544. // Use a single vertex buffer with interleaved vertices.
  545. const interleavedAttributes = interleaveAttributes(attributes);
  546. if (defined(interleavedAttributes)) {
  547. vertexBuffer = Buffer.createVertexBuffer({
  548. context: context,
  549. typedArray: interleavedAttributes.buffer,
  550. usage: bufferUsage,
  551. });
  552. const offsetsInBytes = interleavedAttributes.offsetsInBytes;
  553. const strideInBytes = interleavedAttributes.vertexSizeInBytes;
  554. for (name in attributes) {
  555. if (attributes.hasOwnProperty(name) && defined(attributes[name])) {
  556. attribute = attributes[name];
  557. if (defined(attribute.values)) {
  558. // Common case: per-vertex attributes
  559. vaAttributes.push({
  560. index: attributeLocations[name],
  561. vertexBuffer: vertexBuffer,
  562. componentDatatype: attribute.componentDatatype,
  563. componentsPerAttribute: attribute.componentsPerAttribute,
  564. normalize: attribute.normalize,
  565. offsetInBytes: offsetsInBytes[name],
  566. strideInBytes: strideInBytes,
  567. });
  568. } else {
  569. // Constant attribute for all vertices
  570. vaAttributes.push({
  571. index: attributeLocations[name],
  572. value: attribute.value,
  573. componentDatatype: attribute.componentDatatype,
  574. normalize: attribute.normalize,
  575. });
  576. }
  577. }
  578. }
  579. }
  580. } else {
  581. // One vertex buffer per attribute.
  582. for (name in attributes) {
  583. if (attributes.hasOwnProperty(name) && defined(attributes[name])) {
  584. attribute = attributes[name];
  585. let componentDatatype = attribute.componentDatatype;
  586. if (componentDatatype === ComponentDatatype.DOUBLE) {
  587. componentDatatype = ComponentDatatype.FLOAT;
  588. }
  589. let attrProps = {};
  590. vertexBuffer = undefined;
  591. if (defined(attribute.values)) {
  592. vertexBuffer = Buffer.createVertexBuffer({
  593. context: context,
  594. typedArray: ComponentDatatype.createTypedArray(
  595. componentDatatype,
  596. attribute.values,
  597. ),
  598. usage: bufferUsage,
  599. });
  600. attrProps = {
  601. index: attributeLocations[name],
  602. vertexBuffer: vertexBuffer,
  603. value: attribute.value,
  604. componentDatatype: componentDatatype,
  605. componentsPerAttribute: attribute.componentsPerAttribute,
  606. normalize: attribute.normalize,
  607. };
  608. }
  609. //if we already have a typedArray lets use it
  610. if (defined(attribute.typedArray)) {
  611. vertexBuffer = Buffer.createVertexBuffer({
  612. context: context,
  613. typedArray: attribute.typedArray,
  614. usage: bufferUsage,
  615. });
  616. attrProps = {
  617. index: attributeLocations[name],
  618. vertexBuffer: vertexBuffer,
  619. value: undefined,
  620. componentDatatype: componentDatatype,
  621. componentsPerAttribute: AttributeType.getNumberOfComponents(
  622. attribute.type,
  623. ),
  624. normalize: attribute.normalized,
  625. instanceDivisor: attribute.instanceDivisor,
  626. };
  627. }
  628. vaAttributes.push(attrProps);
  629. }
  630. }
  631. }
  632. let indexBuffer;
  633. const indices = geometry.indices;
  634. if (defined(indices)) {
  635. if (
  636. Geometry.computeNumberOfVertices(geometry) >=
  637. CesiumMath.SIXTY_FOUR_KILOBYTES &&
  638. context.elementIndexUint
  639. ) {
  640. indexBuffer = Buffer.createIndexBuffer({
  641. context: context,
  642. typedArray: new Uint32Array(indices),
  643. usage: bufferUsage,
  644. indexDatatype: IndexDatatype.UNSIGNED_INT,
  645. });
  646. } else {
  647. indexBuffer = Buffer.createIndexBuffer({
  648. context: context,
  649. typedArray: new Uint16Array(indices),
  650. usage: bufferUsage,
  651. indexDatatype: IndexDatatype.UNSIGNED_SHORT,
  652. });
  653. }
  654. }
  655. return new VertexArray({
  656. context: context,
  657. attributes: vaAttributes,
  658. indexBuffer: indexBuffer,
  659. });
  660. };
  661. Object.defineProperties(VertexArray.prototype, {
  662. numberOfAttributes: {
  663. get: function () {
  664. return this._attributes.length;
  665. },
  666. },
  667. numberOfVertices: {
  668. get: function () {
  669. return this._numberOfVertices;
  670. },
  671. },
  672. indexBuffer: {
  673. get: function () {
  674. return this._indexBuffer;
  675. },
  676. },
  677. });
  678. /**
  679. * index is the location in the array of attributes, not the index property of an attribute.
  680. */
  681. VertexArray.prototype.getAttribute = function (index) {
  682. //>>includeStart('debug', pragmas.debug);
  683. Check.defined("index", index);
  684. //>>includeEnd('debug');
  685. return this._attributes[index];
  686. };
  687. // Workaround for ANGLE, where the attribute divisor seems to be part of the global state instead
  688. // of the VAO state. This function is called when the vao is bound, and should be removed
  689. // once the ANGLE issue is resolved. Setting the divisor should normally happen in vertexAttrib and
  690. // disableVertexAttribArray.
  691. function setVertexAttribDivisor(vertexArray) {
  692. const context = vertexArray._context;
  693. const hasInstancedAttributes = vertexArray._hasInstancedAttributes;
  694. if (!hasInstancedAttributes && !context._previousDrawInstanced) {
  695. return;
  696. }
  697. context._previousDrawInstanced = hasInstancedAttributes;
  698. const divisors = context._vertexAttribDivisors;
  699. const attributes = vertexArray._attributes;
  700. const maxAttributes = ContextLimits.maximumVertexAttributes;
  701. let i;
  702. if (hasInstancedAttributes) {
  703. const length = attributes.length;
  704. for (i = 0; i < length; ++i) {
  705. const attribute = attributes[i];
  706. if (attribute.enabled) {
  707. const divisor = attribute.instanceDivisor;
  708. const index = attribute.index;
  709. if (divisor !== divisors[index]) {
  710. context.glVertexAttribDivisor(index, divisor);
  711. divisors[index] = divisor;
  712. }
  713. }
  714. }
  715. } else {
  716. for (i = 0; i < maxAttributes; ++i) {
  717. if (divisors[i] > 0) {
  718. context.glVertexAttribDivisor(i, 0);
  719. divisors[i] = 0;
  720. }
  721. }
  722. }
  723. }
  724. // Vertex attributes backed by a constant value go through vertexAttrib[1234]f[v]
  725. // which is part of context state rather than VAO state.
  726. function setConstantAttributes(vertexArray, gl) {
  727. const attributes = vertexArray._attributes;
  728. const length = attributes.length;
  729. for (let i = 0; i < length; ++i) {
  730. const attribute = attributes[i];
  731. if (attribute.enabled && defined(attribute.value)) {
  732. attribute.vertexAttrib(gl);
  733. }
  734. }
  735. }
  736. /**
  737. * Copies into a vertex attribute buffer from the given array, at a given
  738. * range specified as offset and count, in number of (VECN) vertices. Array
  739. * and vertex attribute must have the same length, which can be larger
  740. * than the specified range to update.
  741. * @param {number} attributeIndex
  742. * @param {TypedArray} array
  743. * @param {number} vertexOffset
  744. * @param {number} vertexCount
  745. */
  746. VertexArray.prototype.copyAttributeFromRange = function (
  747. attributeIndex,
  748. array,
  749. vertexOffset,
  750. vertexCount,
  751. ) {
  752. const attribute = this.getAttribute(attributeIndex);
  753. const buffer = /** @type {Buffer} */ (attribute.vertexBuffer);
  754. const elementsPerVertex = attribute.componentsPerAttribute;
  755. //>>includeStart('debug', pragmas.debug);
  756. assert(buffer.sizeInBytes === array.byteLength, "Invalid buffer length");
  757. //>>includeEnd('debug');
  758. const ArrayConstructor = /** @type {TypedArrayConstructor} */ (
  759. array.constructor
  760. );
  761. const byteOffset =
  762. vertexOffset * elementsPerVertex * ArrayConstructor.BYTES_PER_ELEMENT;
  763. // Create a zero-copy ArrayView onto the specified range of the source array.
  764. const rangeArrayView = new ArrayConstructor(
  765. /** @type {ArrayBuffer} */ (array.buffer),
  766. array.byteOffset + byteOffset,
  767. vertexCount * elementsPerVertex,
  768. );
  769. buffer.copyFromArrayView(rangeArrayView, byteOffset);
  770. };
  771. /**
  772. * Copies into the index buffer from the given array, at a given range
  773. * specified as offset and count, in number of (uint) indices. Array
  774. * and index buffer must have the same length, which can be larger
  775. * than the specified range to update.
  776. * @param {TypedArray} array
  777. * @param {number} indexOffset
  778. * @param {number} indexCount
  779. */
  780. VertexArray.prototype.copyIndexFromRange = function (
  781. array,
  782. indexOffset,
  783. indexCount,
  784. ) {
  785. const buffer = /** @type {Buffer} */ (this._indexBuffer);
  786. //>>includeStart('debug', pragmas.debug);
  787. assert(buffer.sizeInBytes === array.byteLength, "Invalid buffer length");
  788. //>>includeEnd('debug');
  789. const ArrayConstructor = /** @type {TypedArrayConstructor} */ (
  790. array.constructor
  791. );
  792. const byteOffset = indexOffset * ArrayConstructor.BYTES_PER_ELEMENT;
  793. // Create a zero-copy ArrayView onto the specified range of the source array.
  794. const rangeArrayView = new ArrayConstructor(
  795. /** @type {ArrayBuffer} */ (array.buffer),
  796. array.byteOffset + byteOffset,
  797. indexCount,
  798. );
  799. buffer.copyFromArrayView(rangeArrayView, byteOffset);
  800. };
  801. VertexArray.prototype._bind = function () {
  802. if (defined(this._vao)) {
  803. this._context.glBindVertexArray(this._vao);
  804. if (this._context.instancedArrays) {
  805. setVertexAttribDivisor(this);
  806. }
  807. if (this._hasConstantAttributes) {
  808. setConstantAttributes(this, this._gl);
  809. }
  810. } else {
  811. bind(this._gl, this._attributes, this._indexBuffer);
  812. }
  813. };
  814. VertexArray.prototype._unBind = function () {
  815. if (defined(this._vao)) {
  816. this._context.glBindVertexArray(null);
  817. } else {
  818. const attributes = this._attributes;
  819. const gl = this._gl;
  820. for (let i = 0; i < attributes.length; ++i) {
  821. const attribute = attributes[i];
  822. if (attribute.enabled) {
  823. attribute.disableVertexAttribArray(gl);
  824. }
  825. }
  826. if (this._indexBuffer) {
  827. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  828. }
  829. }
  830. };
  831. VertexArray.prototype.isDestroyed = function () {
  832. return false;
  833. };
  834. VertexArray.prototype.destroy = function () {
  835. const attributes = this._attributes;
  836. for (let i = 0; i < attributes.length; ++i) {
  837. const vertexBuffer = attributes[i].vertexBuffer;
  838. if (
  839. defined(vertexBuffer) &&
  840. !vertexBuffer.isDestroyed() &&
  841. vertexBuffer.vertexArrayDestroyable
  842. ) {
  843. vertexBuffer.destroy();
  844. }
  845. }
  846. const indexBuffer = this._indexBuffer;
  847. if (
  848. defined(indexBuffer) &&
  849. !indexBuffer.isDestroyed() &&
  850. indexBuffer.vertexArrayDestroyable
  851. ) {
  852. indexBuffer.destroy();
  853. }
  854. if (defined(this._vao)) {
  855. this._context.glDeleteVertexArray(this._vao);
  856. }
  857. return destroyObject(this);
  858. };
  859. export default VertexArray;