BufferGeometry.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458
  1. import { Vector3 } from '../math/Vector3.js';
  2. import { Vector2 } from '../math/Vector2.js';
  3. import { Box3 } from '../math/Box3.js';
  4. import { EventDispatcher } from './EventDispatcher.js';
  5. import { BufferAttribute, Float32BufferAttribute, Uint16BufferAttribute, Uint32BufferAttribute } from './BufferAttribute.js';
  6. import { Sphere } from '../math/Sphere.js';
  7. import { Object3D } from './Object3D.js';
  8. import { Matrix4 } from '../math/Matrix4.js';
  9. import { Matrix3 } from '../math/Matrix3.js';
  10. import { generateUUID } from '../math/MathUtils.js';
  11. import { arrayNeedsUint32, warn, error } from '../utils.js';
  12. let _id = 0;
  13. const _m1 = /*@__PURE__*/ new Matrix4();
  14. const _obj = /*@__PURE__*/ new Object3D();
  15. const _offset = /*@__PURE__*/ new Vector3();
  16. const _box = /*@__PURE__*/ new Box3();
  17. const _boxMorphTargets = /*@__PURE__*/ new Box3();
  18. const _vector = /*@__PURE__*/ new Vector3();
  19. /**
  20. * A representation of mesh, line, or point geometry. Includes vertex
  21. * positions, face indices, normals, colors, UVs, and custom attributes
  22. * within buffers, reducing the cost of passing all this data to the GPU.
  23. *
  24. * ```js
  25. * const geometry = new THREE.BufferGeometry();
  26. * // create a simple square shape. We duplicate the top left and bottom right
  27. * // vertices because each vertex needs to appear once per triangle.
  28. * const vertices = new Float32Array( [
  29. * -1.0, -1.0, 1.0, // v0
  30. * 1.0, -1.0, 1.0, // v1
  31. * 1.0, 1.0, 1.0, // v2
  32. *
  33. * 1.0, 1.0, 1.0, // v3
  34. * -1.0, 1.0, 1.0, // v4
  35. * -1.0, -1.0, 1.0 // v5
  36. * ] );
  37. * // itemSize = 3 because there are 3 values (components) per vertex
  38. * geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
  39. * const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
  40. * const mesh = new THREE.Mesh( geometry, material );
  41. * ```
  42. *
  43. * @augments EventDispatcher
  44. */
  45. class BufferGeometry extends EventDispatcher {
  46. /**
  47. * Constructs a new geometry.
  48. */
  49. constructor() {
  50. super();
  51. /**
  52. * This flag can be used for type testing.
  53. *
  54. * @type {boolean}
  55. * @readonly
  56. * @default true
  57. */
  58. this.isBufferGeometry = true;
  59. /**
  60. * The ID of the geometry.
  61. *
  62. * @name BufferGeometry#id
  63. * @type {number}
  64. * @readonly
  65. */
  66. Object.defineProperty( this, 'id', { value: _id ++ } );
  67. /**
  68. * The UUID of the geometry.
  69. *
  70. * @type {string}
  71. * @readonly
  72. */
  73. this.uuid = generateUUID();
  74. /**
  75. * The name of the geometry.
  76. *
  77. * @type {string}
  78. */
  79. this.name = '';
  80. this.type = 'BufferGeometry';
  81. /**
  82. * Allows for vertices to be re-used across multiple triangles; this is
  83. * called using "indexed triangles". Each triangle is associated with the
  84. * indices of three vertices. This attribute therefore stores the index of
  85. * each vertex for each triangular face. If this attribute is not set, the
  86. * renderer assumes that each three contiguous positions represent a single triangle.
  87. *
  88. * @type {?BufferAttribute}
  89. * @default null
  90. */
  91. this.index = null;
  92. /**
  93. * A (storage) buffer attribute which was generated with a compute shader and
  94. * now defines indirect draw calls.
  95. *
  96. * Can only be used with {@link WebGPURenderer} and a WebGPU backend.
  97. *
  98. * @type {?BufferAttribute}
  99. * @default null
  100. */
  101. this.indirect = null;
  102. /**
  103. * The offset, in bytes, into the indirect drawing buffer where the value data begins. If an array is provided, multiple indirect draw calls will be made for each offset.
  104. *
  105. * Can only be used with {@link WebGPURenderer} and a WebGPU backend.
  106. *
  107. * @type {number|Array<number>}
  108. * @default 0
  109. */
  110. this.indirectOffset = 0;
  111. /**
  112. * This dictionary has as id the name of the attribute to be set and as value
  113. * the buffer attribute to set it to. Rather than accessing this property directly,
  114. * use `setAttribute()` and `getAttribute()` to access attributes of this geometry.
  115. *
  116. * @type {Object<string,(BufferAttribute|InterleavedBufferAttribute)>}
  117. */
  118. this.attributes = {};
  119. /**
  120. * This dictionary holds the morph targets of the geometry.
  121. *
  122. * Note: Once the geometry has been rendered, the morph attribute data cannot
  123. * be changed. You will have to call `dispose()`, and create a new geometry instance.
  124. *
  125. * @type {Object}
  126. */
  127. this.morphAttributes = {};
  128. /**
  129. * Used to control the morph target behavior; when set to `true`, the morph
  130. * target data is treated as relative offsets, rather than as absolute
  131. * positions/normals.
  132. *
  133. * @type {boolean}
  134. * @default false
  135. */
  136. this.morphTargetsRelative = false;
  137. /**
  138. * Split the geometry into groups, each of which will be rendered in a
  139. * separate draw call. This allows an array of materials to be used with the geometry.
  140. *
  141. * Use `addGroup()` and `clearGroups()` to edit groups, rather than modifying this array directly.
  142. *
  143. * Every vertex and index must belong to exactly one group — groups must not share vertices or
  144. * indices, and must not leave vertices or indices unused.
  145. *
  146. * @type {Array<Object>}
  147. */
  148. this.groups = [];
  149. /**
  150. * Bounding box for the geometry which can be calculated with `computeBoundingBox()`.
  151. *
  152. * @type {?Box3}
  153. * @default null
  154. */
  155. this.boundingBox = null;
  156. /**
  157. * Bounding sphere for the geometry which can be calculated with `computeBoundingSphere()`.
  158. *
  159. * @type {?Sphere}
  160. * @default null
  161. */
  162. this.boundingSphere = null;
  163. /**
  164. * Determines the part of the geometry to render. This should not be set directly,
  165. * instead use `setDrawRange()`.
  166. *
  167. * @type {{start:number,count:number}}
  168. */
  169. this.drawRange = { start: 0, count: Infinity };
  170. /**
  171. * An object that can be used to store custom data about the geometry.
  172. * It should not hold references to functions as these will not be cloned.
  173. *
  174. * @type {Object}
  175. */
  176. this.userData = {};
  177. }
  178. /**
  179. * Returns the index of this geometry.
  180. *
  181. * @return {?BufferAttribute} The index. Returns `null` if no index is defined.
  182. */
  183. getIndex() {
  184. return this.index;
  185. }
  186. /**
  187. * Sets the given index to this geometry.
  188. *
  189. * @param {Array<number>|BufferAttribute} index - The index to set.
  190. * @return {BufferGeometry} A reference to this instance.
  191. */
  192. setIndex( index ) {
  193. if ( Array.isArray( index ) ) {
  194. this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );
  195. } else {
  196. this.index = index;
  197. }
  198. return this;
  199. }
  200. /**
  201. * Sets the given indirect attribute to this geometry.
  202. *
  203. * @param {BufferAttribute} indirect - The attribute holding indirect draw calls.
  204. * @param {number|Array<number>} [indirectOffset=0] - The offset, in bytes, into the indirect drawing buffer where the value data begins. If an array is provided, multiple indirect draw calls will be made for each offset.
  205. * @return {BufferGeometry} A reference to this instance.
  206. */
  207. setIndirect( indirect, indirectOffset = 0 ) {
  208. this.indirect = indirect;
  209. this.indirectOffset = indirectOffset;
  210. return this;
  211. }
  212. /**
  213. * Returns the indirect attribute of this geometry.
  214. *
  215. * @return {?BufferAttribute} The indirect attribute. Returns `null` if no indirect attribute is defined.
  216. */
  217. getIndirect() {
  218. return this.indirect;
  219. }
  220. /**
  221. * Returns the buffer attribute for the given name.
  222. *
  223. * @param {string} name - The attribute name.
  224. * @return {BufferAttribute|InterleavedBufferAttribute|undefined} The buffer attribute.
  225. * Returns `undefined` if not attribute has been found.
  226. */
  227. getAttribute( name ) {
  228. return this.attributes[ name ];
  229. }
  230. /**
  231. * Sets the given attribute for the given name.
  232. *
  233. * @param {string} name - The attribute name.
  234. * @param {BufferAttribute|InterleavedBufferAttribute} attribute - The attribute to set.
  235. * @return {BufferGeometry} A reference to this instance.
  236. */
  237. setAttribute( name, attribute ) {
  238. this.attributes[ name ] = attribute;
  239. return this;
  240. }
  241. /**
  242. * Deletes the attribute for the given name.
  243. *
  244. * @param {string} name - The attribute name to delete.
  245. * @return {BufferGeometry} A reference to this instance.
  246. */
  247. deleteAttribute( name ) {
  248. delete this.attributes[ name ];
  249. return this;
  250. }
  251. /**
  252. * Returns `true` if this geometry has an attribute for the given name.
  253. *
  254. * @param {string} name - The attribute name.
  255. * @return {boolean} Whether this geometry has an attribute for the given name or not.
  256. */
  257. hasAttribute( name ) {
  258. return this.attributes[ name ] !== undefined;
  259. }
  260. /**
  261. * Adds a group to this geometry.
  262. *
  263. * @param {number} start - The first element in this draw call. That is the first
  264. * vertex for non-indexed geometry, otherwise the first triangle index.
  265. * @param {number} count - Specifies how many vertices (or indices) are part of this group.
  266. * @param {number} [materialIndex=0] - The material array index to use.
  267. */
  268. addGroup( start, count, materialIndex = 0 ) {
  269. this.groups.push( {
  270. start: start,
  271. count: count,
  272. materialIndex: materialIndex
  273. } );
  274. }
  275. /**
  276. * Clears all groups.
  277. */
  278. clearGroups() {
  279. this.groups = [];
  280. }
  281. /**
  282. * Sets the draw range for this geometry.
  283. *
  284. * @param {number} start - The first vertex for non-indexed geometry, otherwise the first triangle index.
  285. * @param {number} count - For non-indexed BufferGeometry, `count` is the number of vertices to render.
  286. * For indexed BufferGeometry, `count` is the number of indices to render.
  287. */
  288. setDrawRange( start, count ) {
  289. this.drawRange.start = start;
  290. this.drawRange.count = count;
  291. }
  292. /**
  293. * Applies the given 4x4 transformation matrix to the geometry.
  294. *
  295. * @param {Matrix4} matrix - The matrix to apply.
  296. * @return {BufferGeometry} A reference to this instance.
  297. */
  298. applyMatrix4( matrix ) {
  299. const position = this.attributes.position;
  300. if ( position !== undefined ) {
  301. position.applyMatrix4( matrix );
  302. position.needsUpdate = true;
  303. }
  304. const normal = this.attributes.normal;
  305. if ( normal !== undefined ) {
  306. const normalMatrix = new Matrix3().getNormalMatrix( matrix );
  307. normal.applyNormalMatrix( normalMatrix );
  308. normal.needsUpdate = true;
  309. }
  310. const tangent = this.attributes.tangent;
  311. if ( tangent !== undefined ) {
  312. tangent.transformDirection( matrix );
  313. tangent.needsUpdate = true;
  314. }
  315. if ( this.boundingBox !== null ) {
  316. this.computeBoundingBox();
  317. }
  318. if ( this.boundingSphere !== null ) {
  319. this.computeBoundingSphere();
  320. }
  321. return this;
  322. }
  323. /**
  324. * Applies the rotation represented by the Quaternion to the geometry.
  325. *
  326. * @param {Quaternion} q - The Quaternion to apply.
  327. * @return {BufferGeometry} A reference to this instance.
  328. */
  329. applyQuaternion( q ) {
  330. _m1.makeRotationFromQuaternion( q );
  331. this.applyMatrix4( _m1 );
  332. return this;
  333. }
  334. /**
  335. * Rotates the geometry about the X axis. This is typically done as a one time
  336. * operation, and not during a loop. Use {@link Object3D#rotation} for typical
  337. * real-time mesh rotation.
  338. *
  339. * @param {number} angle - The angle in radians.
  340. * @return {BufferGeometry} A reference to this instance.
  341. */
  342. rotateX( angle ) {
  343. // rotate geometry around world x-axis
  344. _m1.makeRotationX( angle );
  345. this.applyMatrix4( _m1 );
  346. return this;
  347. }
  348. /**
  349. * Rotates the geometry about the Y axis. This is typically done as a one time
  350. * operation, and not during a loop. Use {@link Object3D#rotation} for typical
  351. * real-time mesh rotation.
  352. *
  353. * @param {number} angle - The angle in radians.
  354. * @return {BufferGeometry} A reference to this instance.
  355. */
  356. rotateY( angle ) {
  357. // rotate geometry around world y-axis
  358. _m1.makeRotationY( angle );
  359. this.applyMatrix4( _m1 );
  360. return this;
  361. }
  362. /**
  363. * Rotates the geometry about the Z axis. This is typically done as a one time
  364. * operation, and not during a loop. Use {@link Object3D#rotation} for typical
  365. * real-time mesh rotation.
  366. *
  367. * @param {number} angle - The angle in radians.
  368. * @return {BufferGeometry} A reference to this instance.
  369. */
  370. rotateZ( angle ) {
  371. // rotate geometry around world z-axis
  372. _m1.makeRotationZ( angle );
  373. this.applyMatrix4( _m1 );
  374. return this;
  375. }
  376. /**
  377. * Translates the geometry. This is typically done as a one time
  378. * operation, and not during a loop. Use {@link Object3D#position} for typical
  379. * real-time mesh rotation.
  380. *
  381. * @param {number} x - The x offset.
  382. * @param {number} y - The y offset.
  383. * @param {number} z - The z offset.
  384. * @return {BufferGeometry} A reference to this instance.
  385. */
  386. translate( x, y, z ) {
  387. // translate geometry
  388. _m1.makeTranslation( x, y, z );
  389. this.applyMatrix4( _m1 );
  390. return this;
  391. }
  392. /**
  393. * Scales the geometry. This is typically done as a one time
  394. * operation, and not during a loop. Use {@link Object3D#scale} for typical
  395. * real-time mesh rotation.
  396. *
  397. * @param {number} x - The x scale.
  398. * @param {number} y - The y scale.
  399. * @param {number} z - The z scale.
  400. * @return {BufferGeometry} A reference to this instance.
  401. */
  402. scale( x, y, z ) {
  403. // scale geometry
  404. _m1.makeScale( x, y, z );
  405. this.applyMatrix4( _m1 );
  406. return this;
  407. }
  408. /**
  409. * Rotates the geometry to face a point in 3D space. This is typically done as a one time
  410. * operation, and not during a loop. Use {@link Object3D#lookAt} for typical
  411. * real-time mesh rotation.
  412. *
  413. * @param {Vector3} vector - The target point.
  414. * @return {BufferGeometry} A reference to this instance.
  415. */
  416. lookAt( vector ) {
  417. _obj.lookAt( vector );
  418. _obj.updateMatrix();
  419. this.applyMatrix4( _obj.matrix );
  420. return this;
  421. }
  422. /**
  423. * Center the geometry based on its bounding box.
  424. *
  425. * @return {BufferGeometry} A reference to this instance.
  426. */
  427. center() {
  428. this.computeBoundingBox();
  429. this.boundingBox.getCenter( _offset ).negate();
  430. this.translate( _offset.x, _offset.y, _offset.z );
  431. return this;
  432. }
  433. /**
  434. * Defines a geometry by creating a `position` attribute based on the given array of points. The array
  435. * can hold 2D or 3D vectors. When using two-dimensional data, the `z` coordinate for all vertices is
  436. * set to `0`.
  437. *
  438. * If the method is used with an existing `position` attribute, the vertex data are overwritten with the
  439. * data from the array. The length of the array must match the vertex count.
  440. *
  441. * @param {Array<Vector2>|Array<Vector3>} points - The points.
  442. * @return {BufferGeometry} A reference to this instance.
  443. */
  444. setFromPoints( points ) {
  445. const positionAttribute = this.getAttribute( 'position' );
  446. if ( positionAttribute === undefined ) {
  447. const position = [];
  448. for ( let i = 0, l = points.length; i < l; i ++ ) {
  449. const point = points[ i ];
  450. position.push( point.x, point.y, point.z || 0 );
  451. }
  452. this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
  453. } else {
  454. const l = Math.min( points.length, positionAttribute.count ); // make sure data do not exceed buffer size
  455. for ( let i = 0; i < l; i ++ ) {
  456. const point = points[ i ];
  457. positionAttribute.setXYZ( i, point.x, point.y, point.z || 0 );
  458. }
  459. if ( points.length > positionAttribute.count ) {
  460. warn( 'BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry.' );
  461. }
  462. positionAttribute.needsUpdate = true;
  463. }
  464. return this;
  465. }
  466. /**
  467. * Computes the bounding box of the geometry, and updates the `boundingBox` member.
  468. * The bounding box is not computed by the engine; it must be computed by your app.
  469. * You may need to recompute the bounding box if the geometry vertices are modified.
  470. */
  471. computeBoundingBox() {
  472. if ( this.boundingBox === null ) {
  473. this.boundingBox = new Box3();
  474. }
  475. const position = this.attributes.position;
  476. const morphAttributesPosition = this.morphAttributes.position;
  477. if ( position && position.isGLBufferAttribute ) {
  478. error( 'BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.', this );
  479. this.boundingBox.set(
  480. new Vector3( - Infinity, - Infinity, - Infinity ),
  481. new Vector3( + Infinity, + Infinity, + Infinity )
  482. );
  483. return;
  484. }
  485. if ( position !== undefined ) {
  486. this.boundingBox.setFromBufferAttribute( position );
  487. // process morph attributes if present
  488. if ( morphAttributesPosition ) {
  489. for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
  490. const morphAttribute = morphAttributesPosition[ i ];
  491. _box.setFromBufferAttribute( morphAttribute );
  492. if ( this.morphTargetsRelative ) {
  493. _vector.addVectors( this.boundingBox.min, _box.min );
  494. this.boundingBox.expandByPoint( _vector );
  495. _vector.addVectors( this.boundingBox.max, _box.max );
  496. this.boundingBox.expandByPoint( _vector );
  497. } else {
  498. this.boundingBox.expandByPoint( _box.min );
  499. this.boundingBox.expandByPoint( _box.max );
  500. }
  501. }
  502. }
  503. } else {
  504. this.boundingBox.makeEmpty();
  505. }
  506. if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
  507. error( 'BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
  508. }
  509. }
  510. /**
  511. * Computes the bounding sphere of the geometry, and updates the `boundingSphere` member.
  512. * The engine automatically computes the bounding sphere when it is needed, e.g., for ray casting or view frustum culling.
  513. * You may need to recompute the bounding sphere if the geometry vertices are modified.
  514. */
  515. computeBoundingSphere() {
  516. if ( this.boundingSphere === null ) {
  517. this.boundingSphere = new Sphere();
  518. }
  519. const position = this.attributes.position;
  520. const morphAttributesPosition = this.morphAttributes.position;
  521. if ( position && position.isGLBufferAttribute ) {
  522. error( 'BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.', this );
  523. this.boundingSphere.set( new Vector3(), Infinity );
  524. return;
  525. }
  526. if ( position ) {
  527. // first, find the center of the bounding sphere
  528. const center = this.boundingSphere.center;
  529. _box.setFromBufferAttribute( position );
  530. // process morph attributes if present
  531. if ( morphAttributesPosition ) {
  532. for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
  533. const morphAttribute = morphAttributesPosition[ i ];
  534. _boxMorphTargets.setFromBufferAttribute( morphAttribute );
  535. if ( this.morphTargetsRelative ) {
  536. _vector.addVectors( _box.min, _boxMorphTargets.min );
  537. _box.expandByPoint( _vector );
  538. _vector.addVectors( _box.max, _boxMorphTargets.max );
  539. _box.expandByPoint( _vector );
  540. } else {
  541. _box.expandByPoint( _boxMorphTargets.min );
  542. _box.expandByPoint( _boxMorphTargets.max );
  543. }
  544. }
  545. }
  546. _box.getCenter( center );
  547. // second, try to find a boundingSphere with a radius smaller than the
  548. // boundingSphere of the boundingBox: sqrt(3) smaller in the best case
  549. let maxRadiusSq = 0;
  550. for ( let i = 0, il = position.count; i < il; i ++ ) {
  551. _vector.fromBufferAttribute( position, i );
  552. maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
  553. }
  554. // process morph attributes if present
  555. if ( morphAttributesPosition ) {
  556. for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
  557. const morphAttribute = morphAttributesPosition[ i ];
  558. const morphTargetsRelative = this.morphTargetsRelative;
  559. for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) {
  560. _vector.fromBufferAttribute( morphAttribute, j );
  561. if ( morphTargetsRelative ) {
  562. _offset.fromBufferAttribute( position, j );
  563. _vector.add( _offset );
  564. }
  565. maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
  566. }
  567. }
  568. }
  569. this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
  570. if ( isNaN( this.boundingSphere.radius ) ) {
  571. error( 'BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );
  572. }
  573. }
  574. }
  575. /**
  576. * Calculates and adds a tangent attribute to this geometry.
  577. *
  578. * The computation is only supported for indexed geometries and if position, normal, and uv attributes
  579. * are defined. When using a tangent space normal map, prefer the MikkTSpace algorithm provided by
  580. * {@link BufferGeometryUtils#computeMikkTSpaceTangents} instead.
  581. */
  582. computeTangents() {
  583. const index = this.index;
  584. const attributes = this.attributes;
  585. // based on http://www.terathon.com/code/tangent.html
  586. // (per vertex tangents)
  587. if ( index === null ||
  588. attributes.position === undefined ||
  589. attributes.normal === undefined ||
  590. attributes.uv === undefined ) {
  591. error( 'BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' );
  592. return;
  593. }
  594. const positionAttribute = attributes.position;
  595. const normalAttribute = attributes.normal;
  596. const uvAttribute = attributes.uv;
  597. if ( this.hasAttribute( 'tangent' ) === false ) {
  598. this.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * positionAttribute.count ), 4 ) );
  599. }
  600. const tangentAttribute = this.getAttribute( 'tangent' );
  601. const tan1 = [], tan2 = [];
  602. for ( let i = 0; i < positionAttribute.count; i ++ ) {
  603. tan1[ i ] = new Vector3();
  604. tan2[ i ] = new Vector3();
  605. }
  606. const vA = new Vector3(),
  607. vB = new Vector3(),
  608. vC = new Vector3(),
  609. uvA = new Vector2(),
  610. uvB = new Vector2(),
  611. uvC = new Vector2(),
  612. sdir = new Vector3(),
  613. tdir = new Vector3();
  614. function handleTriangle( a, b, c ) {
  615. vA.fromBufferAttribute( positionAttribute, a );
  616. vB.fromBufferAttribute( positionAttribute, b );
  617. vC.fromBufferAttribute( positionAttribute, c );
  618. uvA.fromBufferAttribute( uvAttribute, a );
  619. uvB.fromBufferAttribute( uvAttribute, b );
  620. uvC.fromBufferAttribute( uvAttribute, c );
  621. vB.sub( vA );
  622. vC.sub( vA );
  623. uvB.sub( uvA );
  624. uvC.sub( uvA );
  625. const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y );
  626. // silently ignore degenerate uv triangles having coincident or colinear vertices
  627. if ( ! isFinite( r ) ) return;
  628. sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r );
  629. tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r );
  630. tan1[ a ].add( sdir );
  631. tan1[ b ].add( sdir );
  632. tan1[ c ].add( sdir );
  633. tan2[ a ].add( tdir );
  634. tan2[ b ].add( tdir );
  635. tan2[ c ].add( tdir );
  636. }
  637. let groups = this.groups;
  638. if ( groups.length === 0 ) {
  639. groups = [ {
  640. start: 0,
  641. count: index.count
  642. } ];
  643. }
  644. for ( let i = 0, il = groups.length; i < il; ++ i ) {
  645. const group = groups[ i ];
  646. const start = group.start;
  647. const count = group.count;
  648. for ( let j = start, jl = start + count; j < jl; j += 3 ) {
  649. handleTriangle(
  650. index.getX( j + 0 ),
  651. index.getX( j + 1 ),
  652. index.getX( j + 2 )
  653. );
  654. }
  655. }
  656. const tmp = new Vector3(), tmp2 = new Vector3();
  657. const n = new Vector3(), n2 = new Vector3();
  658. function handleVertex( v ) {
  659. n.fromBufferAttribute( normalAttribute, v );
  660. n2.copy( n );
  661. const t = tan1[ v ];
  662. // Gram-Schmidt orthogonalize
  663. tmp.copy( t );
  664. tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
  665. // Calculate handedness
  666. tmp2.crossVectors( n2, t );
  667. const test = tmp2.dot( tan2[ v ] );
  668. const w = ( test < 0.0 ) ? - 1.0 : 1.0;
  669. tangentAttribute.setXYZW( v, tmp.x, tmp.y, tmp.z, w );
  670. }
  671. for ( let i = 0, il = groups.length; i < il; ++ i ) {
  672. const group = groups[ i ];
  673. const start = group.start;
  674. const count = group.count;
  675. for ( let j = start, jl = start + count; j < jl; j += 3 ) {
  676. handleVertex( index.getX( j + 0 ) );
  677. handleVertex( index.getX( j + 1 ) );
  678. handleVertex( index.getX( j + 2 ) );
  679. }
  680. }
  681. }
  682. /**
  683. * Computes vertex normals for the given vertex data. For indexed geometries, the method sets
  684. * each vertex normal to be the average of the face normals of the faces that share that vertex.
  685. * For non-indexed geometries, vertices are not shared, and the method sets each vertex normal
  686. * to be the same as the face normal.
  687. */
  688. computeVertexNormals() {
  689. const index = this.index;
  690. const positionAttribute = this.getAttribute( 'position' );
  691. if ( positionAttribute !== undefined ) {
  692. let normalAttribute = this.getAttribute( 'normal' );
  693. if ( normalAttribute === undefined ) {
  694. normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 );
  695. this.setAttribute( 'normal', normalAttribute );
  696. } else {
  697. // reset existing normals to zero
  698. for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) {
  699. normalAttribute.setXYZ( i, 0, 0, 0 );
  700. }
  701. }
  702. const pA = new Vector3(), pB = new Vector3(), pC = new Vector3();
  703. const nA = new Vector3(), nB = new Vector3(), nC = new Vector3();
  704. const cb = new Vector3(), ab = new Vector3();
  705. // indexed elements
  706. if ( index ) {
  707. for ( let i = 0, il = index.count; i < il; i += 3 ) {
  708. const vA = index.getX( i + 0 );
  709. const vB = index.getX( i + 1 );
  710. const vC = index.getX( i + 2 );
  711. pA.fromBufferAttribute( positionAttribute, vA );
  712. pB.fromBufferAttribute( positionAttribute, vB );
  713. pC.fromBufferAttribute( positionAttribute, vC );
  714. cb.subVectors( pC, pB );
  715. ab.subVectors( pA, pB );
  716. cb.cross( ab );
  717. nA.fromBufferAttribute( normalAttribute, vA );
  718. nB.fromBufferAttribute( normalAttribute, vB );
  719. nC.fromBufferAttribute( normalAttribute, vC );
  720. nA.add( cb );
  721. nB.add( cb );
  722. nC.add( cb );
  723. normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z );
  724. normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z );
  725. normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z );
  726. }
  727. } else {
  728. // non-indexed elements (unconnected triangle soup)
  729. for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) {
  730. pA.fromBufferAttribute( positionAttribute, i + 0 );
  731. pB.fromBufferAttribute( positionAttribute, i + 1 );
  732. pC.fromBufferAttribute( positionAttribute, i + 2 );
  733. cb.subVectors( pC, pB );
  734. ab.subVectors( pA, pB );
  735. cb.cross( ab );
  736. normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z );
  737. normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z );
  738. normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z );
  739. }
  740. }
  741. this.normalizeNormals();
  742. normalAttribute.needsUpdate = true;
  743. }
  744. }
  745. /**
  746. * Ensures every normal vector in a geometry will have a magnitude of `1`. This will
  747. * correct lighting on the geometry surfaces.
  748. */
  749. normalizeNormals() {
  750. const normals = this.attributes.normal;
  751. for ( let i = 0, il = normals.count; i < il; i ++ ) {
  752. _vector.fromBufferAttribute( normals, i );
  753. _vector.normalize();
  754. normals.setXYZ( i, _vector.x, _vector.y, _vector.z );
  755. }
  756. }
  757. /**
  758. * Return a new non-index version of this indexed geometry. If the geometry
  759. * is already non-indexed, the method is a NOOP.
  760. *
  761. * @return {BufferGeometry} The non-indexed version of this indexed geometry.
  762. */
  763. toNonIndexed() {
  764. function convertBufferAttribute( attribute, indices ) {
  765. const array = attribute.array;
  766. const itemSize = attribute.itemSize;
  767. const normalized = attribute.normalized;
  768. const array2 = new array.constructor( indices.length * itemSize );
  769. let index = 0, index2 = 0;
  770. for ( let i = 0, l = indices.length; i < l; i ++ ) {
  771. if ( attribute.isInterleavedBufferAttribute ) {
  772. index = indices[ i ] * attribute.data.stride + attribute.offset;
  773. } else {
  774. index = indices[ i ] * itemSize;
  775. }
  776. for ( let j = 0; j < itemSize; j ++ ) {
  777. array2[ index2 ++ ] = array[ index ++ ];
  778. }
  779. }
  780. return new BufferAttribute( array2, itemSize, normalized );
  781. }
  782. //
  783. if ( this.index === null ) {
  784. warn( 'BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' );
  785. return this;
  786. }
  787. const geometry2 = new BufferGeometry();
  788. const indices = this.index.array;
  789. const attributes = this.attributes;
  790. // attributes
  791. for ( const name in attributes ) {
  792. const attribute = attributes[ name ];
  793. const newAttribute = convertBufferAttribute( attribute, indices );
  794. geometry2.setAttribute( name, newAttribute );
  795. }
  796. // morph attributes
  797. const morphAttributes = this.morphAttributes;
  798. for ( const name in morphAttributes ) {
  799. const morphArray = [];
  800. const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
  801. for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {
  802. const attribute = morphAttribute[ i ];
  803. const newAttribute = convertBufferAttribute( attribute, indices );
  804. morphArray.push( newAttribute );
  805. }
  806. geometry2.morphAttributes[ name ] = morphArray;
  807. }
  808. geometry2.morphTargetsRelative = this.morphTargetsRelative;
  809. // groups
  810. const groups = this.groups;
  811. for ( let i = 0, l = groups.length; i < l; i ++ ) {
  812. const group = groups[ i ];
  813. geometry2.addGroup( group.start, group.count, group.materialIndex );
  814. }
  815. return geometry2;
  816. }
  817. /**
  818. * Serializes the geometry into JSON.
  819. *
  820. * @return {Object} A JSON object representing the serialized geometry.
  821. */
  822. toJSON() {
  823. const data = {
  824. metadata: {
  825. version: 4.7,
  826. type: 'BufferGeometry',
  827. generator: 'BufferGeometry.toJSON'
  828. }
  829. };
  830. // standard BufferGeometry serialization
  831. data.uuid = this.uuid;
  832. data.type = this.type;
  833. if ( this.name !== '' ) data.name = this.name;
  834. if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;
  835. if ( this.parameters !== undefined ) {
  836. const parameters = this.parameters;
  837. for ( const key in parameters ) {
  838. if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
  839. }
  840. return data;
  841. }
  842. // for simplicity the code assumes attributes are not shared across geometries, see #15811
  843. data.data = { attributes: {} };
  844. const index = this.index;
  845. if ( index !== null ) {
  846. data.data.index = {
  847. type: index.array.constructor.name,
  848. array: Array.prototype.slice.call( index.array )
  849. };
  850. }
  851. const attributes = this.attributes;
  852. for ( const key in attributes ) {
  853. const attribute = attributes[ key ];
  854. data.data.attributes[ key ] = attribute.toJSON( data.data );
  855. }
  856. const morphAttributes = {};
  857. let hasMorphAttributes = false;
  858. for ( const key in this.morphAttributes ) {
  859. const attributeArray = this.morphAttributes[ key ];
  860. const array = [];
  861. for ( let i = 0, il = attributeArray.length; i < il; i ++ ) {
  862. const attribute = attributeArray[ i ];
  863. array.push( attribute.toJSON( data.data ) );
  864. }
  865. if ( array.length > 0 ) {
  866. morphAttributes[ key ] = array;
  867. hasMorphAttributes = true;
  868. }
  869. }
  870. if ( hasMorphAttributes ) {
  871. data.data.morphAttributes = morphAttributes;
  872. data.data.morphTargetsRelative = this.morphTargetsRelative;
  873. }
  874. const groups = this.groups;
  875. if ( groups.length > 0 ) {
  876. data.data.groups = JSON.parse( JSON.stringify( groups ) );
  877. }
  878. const boundingSphere = this.boundingSphere;
  879. if ( boundingSphere !== null ) {
  880. data.data.boundingSphere = boundingSphere.toJSON();
  881. }
  882. return data;
  883. }
  884. /**
  885. * Returns a new geometry with copied values from this instance.
  886. *
  887. * @return {BufferGeometry} A clone of this instance.
  888. */
  889. clone() {
  890. return new this.constructor().copy( this );
  891. }
  892. /**
  893. * Copies the values of the given geometry to this instance.
  894. *
  895. * @param {BufferGeometry} source - The geometry to copy.
  896. * @return {BufferGeometry} A reference to this instance.
  897. */
  898. copy( source ) {
  899. // reset
  900. this.index = null;
  901. this.attributes = {};
  902. this.morphAttributes = {};
  903. this.groups = [];
  904. this.boundingBox = null;
  905. this.boundingSphere = null;
  906. // used for storing cloned, shared data
  907. const data = {};
  908. // name
  909. this.name = source.name;
  910. // index
  911. const index = source.index;
  912. if ( index !== null ) {
  913. this.setIndex( index.clone() );
  914. }
  915. // attributes
  916. const attributes = source.attributes;
  917. for ( const name in attributes ) {
  918. const attribute = attributes[ name ];
  919. this.setAttribute( name, attribute.clone( data ) );
  920. }
  921. // morph attributes
  922. const morphAttributes = source.morphAttributes;
  923. for ( const name in morphAttributes ) {
  924. const array = [];
  925. const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
  926. for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) {
  927. array.push( morphAttribute[ i ].clone( data ) );
  928. }
  929. this.morphAttributes[ name ] = array;
  930. }
  931. this.morphTargetsRelative = source.morphTargetsRelative;
  932. // groups
  933. const groups = source.groups;
  934. for ( let i = 0, l = groups.length; i < l; i ++ ) {
  935. const group = groups[ i ];
  936. this.addGroup( group.start, group.count, group.materialIndex );
  937. }
  938. // bounding box
  939. const boundingBox = source.boundingBox;
  940. if ( boundingBox !== null ) {
  941. this.boundingBox = boundingBox.clone();
  942. }
  943. // bounding sphere
  944. const boundingSphere = source.boundingSphere;
  945. if ( boundingSphere !== null ) {
  946. this.boundingSphere = boundingSphere.clone();
  947. }
  948. // draw range
  949. this.drawRange.start = source.drawRange.start;
  950. this.drawRange.count = source.drawRange.count;
  951. // user data
  952. this.userData = source.userData;
  953. return this;
  954. }
  955. /**
  956. * Frees the GPU-related resources allocated by this instance. Call this
  957. * method whenever this instance is no longer used in your app.
  958. *
  959. * @fires BufferGeometry#dispose
  960. */
  961. dispose() {
  962. this.dispatchEvent( { type: 'dispose' } );
  963. }
  964. }
  965. export { BufferGeometry };
粤ICP备19079148号