BufferGeometryUtils.js 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  1. import {
  2. BufferAttribute,
  3. BufferGeometry,
  4. Float32BufferAttribute,
  5. InstancedBufferAttribute,
  6. InterleavedBuffer,
  7. InterleavedBufferAttribute,
  8. TriangleFanDrawMode,
  9. TriangleStripDrawMode,
  10. TrianglesDrawMode,
  11. Vector3,
  12. } from 'three';
  13. function computeTangents() {
  14. throw new Error( 'BufferGeometryUtils: computeTangents renamed to computeMikkTSpaceTangents.' );
  15. }
  16. function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
  17. if ( ! MikkTSpace || ! MikkTSpace.isReady ) {
  18. throw new Error( 'BufferGeometryUtils: Initialized MikkTSpace library required.' );
  19. }
  20. if ( ! geometry.hasAttribute( 'position' ) || ! geometry.hasAttribute( 'normal' ) || ! geometry.hasAttribute( 'uv' ) ) {
  21. throw new Error( 'BufferGeometryUtils: Tangents require "position", "normal", and "uv" attributes.' );
  22. }
  23. function getAttributeArray( attribute ) {
  24. if ( attribute.normalized || attribute.isInterleavedBufferAttribute ) {
  25. const dstArray = new Float32Array( attribute.getCount() * attribute.itemSize );
  26. for ( let i = 0, j = 0; i < attribute.getCount(); i ++ ) {
  27. dstArray[ j ++ ] = attribute.getX( i );
  28. dstArray[ j ++ ] = attribute.getY( i );
  29. if ( attribute.itemSize > 2 ) {
  30. dstArray[ j ++ ] = attribute.getZ( i );
  31. }
  32. }
  33. return dstArray;
  34. }
  35. if ( attribute.array instanceof Float32Array ) {
  36. return attribute.array;
  37. }
  38. return new Float32Array( attribute.array );
  39. }
  40. // MikkTSpace algorithm requires non-indexed input.
  41. const _geometry = geometry.index ? geometry.toNonIndexed() : geometry;
  42. // Compute vertex tangents.
  43. const tangents = MikkTSpace.generateTangents(
  44. getAttributeArray( _geometry.attributes.position ),
  45. getAttributeArray( _geometry.attributes.normal ),
  46. getAttributeArray( _geometry.attributes.uv )
  47. );
  48. // Texture coordinate convention of glTF differs from the apparent
  49. // default of the MikkTSpace library; .w component must be flipped.
  50. if ( negateSign ) {
  51. for ( let i = 3; i < tangents.length; i += 4 ) {
  52. tangents[ i ] *= - 1;
  53. }
  54. }
  55. //
  56. _geometry.setAttribute( 'tangent', new BufferAttribute( tangents, 4 ) );
  57. if ( geometry !== _geometry ) {
  58. geometry.copy( _geometry );
  59. }
  60. return geometry;
  61. }
  62. /**
  63. * @param {Array<BufferGeometry>} geometries
  64. * @param {Boolean} useGroups
  65. * @return {BufferGeometry}
  66. */
  67. function mergeBufferGeometries( geometries, useGroups = false ) {
  68. const isIndexed = geometries[ 0 ].index !== null;
  69. const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) );
  70. const morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) );
  71. const attributes = {};
  72. const morphAttributes = {};
  73. const morphTargetsRelative = geometries[ 0 ].morphTargetsRelative;
  74. const mergedGeometry = new BufferGeometry();
  75. let offset = 0;
  76. for ( let i = 0; i < geometries.length; ++ i ) {
  77. const geometry = geometries[ i ];
  78. let attributesCount = 0;
  79. // ensure that all geometries are indexed, or none
  80. if ( isIndexed !== ( geometry.index !== null ) ) {
  81. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' );
  82. return null;
  83. }
  84. // gather attributes, exit early if they're different
  85. for ( const name in geometry.attributes ) {
  86. if ( ! attributesUsed.has( name ) ) {
  87. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' );
  88. return null;
  89. }
  90. if ( attributes[ name ] === undefined ) attributes[ name ] = [];
  91. attributes[ name ].push( geometry.attributes[ name ] );
  92. attributesCount ++;
  93. }
  94. // ensure geometries have the same number of attributes
  95. if ( attributesCount !== attributesUsed.size ) {
  96. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' );
  97. return null;
  98. }
  99. // gather morph attributes, exit early if they're different
  100. if ( morphTargetsRelative !== geometry.morphTargetsRelative ) {
  101. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' );
  102. return null;
  103. }
  104. for ( const name in geometry.morphAttributes ) {
  105. if ( ! morphAttributesUsed.has( name ) ) {
  106. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.' );
  107. return null;
  108. }
  109. if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = [];
  110. morphAttributes[ name ].push( geometry.morphAttributes[ name ] );
  111. }
  112. // gather .userData
  113. mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || [];
  114. mergedGeometry.userData.mergedUserData.push( geometry.userData );
  115. if ( useGroups ) {
  116. let count;
  117. if ( isIndexed ) {
  118. count = geometry.index.count;
  119. } else if ( geometry.attributes.position !== undefined ) {
  120. count = geometry.attributes.position.count;
  121. } else {
  122. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' );
  123. return null;
  124. }
  125. mergedGeometry.addGroup( offset, count, i );
  126. offset += count;
  127. }
  128. }
  129. // merge indices
  130. if ( isIndexed ) {
  131. let indexOffset = 0;
  132. const mergedIndex = [];
  133. for ( let i = 0; i < geometries.length; ++ i ) {
  134. const index = geometries[ i ].index;
  135. for ( let j = 0; j < index.count; ++ j ) {
  136. mergedIndex.push( index.getX( j ) + indexOffset );
  137. }
  138. indexOffset += geometries[ i ].attributes.position.count;
  139. }
  140. mergedGeometry.setIndex( mergedIndex );
  141. }
  142. // merge attributes
  143. for ( const name in attributes ) {
  144. const mergedAttribute = mergeBufferAttributes( attributes[ name ] );
  145. if ( ! mergedAttribute ) {
  146. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' attribute.' );
  147. return null;
  148. }
  149. mergedGeometry.setAttribute( name, mergedAttribute );
  150. }
  151. // merge morph attributes
  152. for ( const name in morphAttributes ) {
  153. const numMorphTargets = morphAttributes[ name ][ 0 ].length;
  154. if ( numMorphTargets === 0 ) break;
  155. mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {};
  156. mergedGeometry.morphAttributes[ name ] = [];
  157. for ( let i = 0; i < numMorphTargets; ++ i ) {
  158. const morphAttributesToMerge = [];
  159. for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) {
  160. morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] );
  161. }
  162. const mergedMorphAttribute = mergeBufferAttributes( morphAttributesToMerge );
  163. if ( ! mergedMorphAttribute ) {
  164. console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' morphAttribute.' );
  165. return null;
  166. }
  167. mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute );
  168. }
  169. }
  170. return mergedGeometry;
  171. }
  172. /**
  173. * @param {Array<BufferAttribute>} attributes
  174. * @return {BufferAttribute}
  175. */
  176. function mergeBufferAttributes( attributes ) {
  177. let TypedArray;
  178. let itemSize;
  179. let normalized;
  180. let arrayLength = 0;
  181. for ( let i = 0; i < attributes.length; ++ i ) {
  182. const attribute = attributes[ i ];
  183. if ( attribute.isInterleavedBufferAttribute ) {
  184. console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. InterleavedBufferAttributes are not supported.' );
  185. return null;
  186. }
  187. if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
  188. if ( TypedArray !== attribute.array.constructor ) {
  189. console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.' );
  190. return null;
  191. }
  192. if ( itemSize === undefined ) itemSize = attribute.itemSize;
  193. if ( itemSize !== attribute.itemSize ) {
  194. console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.' );
  195. return null;
  196. }
  197. if ( normalized === undefined ) normalized = attribute.normalized;
  198. if ( normalized !== attribute.normalized ) {
  199. console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.' );
  200. return null;
  201. }
  202. arrayLength += attribute.array.length;
  203. }
  204. const array = new TypedArray( arrayLength );
  205. let offset = 0;
  206. for ( let i = 0; i < attributes.length; ++ i ) {
  207. array.set( attributes[ i ].array, offset );
  208. offset += attributes[ i ].array.length;
  209. }
  210. return new BufferAttribute( array, itemSize, normalized );
  211. }
  212. /**
  213. * @param {Array<BufferAttribute>} attributes
  214. * @return {Array<InterleavedBufferAttribute>}
  215. */
  216. function interleaveAttributes( attributes ) {
  217. // Interleaves the provided attributes into an InterleavedBuffer and returns
  218. // a set of InterleavedBufferAttributes for each attribute
  219. let TypedArray;
  220. let arrayLength = 0;
  221. let stride = 0;
  222. // calculate the length and type of the interleavedBuffer
  223. for ( let i = 0, l = attributes.length; i < l; ++ i ) {
  224. const attribute = attributes[ i ];
  225. if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
  226. if ( TypedArray !== attribute.array.constructor ) {
  227. console.error( 'AttributeBuffers of different types cannot be interleaved' );
  228. return null;
  229. }
  230. arrayLength += attribute.array.length;
  231. stride += attribute.itemSize;
  232. }
  233. // Create the set of buffer attributes
  234. const interleavedBuffer = new InterleavedBuffer( new TypedArray( arrayLength ), stride );
  235. let offset = 0;
  236. const res = [];
  237. const getters = [ 'getX', 'getY', 'getZ', 'getW' ];
  238. const setters = [ 'setX', 'setY', 'setZ', 'setW' ];
  239. for ( let j = 0, l = attributes.length; j < l; j ++ ) {
  240. const attribute = attributes[ j ];
  241. const itemSize = attribute.itemSize;
  242. const count = attribute.count;
  243. const iba = new InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, attribute.normalized );
  244. res.push( iba );
  245. offset += itemSize;
  246. // Move the data for each attribute into the new interleavedBuffer
  247. // at the appropriate offset
  248. for ( let c = 0; c < count; c ++ ) {
  249. for ( let k = 0; k < itemSize; k ++ ) {
  250. iba[ setters[ k ] ]( c, attribute[ getters[ k ] ]( c ) );
  251. }
  252. }
  253. }
  254. return res;
  255. }
  256. // returns a new, non-interleaved version of the provided attribute
  257. export function deinterleaveAttribute( attribute ) {
  258. const cons = attribute.data.array.constructor;
  259. const count = attribute.count;
  260. const itemSize = attribute.itemSize;
  261. const normalized = attribute.normalized;
  262. const array = new cons( count * itemSize );
  263. let newAttribute;
  264. if ( attribute.isInstancedInterleavedBufferAttribute ) {
  265. newAttribute = new InstancedBufferAttribute( array, itemSize, normalized, attribute.meshPerAttribute );
  266. } else {
  267. newAttribute = new BufferAttribute( array, itemSize, normalized );
  268. }
  269. for ( let i = 0; i < count; i ++ ) {
  270. newAttribute.setX( i, attribute.getX( i ) );
  271. if ( itemSize >= 2 ) {
  272. newAttribute.setY( i, attribute.getY( i ) );
  273. }
  274. if ( itemSize >= 3 ) {
  275. newAttribute.setZ( i, attribute.getZ( i ) );
  276. }
  277. if ( itemSize >= 4 ) {
  278. newAttribute.setW( i, attribute.getW( i ) );
  279. }
  280. }
  281. return newAttribute;
  282. }
  283. // deinterleaves all attributes on the geometry
  284. export function deinterleaveGeometry( geometry ) {
  285. const attributes = geometry.attributes;
  286. const morphTargets = geometry.morphTargets;
  287. const attrMap = new Map();
  288. for ( const key in attributes ) {
  289. const attr = attributes[ key ];
  290. if ( attr.isInterleavedBufferAttribute ) {
  291. if ( ! attrMap.has( attr ) ) {
  292. attrMap.set( attr, deinterleaveAttribute( attr ) );
  293. }
  294. attributes[ key ] = attrMap.get( attr );
  295. }
  296. }
  297. for ( const key in morphTargets ) {
  298. const attr = morphTargets[ key ];
  299. if ( attr.isInterleavedBufferAttribute ) {
  300. if ( ! attrMap.has( attr ) ) {
  301. attrMap.set( attr, deinterleaveAttribute( attr ) );
  302. }
  303. morphTargets[ key ] = attrMap.get( attr );
  304. }
  305. }
  306. }
  307. /**
  308. * @param {Array<BufferGeometry>} geometry
  309. * @return {number}
  310. */
  311. function estimateBytesUsed( geometry ) {
  312. // Return the estimated memory used by this geometry in bytes
  313. // Calculate using itemSize, count, and BYTES_PER_ELEMENT to account
  314. // for InterleavedBufferAttributes.
  315. let mem = 0;
  316. for ( const name in geometry.attributes ) {
  317. const attr = geometry.getAttribute( name );
  318. mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT;
  319. }
  320. const indices = geometry.getIndex();
  321. mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0;
  322. return mem;
  323. }
  324. /**
  325. * @param {BufferGeometry} geometry
  326. * @param {number} tolerance
  327. * @return {BufferGeometry}
  328. */
  329. function mergeVertices( geometry, tolerance = 1e-4 ) {
  330. tolerance = Math.max( tolerance, Number.EPSILON );
  331. // Generate an index buffer if the geometry doesn't have one, or optimize it
  332. // if it's already available.
  333. const hashToIndex = {};
  334. const indices = geometry.getIndex();
  335. const positions = geometry.getAttribute( 'position' );
  336. const vertexCount = indices ? indices.count : positions.count;
  337. // next value for triangle indices
  338. let nextIndex = 0;
  339. // attributes and new attribute arrays
  340. const attributeNames = Object.keys( geometry.attributes );
  341. const tmpAttributes = {};
  342. const tmpMorphAttributes = {};
  343. const newIndices = [];
  344. const getters = [ 'getX', 'getY', 'getZ', 'getW' ];
  345. const setters = [ 'setX', 'setY', 'setZ', 'setW' ];
  346. // Initialize the arrays, allocating space conservatively. Extra
  347. // space will be trimmed in the last step.
  348. for ( let i = 0, l = attributeNames.length; i < l; i ++ ) {
  349. const name = attributeNames[ i ];
  350. const attr = geometry.attributes[ name ];
  351. tmpAttributes[ name ] = new BufferAttribute(
  352. new attr.array.constructor( attr.count * attr.itemSize ),
  353. attr.itemSize,
  354. attr.normalized
  355. );
  356. const morphAttr = geometry.morphAttributes[ name ];
  357. if ( morphAttr ) {
  358. tmpMorphAttributes[ name ] = new BufferAttribute(
  359. new morphAttr.array.constructor( morphAttr.count * morphAttr.itemSize ),
  360. morphAttr.itemSize,
  361. morphAttr.normalized
  362. );
  363. }
  364. }
  365. // convert the error tolerance to an amount of decimal places to truncate to
  366. const decimalShift = Math.log10( 1 / tolerance );
  367. const shiftMultiplier = Math.pow( 10, decimalShift );
  368. for ( let i = 0; i < vertexCount; i ++ ) {
  369. const index = indices ? indices.getX( i ) : i;
  370. // Generate a hash for the vertex attributes at the current index 'i'
  371. let hash = '';
  372. for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
  373. const name = attributeNames[ j ];
  374. const attribute = geometry.getAttribute( name );
  375. const itemSize = attribute.itemSize;
  376. for ( let k = 0; k < itemSize; k ++ ) {
  377. // double tilde truncates the decimal value
  378. hash += `${ ~ ~ ( attribute[ getters[ k ] ]( index ) * shiftMultiplier ) },`;
  379. }
  380. }
  381. // Add another reference to the vertex if it's already
  382. // used by another index
  383. if ( hash in hashToIndex ) {
  384. newIndices.push( hashToIndex[ hash ] );
  385. } else {
  386. // copy data to the new index in the temporary attributes
  387. for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
  388. const name = attributeNames[ j ];
  389. const attribute = geometry.getAttribute( name );
  390. const morphAttr = geometry.morphAttributes[ name ];
  391. const itemSize = attribute.itemSize;
  392. const newarray = tmpAttributes[ name ];
  393. const newMorphArrays = tmpMorphAttributes[ name ];
  394. for ( let k = 0; k < itemSize; k ++ ) {
  395. const getterFunc = getters[ k ];
  396. const setterFunc = setters[ k ];
  397. newarray[ setterFunc ]( nextIndex, attribute[ getterFunc ]( index ) );
  398. if ( morphAttr ) {
  399. for ( let m = 0, ml = morphAttr.length; m < ml; m ++ ) {
  400. newMorphArrays[ m ][ setterFunc ]( nextIndex, morphAttr[ m ][ getterFunc ]( index ) );
  401. }
  402. }
  403. }
  404. }
  405. hashToIndex[ hash ] = nextIndex;
  406. newIndices.push( nextIndex );
  407. nextIndex ++;
  408. }
  409. }
  410. // generate result BufferGeometry
  411. const result = geometry.clone();
  412. for ( const name in geometry.attributes ) {
  413. const tmpAttribute = tmpAttributes[ name ];
  414. result.setAttribute( name, new BufferAttribute(
  415. tmpAttribute.array.slice( 0, nextIndex * tmpAttribute.itemSize ),
  416. tmpAttribute.itemSize,
  417. tmpAttribute.normalized,
  418. ) );
  419. if ( ! ( name in tmpMorphAttributes ) ) continue;
  420. for ( let j = 0; j < tmpMorphAttributes[ name ].length; j ++ ) {
  421. const tmpMorphAttribute = tmpMorphAttributes[ name ][ j ];
  422. result.morphAttributes[ name ][ j ] = new BufferAttribute(
  423. tmpMorphAttribute.array.slice( 0, nextIndex * tmpMorphAttribute.itemSize ),
  424. tmpMorphAttribute.itemSize,
  425. tmpMorphAttribute.normalized,
  426. );
  427. }
  428. }
  429. // indices
  430. result.setIndex( newIndices );
  431. return result;
  432. }
  433. /**
  434. * @param {BufferGeometry} geometry
  435. * @param {number} drawMode
  436. * @return {BufferGeometry}
  437. */
  438. function toTrianglesDrawMode( geometry, drawMode ) {
  439. if ( drawMode === TrianglesDrawMode ) {
  440. console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' );
  441. return geometry;
  442. }
  443. if ( drawMode === TriangleFanDrawMode || drawMode === TriangleStripDrawMode ) {
  444. let index = geometry.getIndex();
  445. // generate index if not present
  446. if ( index === null ) {
  447. const indices = [];
  448. const position = geometry.getAttribute( 'position' );
  449. if ( position !== undefined ) {
  450. for ( let i = 0; i < position.count; i ++ ) {
  451. indices.push( i );
  452. }
  453. geometry.setIndex( indices );
  454. index = geometry.getIndex();
  455. } else {
  456. console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
  457. return geometry;
  458. }
  459. }
  460. //
  461. const numberOfTriangles = index.count - 2;
  462. const newIndices = [];
  463. if ( drawMode === TriangleFanDrawMode ) {
  464. // gl.TRIANGLE_FAN
  465. for ( let i = 1; i <= numberOfTriangles; i ++ ) {
  466. newIndices.push( index.getX( 0 ) );
  467. newIndices.push( index.getX( i ) );
  468. newIndices.push( index.getX( i + 1 ) );
  469. }
  470. } else {
  471. // gl.TRIANGLE_STRIP
  472. for ( let i = 0; i < numberOfTriangles; i ++ ) {
  473. if ( i % 2 === 0 ) {
  474. newIndices.push( index.getX( i ) );
  475. newIndices.push( index.getX( i + 1 ) );
  476. newIndices.push( index.getX( i + 2 ) );
  477. } else {
  478. newIndices.push( index.getX( i + 2 ) );
  479. newIndices.push( index.getX( i + 1 ) );
  480. newIndices.push( index.getX( i ) );
  481. }
  482. }
  483. }
  484. if ( ( newIndices.length / 3 ) !== numberOfTriangles ) {
  485. console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' );
  486. }
  487. // build final geometry
  488. const newGeometry = geometry.clone();
  489. newGeometry.setIndex( newIndices );
  490. newGeometry.clearGroups();
  491. return newGeometry;
  492. } else {
  493. console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode );
  494. return geometry;
  495. }
  496. }
  497. /**
  498. * Calculates the morphed attributes of a morphed/skinned BufferGeometry.
  499. * Helpful for Raytracing or Decals.
  500. * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points.
  501. * @return {Object} An Object with original position/normal attributes and morphed ones.
  502. */
  503. function computeMorphedAttributes( object ) {
  504. if ( object.geometry.isBufferGeometry !== true ) {
  505. console.error( 'THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.' );
  506. return null;
  507. }
  508. const _vA = new Vector3();
  509. const _vB = new Vector3();
  510. const _vC = new Vector3();
  511. const _tempA = new Vector3();
  512. const _tempB = new Vector3();
  513. const _tempC = new Vector3();
  514. const _morphA = new Vector3();
  515. const _morphB = new Vector3();
  516. const _morphC = new Vector3();
  517. function _calculateMorphedAttributeData(
  518. object,
  519. attribute,
  520. morphAttribute,
  521. morphTargetsRelative,
  522. a,
  523. b,
  524. c,
  525. modifiedAttributeArray
  526. ) {
  527. _vA.fromBufferAttribute( attribute, a );
  528. _vB.fromBufferAttribute( attribute, b );
  529. _vC.fromBufferAttribute( attribute, c );
  530. const morphInfluences = object.morphTargetInfluences;
  531. if ( morphAttribute && morphInfluences ) {
  532. _morphA.set( 0, 0, 0 );
  533. _morphB.set( 0, 0, 0 );
  534. _morphC.set( 0, 0, 0 );
  535. for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {
  536. const influence = morphInfluences[ i ];
  537. const morph = morphAttribute[ i ];
  538. if ( influence === 0 ) continue;
  539. _tempA.fromBufferAttribute( morph, a );
  540. _tempB.fromBufferAttribute( morph, b );
  541. _tempC.fromBufferAttribute( morph, c );
  542. if ( morphTargetsRelative ) {
  543. _morphA.addScaledVector( _tempA, influence );
  544. _morphB.addScaledVector( _tempB, influence );
  545. _morphC.addScaledVector( _tempC, influence );
  546. } else {
  547. _morphA.addScaledVector( _tempA.sub( _vA ), influence );
  548. _morphB.addScaledVector( _tempB.sub( _vB ), influence );
  549. _morphC.addScaledVector( _tempC.sub( _vC ), influence );
  550. }
  551. }
  552. _vA.add( _morphA );
  553. _vB.add( _morphB );
  554. _vC.add( _morphC );
  555. }
  556. if ( object.isSkinnedMesh ) {
  557. object.boneTransform( a, _vA );
  558. object.boneTransform( b, _vB );
  559. object.boneTransform( c, _vC );
  560. }
  561. modifiedAttributeArray[ a * 3 + 0 ] = _vA.x;
  562. modifiedAttributeArray[ a * 3 + 1 ] = _vA.y;
  563. modifiedAttributeArray[ a * 3 + 2 ] = _vA.z;
  564. modifiedAttributeArray[ b * 3 + 0 ] = _vB.x;
  565. modifiedAttributeArray[ b * 3 + 1 ] = _vB.y;
  566. modifiedAttributeArray[ b * 3 + 2 ] = _vB.z;
  567. modifiedAttributeArray[ c * 3 + 0 ] = _vC.x;
  568. modifiedAttributeArray[ c * 3 + 1 ] = _vC.y;
  569. modifiedAttributeArray[ c * 3 + 2 ] = _vC.z;
  570. }
  571. const geometry = object.geometry;
  572. const material = object.material;
  573. let a, b, c;
  574. const index = geometry.index;
  575. const positionAttribute = geometry.attributes.position;
  576. const morphPosition = geometry.morphAttributes.position;
  577. const morphTargetsRelative = geometry.morphTargetsRelative;
  578. const normalAttribute = geometry.attributes.normal;
  579. const morphNormal = geometry.morphAttributes.position;
  580. const groups = geometry.groups;
  581. const drawRange = geometry.drawRange;
  582. let i, j, il, jl;
  583. let group;
  584. let start, end;
  585. const modifiedPosition = new Float32Array( positionAttribute.count * positionAttribute.itemSize );
  586. const modifiedNormal = new Float32Array( normalAttribute.count * normalAttribute.itemSize );
  587. if ( index !== null ) {
  588. // indexed buffer geometry
  589. if ( Array.isArray( material ) ) {
  590. for ( i = 0, il = groups.length; i < il; i ++ ) {
  591. group = groups[ i ];
  592. start = Math.max( group.start, drawRange.start );
  593. end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) );
  594. for ( j = start, jl = end; j < jl; j += 3 ) {
  595. a = index.getX( j );
  596. b = index.getX( j + 1 );
  597. c = index.getX( j + 2 );
  598. _calculateMorphedAttributeData(
  599. object,
  600. positionAttribute,
  601. morphPosition,
  602. morphTargetsRelative,
  603. a, b, c,
  604. modifiedPosition
  605. );
  606. _calculateMorphedAttributeData(
  607. object,
  608. normalAttribute,
  609. morphNormal,
  610. morphTargetsRelative,
  611. a, b, c,
  612. modifiedNormal
  613. );
  614. }
  615. }
  616. } else {
  617. start = Math.max( 0, drawRange.start );
  618. end = Math.min( index.count, ( drawRange.start + drawRange.count ) );
  619. for ( i = start, il = end; i < il; i += 3 ) {
  620. a = index.getX( i );
  621. b = index.getX( i + 1 );
  622. c = index.getX( i + 2 );
  623. _calculateMorphedAttributeData(
  624. object,
  625. positionAttribute,
  626. morphPosition,
  627. morphTargetsRelative,
  628. a, b, c,
  629. modifiedPosition
  630. );
  631. _calculateMorphedAttributeData(
  632. object,
  633. normalAttribute,
  634. morphNormal,
  635. morphTargetsRelative,
  636. a, b, c,
  637. modifiedNormal
  638. );
  639. }
  640. }
  641. } else {
  642. // non-indexed buffer geometry
  643. if ( Array.isArray( material ) ) {
  644. for ( i = 0, il = groups.length; i < il; i ++ ) {
  645. group = groups[ i ];
  646. start = Math.max( group.start, drawRange.start );
  647. end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) );
  648. for ( j = start, jl = end; j < jl; j += 3 ) {
  649. a = j;
  650. b = j + 1;
  651. c = j + 2;
  652. _calculateMorphedAttributeData(
  653. object,
  654. positionAttribute,
  655. morphPosition,
  656. morphTargetsRelative,
  657. a, b, c,
  658. modifiedPosition
  659. );
  660. _calculateMorphedAttributeData(
  661. object,
  662. normalAttribute,
  663. morphNormal,
  664. morphTargetsRelative,
  665. a, b, c,
  666. modifiedNormal
  667. );
  668. }
  669. }
  670. } else {
  671. start = Math.max( 0, drawRange.start );
  672. end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );
  673. for ( i = start, il = end; i < il; i += 3 ) {
  674. a = i;
  675. b = i + 1;
  676. c = i + 2;
  677. _calculateMorphedAttributeData(
  678. object,
  679. positionAttribute,
  680. morphPosition,
  681. morphTargetsRelative,
  682. a, b, c,
  683. modifiedPosition
  684. );
  685. _calculateMorphedAttributeData(
  686. object,
  687. normalAttribute,
  688. morphNormal,
  689. morphTargetsRelative,
  690. a, b, c,
  691. modifiedNormal
  692. );
  693. }
  694. }
  695. }
  696. const morphedPositionAttribute = new Float32BufferAttribute( modifiedPosition, 3 );
  697. const morphedNormalAttribute = new Float32BufferAttribute( modifiedNormal, 3 );
  698. return {
  699. positionAttribute: positionAttribute,
  700. normalAttribute: normalAttribute,
  701. morphedPositionAttribute: morphedPositionAttribute,
  702. morphedNormalAttribute: morphedNormalAttribute
  703. };
  704. }
  705. function mergeGroups( geometry ) {
  706. if ( geometry.groups.length === 0 ) {
  707. console.warn( 'THREE.BufferGeometryUtils.mergeGroups(): No groups are defined. Nothing to merge.' );
  708. return geometry;
  709. }
  710. let groups = geometry.groups;
  711. // sort groups by material index
  712. groups = groups.sort( ( a, b ) => {
  713. if ( a.materialIndex !== b.materialIndex ) return a.materialIndex - b.materialIndex;
  714. return a.start - b.start;
  715. } );
  716. // create index for non-indexed geometries
  717. if ( geometry.getIndex() === null ) {
  718. const positionAttribute = geometry.getAttribute( 'position' );
  719. const indices = [];
  720. for ( let i = 0; i < positionAttribute.count; i += 3 ) {
  721. indices.push( i, i + 1, i + 2 );
  722. }
  723. geometry.setIndex( indices );
  724. }
  725. // sort index
  726. const index = geometry.getIndex();
  727. const newIndices = [];
  728. for ( let i = 0; i < groups.length; i ++ ) {
  729. const group = groups[ i ];
  730. const groupStart = group.start;
  731. const groupLength = groupStart + group.count;
  732. for ( let j = groupStart; j < groupLength; j ++ ) {
  733. newIndices.push( index.getX( j ) );
  734. }
  735. }
  736. geometry.dispose(); // Required to force buffer recreation
  737. geometry.setIndex( newIndices );
  738. // update groups indices
  739. let start = 0;
  740. for ( let i = 0; i < groups.length; i ++ ) {
  741. const group = groups[ i ];
  742. group.start = start;
  743. start += group.count;
  744. }
  745. // merge groups
  746. let currentGroup = groups[ 0 ];
  747. geometry.groups = [ currentGroup ];
  748. for ( let i = 1; i < groups.length; i ++ ) {
  749. const group = groups[ i ];
  750. if ( currentGroup.materialIndex === group.materialIndex ) {
  751. currentGroup.count += group.count;
  752. } else {
  753. currentGroup = group;
  754. geometry.groups.push( currentGroup );
  755. }
  756. }
  757. return geometry;
  758. }
  759. export {
  760. computeTangents,
  761. computeMikkTSpaceTangents,
  762. mergeBufferGeometries,
  763. mergeBufferAttributes,
  764. interleaveAttributes,
  765. estimateBytesUsed,
  766. mergeVertices,
  767. toTrianglesDrawMode,
  768. computeMorphedAttributes,
  769. mergeGroups
  770. };