vendor: 9,107 bytes, lines 1-380
1import { 2 BufferAttribute, 3 BufferGeometry, 4 Float32BufferAttribute, 5 InstancedBufferAttribute, 6 InterleavedBuffer, 7 InterleavedBufferAttribute, 8 TriangleFanDrawMode, 9 TriangleStripDrawMode, 10 TrianglesDrawMode, 11 Vector3, 12} from 'three'; 13 14function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) { 15 16 if ( ! MikkTSpace || ! MikkTSpace.isReady ) { 17 18 throw new Error( 'BufferGeometryUtils: Initialized MikkTSpace library required.' ); 19 20 } 21 22 if ( ! geometry.hasAttribute( 'position' ) || ! geometry.hasAttribute( 'normal' ) || ! geometry.hasAttribute( 'uv' ) ) { 23 24 throw new Error( 'BufferGeometryUtils: Tangents require "position", "normal", and "uv" attributes.' ); 25 26 } 27 28 function getAttributeArray( attribute ) { 29 30 if ( attribute.normalized || attribute.isInterleavedBufferAttribute ) { 31 32 const dstArray = new Float32Array( attribute.count * attribute.itemSize ); 33 34 for ( let i = 0, j = 0; i < attribute.count; i ++ ) { 35 36 dstArray[ j ++ ] = attribute.getX( i ); 37 dstArray[ j ++ ] = attribute.getY( i ); 38 39 if ( attribute.itemSize > 2 ) { 40 41 dstArray[ j ++ ] = attribute.getZ( i ); 42 43 } 44 45 } 46 47 return dstArray; 48 49 } 50 51 if ( attribute.array instanceof Float32Array ) { 52 53 return attribute.array; 54 55 } 56 57 return new Float32Array( attribute.array ); 58 59 } 60 61 // MikkTSpace algorithm requires non-indexed input. 62 63 const _geometry = geometry.index ? geometry.toNonIndexed() : geometry; 64 65 // Compute vertex tangents. 66 67 const tangents = MikkTSpace.generateTangents( 68 69 getAttributeArray( _geometry.attributes.position ), 70 getAttributeArray( _geometry.attributes.normal ), 71 getAttributeArray( _geometry.attributes.uv ) 72 73 ); 74 75 // Texture coordinate convention of glTF differs from the apparent 76 // default of the MikkTSpace library; .w component must be flipped. 77 78 if ( negateSign ) { 79 80 for ( let i = 3; i < tangents.length; i += 4 ) { 81 82 tangents[ i ] *= - 1; 83 84 } 85 86 } 87 88 // 89 90 _geometry.setAttribute( 'tangent', new BufferAttribute( tangents, 4 ) ); 91 92 if ( geometry !== _geometry ) { 93 94 geometry.copy( _geometry ); 95 96 } 97 98 return geometry; 99 100} 101 102/** 103 * @param {Array<BufferGeometry>} geometries 104 * @param {Boolean} useGroups 105 * @return {BufferGeometry} 106 */ 107function mergeGeometries( geometries, useGroups = false ) { 108 109 const isIndexed = geometries[ 0 ].index !== null; 110 111 const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) ); 112 const morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) ); 113 114 const attributes = {}; 115 const morphAttributes = {}; 116 117 const morphTargetsRelative = geometries[ 0 ].morphTargetsRelative; 118 119 const mergedGeometry = new BufferGeometry(); 120 121 let offset = 0; 122 123 for ( let i = 0; i < geometries.length; ++ i ) { 124 125 const geometry = geometries[ i ]; 126 let attributesCount = 0; 127 128 // ensure that all geometries are indexed, or none 129 130 if ( isIndexed !== ( geometry.index !== null ) ) { 131 132 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() 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.' ); 133 return null; 134 135 } 136 137 // gather attributes, exit early if they're different 138 139 for ( const name in geometry.attributes ) { 140 141 if ( ! attributesUsed.has( name ) ) { 142 143 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() 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.' ); 144 return null; 145 146 } 147 148 if ( attributes[ name ] === undefined ) attributes[ name ] = []; 149 150 attributes[ name ].push( geometry.attributes[ name ] ); 151 152 attributesCount ++; 153 154 } 155 156 // ensure geometries have the same number of attributes 157 158 if ( attributesCount !== attributesUsed.size ) { 159 160 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' ); 161 return null; 162 163 } 164 165 // gather morph attributes, exit early if they're different 166 167 if ( morphTargetsRelative !== geometry.morphTargetsRelative ) { 168 169 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' ); 170 return null; 171 172 } 173 174 for ( const name in geometry.morphAttributes ) { 175 176 if ( ! morphAttributesUsed.has( name ) ) { 177 178 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.' ); 179 return null; 180 181 } 182 183 if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = []; 184 185 morphAttributes[ name ].push( geometry.morphAttributes[ name ] ); 186 187 } 188 189 if ( useGroups ) { 190 191 let count; 192 193 if ( isIndexed ) { 194 195 count = geometry.index.count; 196 197 } else if ( geometry.attributes.position !== undefined ) { 198 199 count = geometry.attributes.position.count; 200 201 } else { 202 203 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' ); 204 return null; 205 206 } 207 208 mergedGeometry.addGroup( offset, count, i ); 209 210 offset += count; 211 212 } 213 214 } 215 216 // merge indices 217 218 if ( isIndexed ) { 219 220 let indexOffset = 0; 221 const mergedIndex = []; 222 223 for ( let i = 0; i < geometries.length; ++ i ) { 224 225 const index = geometries[ i ].index; 226 227 for ( let j = 0; j < index.count; ++ j ) { 228 229 mergedIndex.push( index.getX( j ) + indexOffset ); 230 231 } 232 233 indexOffset += geometries[ i ].attributes.position.count; 234 235 } 236 237 mergedGeometry.setIndex( mergedIndex ); 238 239 } 240 241 // merge attributes 242 243 for ( const name in attributes ) { 244 245 const mergedAttribute = mergeAttributes( attributes[ name ] ); 246 247 if ( ! mergedAttribute ) { 248 249 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' attribute.' ); 250 return null; 251 252 } 253 254 mergedGeometry.setAttribute( name, mergedAttribute ); 255 256 } 257 258 // merge morph attributes 259 260 for ( const name in morphAttributes ) { 261 262 const numMorphTargets = morphAttributes[ name ][ 0 ].length; 263 264 if ( numMorphTargets === 0 ) break; 265 266 mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {}; 267 mergedGeometry.morphAttributes[ name ] = []; 268 269 for ( let i = 0; i < numMorphTargets; ++ i ) { 270 271 const morphAttributesToMerge = []; 272 273 for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) { 274 275 morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] ); 276 277 } 278 279 const mergedMorphAttribute = mergeAttributes( morphAttributesToMerge ); 280 281 if ( ! mergedMorphAttribute ) { 282 283 console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' morphAttribute.' ); 284 return null; 285 286 } 287 288 mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute ); 289 290 } 291 292 } 293 294 return mergedGeometry; 295 296} 297 298/** 299 * @param {Array<BufferAttribute>} attributes 300 * @return {BufferAttribute} 301 */ 302function mergeAttributes( attributes ) { 303 304 let TypedArray; 305 let itemSize; 306 let normalized; 307 let gpuType = - 1; 308 let arrayLength = 0; 309 310 for ( let i = 0; i < attributes.length; ++ i ) { 311 312 const attribute = attributes[ i ]; 313 314 if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; 315 if ( TypedArray !== attribute.array.constructor ) { 316 317 console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.' ); 318 return null; 319 320 } 321 322 if ( itemSize === undefined ) itemSize = attribute.itemSize; 323 if ( itemSize !== attribute.itemSize ) { 324 325 console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.' ); 326 return null; 327 328 } 329 330 if ( normalized === undefined ) normalized = attribute.normalized; 331 if ( normalized !== attribute.normalized ) { 332 333 console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.' ); 334 return null; 335 336 } 337 338 if ( gpuType === - 1 ) gpuType = attribute.gpuType; 339 if ( gpuType !== attribute.gpuType ) { 340 341 console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.gpuType must be consistent across matching attributes.' ); 342 return null; 343 344 } 345 346 arrayLength += attribute.count * itemSize; 347 348 } 349 350 const array = new TypedArray( arrayLength ); 351 const result = new BufferAttribute( array, itemSize, normalized ); 352 let offset = 0; 353 354 for ( let i = 0; i < attributes.length; ++ i ) { 355 356 const attribute = attributes[ i ]; 357 if ( attribute.isInterleavedBufferAttribute ) { 358 359 const tupleOffset = offset / itemSize; 360 for ( let j = 0, l = attribute.count; j < l; j ++ ) { 361 362 for ( let c = 0; c < itemSize; c ++ ) { 363 364 const value = attribute.getComponent( j, c ); 365 result.setComponent( j + tupleOffset, c, value ); 366 367 } 368 369 } 370 371 } else { 372 373 array.set( attribute.array, offset ); 374 375 } 376 377 offset += attribute.count * itemSize; 378 379 } 380
381 if ( gpuType !== undefined ) { 382 383 result.gpuType = gpuType; 384 385 } 386 387 return result; 388 389} 390 391/** 392 * @param {BufferAttribute} 393 * @return {BufferAttribute} 394 */ 395export function deepCloneAttribute( attribute ) { 396 397 if ( attribute.isInstancedInterleavedBufferAttribute || attribute.isInterleavedBufferAttribute ) { 398 399 return deinterleaveAttribute( attribute ); 400 401 } 402 403 if ( attribute.isInstancedBufferAttribute ) { 404 405 return new InstancedBufferAttribute().copy( attribute ); 406 407 } 408 409 return new BufferAttribute().copy( attribute ); 410 411} 412 413/** 414 * @param {Array<BufferAttribute>} attributes 415 * @return {Array<InterleavedBufferAttribute>} 416 */ 417function interleaveAttributes( attributes ) { 418 419 // Interleaves the provided attributes into an InterleavedBuffer and returns 420 // a set of InterleavedBufferAttributes for each attribute 421 let TypedArray; 422 let arrayLength = 0; 423 let stride = 0; 424 425 // calculate the length and type of the interleavedBuffer 426 for ( let i = 0, l = attributes.length; i < l; ++ i ) { 427 428 const attribute = attributes[ i ]; 429 430 if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; 431 if ( TypedArray !== attribute.array.constructor ) { 432 433 console.error( 'AttributeBuffers of different types cannot be interleaved' ); 434 return null; 435 436 } 437 438 arrayLength += attribute.array.length; 439 stride += attribute.itemSize; 440 441 } 442 443 // Create the set of buffer attributes 444 const interleavedBuffer = new InterleavedBuffer( new TypedArray( arrayLength ), stride ); 445 let offset = 0; 446 const res = []; 447 const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; 448 const setters = [ 'setX', 'setY', 'setZ', 'setW' ]; 449 450 for ( let j = 0, l = attributes.length; j < l; j ++ ) { 451 452 const attribute = attributes[ j ]; 453 const itemSize = attribute.itemSize; 454 const count = attribute.count; 455 const iba = new InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, attribute.normalized ); 456 res.push( iba ); 457 458 offset += itemSize; 459 460 // Move the data for each attribute into the new interleavedBuffer 461 // at the appropriate offset 462 for ( let c = 0; c < count; c ++ ) { 463 464 for ( let k = 0; k < itemSize; k ++ ) { 465 466 iba[ setters[ k ] ]( c, attribute[ getters[ k ] ]( c ) ); 467 468 } 469 470 } 471 472 } 473 474 return res; 475 476} 477 478// returns a new, non-interleaved version of the provided attribute 479export function deinterleaveAttribute( attribute ) { 480 481 const cons = attribute.data.array.constructor; 482 const count = attribute.count; 483 const itemSize = attribute.itemSize; 484 const normalized = attribute.normalized; 485 486 const array = new cons( count * itemSize ); 487 let newAttribute; 488 if ( attribute.isInstancedInterleavedBufferAttribute ) { 489 490 newAttribute = new InstancedBufferAttribute( array, itemSize, normalized, attribute.meshPerAttribute ); 491 492 } else { 493 494 newAttribute = new BufferAttribute( array, itemSize, normalized ); 495 496 } 497 498 for ( let i = 0; i < count; i ++ ) { 499 500 newAttribute.setX( i, attribute.getX( i ) ); 501 502 if ( itemSize >= 2 ) { 503 504 newAttribute.setY( i, attribute.getY( i ) ); 505 506 } 507 508 if ( itemSize >= 3 ) { 509 510 newAttribute.setZ( i, attribute.getZ( i ) ); 511 512 } 513 514 if ( itemSize >= 4 ) { 515 516 newAttribute.setW( i, attribute.getW( i ) ); 517 518 } 519 520 } 521 522 return newAttribute; 523 524} 525 526// deinterleaves all attributes on the geometry 527export function deinterleaveGeometry( geometry ) { 528 529 const attributes = geometry.attributes; 530 const morphTargets = geometry.morphTargets; 531 const attrMap = new Map(); 532 533 for ( const key in attributes ) { 534 535 const attr = attributes[ key ]; 536 if ( attr.isInterleavedBufferAttribute ) { 537 538 if ( ! attrMap.has( attr ) ) { 539 540 attrMap.set( attr, deinterleaveAttribute( attr ) ); 541 542 } 543 544 attributes[ key ] = attrMap.get( attr ); 545 546 } 547 548 } 549 550 for ( const key in morphTargets ) { 551 552 const attr = morphTargets[ key ]; 553 if ( attr.isInterleavedBufferAttribute ) { 554 555 if ( ! attrMap.has( attr ) ) { 556 557 attrMap.set( attr, deinterleaveAttribute( attr ) ); 558 559 } 560 561 morphTargets[ key ] = attrMap.get( attr ); 562 563 } 564 565 } 566 567} 568 569/** 570 * @param {BufferGeometry} geometry 571 * @return {number} 572 */ 573function estimateBytesUsed( geometry ) { 574 575 // Return the estimated memory used by this geometry in bytes 576 // Calculate using itemSize, count, and BYTES_PER_ELEMENT to account 577 // for InterleavedBufferAttributes. 578 let mem = 0; 579 for ( const name in geometry.attributes ) { 580 581 const attr = geometry.getAttribute( name ); 582 mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT; 583 584 } 585 586 const indices = geometry.getIndex(); 587 mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0; 588 return mem; 589 590} 591 592/** 593 * @param {BufferGeometry} geometry 594 * @param {number} tolerance 595 * @return {BufferGeometry} 596 */ 597function mergeVertices( geometry, tolerance = 1e-4 ) { 598 599 tolerance = Math.max( tolerance, Number.EPSILON ); 600 601 // Generate an index buffer if the geometry doesn't have one, or optimize it 602 // if it's already available. 603 const hashToIndex = {}; 604 const indices = geometry.getIndex(); 605 const positions = geometry.getAttribute( 'position' ); 606 const vertexCount = indices ? indices.count : positions.count; 607 608 // next value for triangle indices 609 let nextIndex = 0; 610 611 // attributes and new attribute arrays 612 const attributeNames = Object.keys( geometry.attributes ); 613 const tmpAttributes = {}; 614 const tmpMorphAttributes = {}; 615 const newIndices = []; 616 const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; 617 const setters = [ 'setX', 'setY', 'setZ', 'setW' ]; 618 619 // Initialize the arrays, allocating space conservatively. Extra 620 // space will be trimmed in the last step. 621 for ( let i = 0, l = attributeNames.length; i < l; i ++ ) { 622 623 const name = attributeNames[ i ]; 624 const attr = geometry.attributes[ name ]; 625 626 tmpAttributes[ name ] = new BufferAttribute( 627 new attr.array.constructor( attr.count * attr.itemSize ), 628 attr.itemSize, 629 attr.normalized 630 ); 631 632 const morphAttr = geometry.morphAttributes[ name ]; 633 if ( morphAttr ) { 634 635 tmpMorphAttributes[ name ] = new BufferAttribute( 636 new morphAttr.array.constructor( morphAttr.count * morphAttr.itemSize ), 637 morphAttr.itemSize, 638 morphAttr.normalized 639 ); 640 641 } 642 643 } 644 645 // convert the error tolerance to an amount of decimal places to truncate to 646 const halfTolerance = tolerance * 0.5; 647 const exponent = Math.log10( 1 / tolerance ); 648 const hashMultiplier = Math.pow( 10, exponent ); 649 const hashAdditive = halfTolerance * hashMultiplier; 650 for ( let i = 0; i < vertexCount; i ++ ) { 651 652 const index = indices ? indices.getX( i ) : i; 653 654 // Generate a hash for the vertex attributes at the current index 'i' 655 let hash = ''; 656 for ( let j = 0, l = attributeNames.length; j < l; j ++ ) { 657 658 const name = attributeNames[ j ]; 659 const attribute = geometry.getAttribute( name ); 660 const itemSize = attribute.itemSize; 661 662 for ( let k = 0; k < itemSize; k ++ ) { 663 664 // double tilde truncates the decimal value 665 hash += `${ ~ ~ ( attribute[ getters[ k ] ]( index ) * hashMultiplier + hashAdditive ) },`; 666 667 } 668 669 } 670 671 // Add another reference to the vertex if it's already 672 // used by another index 673 if ( hash in hashToIndex ) { 674 675 newIndices.push( hashToIndex[ hash ] ); 676 677 } else { 678 679 // copy data to the new index in the temporary attributes 680 for ( let j = 0, l = attributeNames.length; j < l; j ++ ) { 681 682 const name = attributeNames[ j ]; 683 const attribute = geometry.getAttribute( name ); 684 const morphAttr = geometry.morphAttributes[ name ]; 685 const itemSize = attribute.itemSize; 686 const newarray = tmpAttributes[ name ];
vendor: 10,072 bytes, lines 687-1153
687 const newMorphArrays = tmpMorphAttributes[ name ]; 688 689 for ( let k = 0; k < itemSize; k ++ ) { 690 691 const getterFunc = getters[ k ]; 692 const setterFunc = setters[ k ]; 693 newarray[ setterFunc ]( nextIndex, attribute[ getterFunc ]( index ) ); 694 695 if ( morphAttr ) { 696 697 for ( let m = 0, ml = morphAttr.length; m < ml; m ++ ) { 698 699 newMorphArrays[ m ][ setterFunc ]( nextIndex, morphAttr[ m ][ getterFunc ]( index ) ); 700 701 } 702 703 } 704 705 } 706 707 } 708 709 hashToIndex[ hash ] = nextIndex; 710 newIndices.push( nextIndex ); 711 nextIndex ++; 712 713 } 714 715 } 716 717 // generate result BufferGeometry 718 const result = geometry.clone(); 719 for ( const name in geometry.attributes ) { 720 721 const tmpAttribute = tmpAttributes[ name ]; 722 723 result.setAttribute( name, new BufferAttribute( 724 tmpAttribute.array.slice( 0, nextIndex * tmpAttribute.itemSize ), 725 tmpAttribute.itemSize, 726 tmpAttribute.normalized, 727 ) ); 728 729 if ( ! ( name in tmpMorphAttributes ) ) continue; 730 731 for ( let j = 0; j < tmpMorphAttributes[ name ].length; j ++ ) { 732 733 const tmpMorphAttribute = tmpMorphAttributes[ name ][ j ]; 734 735 result.morphAttributes[ name ][ j ] = new BufferAttribute( 736 tmpMorphAttribute.array.slice( 0, nextIndex * tmpMorphAttribute.itemSize ), 737 tmpMorphAttribute.itemSize, 738 tmpMorphAttribute.normalized, 739 ); 740 741 } 742 743 } 744 745 // indices 746 747 result.setIndex( newIndices ); 748 749 return result; 750 751} 752 753/** 754 * @param {BufferGeometry} geometry 755 * @param {number} drawMode 756 * @return {BufferGeometry} 757 */ 758function toTrianglesDrawMode( geometry, drawMode ) { 759 760 if ( drawMode === TrianglesDrawMode ) { 761 762 console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' ); 763 return geometry; 764 765 } 766 767 if ( drawMode === TriangleFanDrawMode || drawMode === TriangleStripDrawMode ) { 768 769 let index = geometry.getIndex(); 770 771 // generate index if not present 772 773 if ( index === null ) { 774 775 const indices = []; 776 777 const position = geometry.getAttribute( 'position' ); 778 779 if ( position !== undefined ) { 780 781 for ( let i = 0; i < position.count; i ++ ) { 782 783 indices.push( i ); 784 785 } 786 787 geometry.setIndex( indices ); 788 index = geometry.getIndex(); 789 790 } else { 791 792 console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); 793 return geometry; 794 795 } 796 797 } 798 799 // 800 801 const numberOfTriangles = index.count - 2; 802 const newIndices = []; 803 804 if ( drawMode === TriangleFanDrawMode ) { 805 806 // gl.TRIANGLE_FAN 807 808 for ( let i = 1; i <= numberOfTriangles; i ++ ) { 809 810 newIndices.push( index.getX( 0 ) ); 811 newIndices.push( index.getX( i ) ); 812 newIndices.push( index.getX( i + 1 ) ); 813 814 } 815 816 } else { 817 818 // gl.TRIANGLE_STRIP 819 820 for ( let i = 0; i < numberOfTriangles; i ++ ) { 821 822 if ( i % 2 === 0 ) { 823 824 newIndices.push( index.getX( i ) ); 825 newIndices.push( index.getX( i + 1 ) ); 826 newIndices.push( index.getX( i + 2 ) ); 827 828 } else { 829 830 newIndices.push( index.getX( i + 2 ) ); 831 newIndices.push( index.getX( i + 1 ) ); 832 newIndices.push( index.getX( i ) ); 833 834 } 835 836 } 837 838 } 839 840 if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { 841 842 console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); 843 844 } 845 846 // build final geometry 847 848 const newGeometry = geometry.clone(); 849 newGeometry.setIndex( newIndices ); 850 newGeometry.clearGroups(); 851 852 return newGeometry; 853 854 } else { 855 856 console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode ); 857 return geometry; 858 859 } 860 861} 862 863/** 864 * Calculates the morphed attributes of a morphed/skinned BufferGeometry. 865 * Helpful for Raytracing or Decals. 866 * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points. 867 * @return {Object} An Object with original position/normal attributes and morphed ones. 868 */ 869function computeMorphedAttributes( object ) { 870 871 const _vA = new Vector3(); 872 const _vB = new Vector3(); 873 const _vC = new Vector3(); 874 875 const _tempA = new Vector3(); 876 const _tempB = new Vector3(); 877 const _tempC = new Vector3(); 878 879 const _morphA = new Vector3(); 880 const _morphB = new Vector3(); 881 const _morphC = new Vector3(); 882 883 function _calculateMorphedAttributeData( 884 object, 885 attribute, 886 morphAttribute, 887 morphTargetsRelative, 888 a, 889 b, 890 c, 891 modifiedAttributeArray 892 ) { 893 894 _vA.fromBufferAttribute( attribute, a ); 895 _vB.fromBufferAttribute( attribute, b ); 896 _vC.fromBufferAttribute( attribute, c ); 897 898 const morphInfluences = object.morphTargetInfluences; 899 900 if ( morphAttribute && morphInfluences ) { 901 902 _morphA.set( 0, 0, 0 ); 903 _morphB.set( 0, 0, 0 ); 904 _morphC.set( 0, 0, 0 ); 905 906 for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { 907 908 const influence = morphInfluences[ i ]; 909 const morph = morphAttribute[ i ]; 910 911 if ( influence === 0 ) continue; 912 913 _tempA.fromBufferAttribute( morph, a ); 914 _tempB.fromBufferAttribute( morph, b ); 915 _tempC.fromBufferAttribute( morph, c ); 916 917 if ( morphTargetsRelative ) { 918 919 _morphA.addScaledVector( _tempA, influence ); 920 _morphB.addScaledVector( _tempB, influence ); 921 _morphC.addScaledVector( _tempC, influence ); 922 923 } else { 924 925 _morphA.addScaledVector( _tempA.sub( _vA ), influence ); 926 _morphB.addScaledVector( _tempB.sub( _vB ), influence ); 927 _morphC.addScaledVector( _tempC.sub( _vC ), influence ); 928 929 } 930 931 } 932 933 _vA.add( _morphA ); 934 _vB.add( _morphB ); 935 _vC.add( _morphC ); 936 937 } 938 939 if ( object.isSkinnedMesh ) { 940 941 object.applyBoneTransform( a, _vA ); 942 object.applyBoneTransform( b, _vB ); 943 object.applyBoneTransform( c, _vC ); 944 945 } 946 947 modifiedAttributeArray[ a * 3 + 0 ] = _vA.x; 948 modifiedAttributeArray[ a * 3 + 1 ] = _vA.y; 949 modifiedAttributeArray[ a * 3 + 2 ] = _vA.z; 950 modifiedAttributeArray[ b * 3 + 0 ] = _vB.x; 951 modifiedAttributeArray[ b * 3 + 1 ] = _vB.y; 952 modifiedAttributeArray[ b * 3 + 2 ] = _vB.z; 953 modifiedAttributeArray[ c * 3 + 0 ] = _vC.x; 954 modifiedAttributeArray[ c * 3 + 1 ] = _vC.y; 955 modifiedAttributeArray[ c * 3 + 2 ] = _vC.z; 956 957 } 958 959 const geometry = object.geometry; 960 const material = object.material; 961 962 let a, b, c; 963 const index = geometry.index; 964 const positionAttribute = geometry.attributes.position; 965 const morphPosition = geometry.morphAttributes.position; 966 const morphTargetsRelative = geometry.morphTargetsRelative; 967 const normalAttribute = geometry.attributes.normal; 968 const morphNormal = geometry.morphAttributes.position; 969 970 const groups = geometry.groups; 971 const drawRange = geometry.drawRange; 972 let i, j, il, jl; 973 let group; 974 let start, end; 975 976 const modifiedPosition = new Float32Array( positionAttribute.count * positionAttribute.itemSize ); 977 const modifiedNormal = new Float32Array( normalAttribute.count * normalAttribute.itemSize ); 978 979 if ( index !== null ) { 980 981 // indexed buffer geometry 982 983 if ( Array.isArray( material ) ) { 984 985 for ( i = 0, il = groups.length; i < il; i ++ ) { 986 987 group = groups[ i ]; 988 989 start = Math.max( group.start, drawRange.start ); 990 end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); 991 992 for ( j = start, jl = end; j < jl; j += 3 ) { 993 994 a = index.getX( j ); 995 b = index.getX( j + 1 ); 996 c = index.getX( j + 2 ); 997 998 _calculateMorphedAttributeData( 999 object, 1000 positionAttribute, 1001 morphPosition, 1002 morphTargetsRelative, 1003 a, b, c, 1004 modifiedPosition 1005 ); 1006 1007 _calculateMorphedAttributeData( 1008 object, 1009 normalAttribute, 1010 morphNormal, 1011 morphTargetsRelative, 1012 a, b, c, 1013 modifiedNormal 1014 ); 1015 1016 } 1017 1018 } 1019 1020 } else { 1021 1022 start = Math.max( 0, drawRange.start ); 1023 end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); 1024 1025 for ( i = start, il = end; i < il; i += 3 ) { 1026 1027 a = index.getX( i ); 1028 b = index.getX( i + 1 ); 1029 c = index.getX( i + 2 ); 1030 1031 _calculateMorphedAttributeData( 1032 object, 1033 positionAttribute, 1034 morphPosition, 1035 morphTargetsRelative, 1036 a, b, c, 1037 modifiedPosition 1038 ); 1039 1040 _calculateMorphedAttributeData( 1041 object, 1042 normalAttribute, 1043 morphNormal, 1044 morphTargetsRelative, 1045 a, b, c, 1046 modifiedNormal 1047 ); 1048 1049 } 1050 1051 } 1052 1053 } else { 1054 1055 // non-indexed buffer geometry 1056 1057 if ( Array.isArray( material ) ) { 1058 1059 for ( i = 0, il = groups.length; i < il; i ++ ) { 1060 1061 group = groups[ i ]; 1062 1063 start = Math.max( group.start, drawRange.start ); 1064 end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); 1065 1066 for ( j = start, jl = end; j < jl; j += 3 ) { 1067 1068 a = j; 1069 b = j + 1; 1070 c = j + 2; 1071 1072 _calculateMorphedAttributeData( 1073 object, 1074 positionAttribute, 1075 morphPosition, 1076 morphTargetsRelative, 1077 a, b, c, 1078 modifiedPosition 1079 ); 1080 1081 _calculateMorphedAttributeData( 1082 object, 1083 normalAttribute, 1084 morphNormal, 1085 morphTargetsRelative, 1086 a, b, c, 1087 modifiedNormal 1088 ); 1089 1090 } 1091 1092 } 1093 1094 } else { 1095 1096 start = Math.max( 0, drawRange.start ); 1097 end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); 1098 1099 for ( i = start, il = end; i < il; i += 3 ) { 1100 1101 a = i; 1102 b = i + 1; 1103 c = i + 2; 1104 1105 _calculateMorphedAttributeData( 1106 object, 1107 positionAttribute, 1108 morphPosition, 1109 morphTargetsRelative, 1110 a, b, c, 1111 modifiedPosition 1112 ); 1113 1114 _calculateMorphedAttributeData( 1115 object, 1116 normalAttribute, 1117 morphNormal, 1118 morphTargetsRelative, 1119 a, b, c, 1120 modifiedNormal 1121 ); 1122 1123 } 1124 1125 } 1126 1127 } 1128 1129 const morphedPositionAttribute = new Float32BufferAttribute( modifiedPosition, 3 ); 1130 const morphedNormalAttribute = new Float32BufferAttribute( modifiedNormal, 3 ); 1131 1132 return { 1133 1134 positionAttribute: positionAttribute, 1135 normalAttribute: normalAttribute, 1136 morphedPositionAttribute: morphedPositionAttribute, 1137 morphedNormalAttribute: morphedNormalAttribute 1138 1139 }; 1140 1141} 1142 1143function mergeGroups( geometry ) { 1144 1145 if ( geometry.groups.length === 0 ) { 1146 1147 console.warn( 'THREE.BufferGeometryUtils.mergeGroups(): No groups are defined. Nothing to merge.' ); 1148 return geometry; 1149 1150 } 1151 1152 let groups = geometry.groups; 1153
vendor: 4,668 bytes, lines 1154-1354
1154 // sort groups by material index 1155 1156 groups = groups.sort( ( a, b ) => { 1157 1158 if ( a.materialIndex !== b.materialIndex ) return a.materialIndex - b.materialIndex; 1159 1160 return a.start - b.start; 1161 1162 } ); 1163 1164 // create index for non-indexed geometries 1165 1166 if ( geometry.getIndex() === null ) { 1167 1168 const positionAttribute = geometry.getAttribute( 'position' ); 1169 const indices = []; 1170 1171 for ( let i = 0; i < positionAttribute.count; i += 3 ) { 1172 1173 indices.push( i, i + 1, i + 2 ); 1174 1175 } 1176 1177 geometry.setIndex( indices ); 1178 1179 } 1180 1181 // sort index 1182 1183 const index = geometry.getIndex(); 1184 1185 const newIndices = []; 1186 1187 for ( let i = 0; i < groups.length; i ++ ) { 1188 1189 const group = groups[ i ]; 1190 1191 const groupStart = group.start; 1192 const groupLength = groupStart + group.count; 1193 1194 for ( let j = groupStart; j < groupLength; j ++ ) { 1195 1196 newIndices.push( index.getX( j ) ); 1197 1198 } 1199 1200 } 1201 1202 geometry.dispose(); // Required to force buffer recreation 1203 geometry.setIndex( newIndices ); 1204 1205 // update groups indices 1206 1207 let start = 0; 1208 1209 for ( let i = 0; i < groups.length; i ++ ) { 1210 1211 const group = groups[ i ]; 1212 1213 group.start = start; 1214 start += group.count; 1215 1216 } 1217 1218 // merge groups 1219 1220 let currentGroup = groups[ 0 ]; 1221 1222 geometry.groups = [ currentGroup ]; 1223 1224 for ( let i = 1; i < groups.length; i ++ ) { 1225 1226 const group = groups[ i ]; 1227 1228 if ( currentGroup.materialIndex === group.materialIndex ) { 1229 1230 currentGroup.count += group.count; 1231 1232 } else { 1233 1234 currentGroup = group; 1235 geometry.groups.push( currentGroup ); 1236 1237 } 1238 1239 } 1240 1241 return geometry; 1242 1243} 1244 1245 1246/** 1247 * Modifies the supplied geometry if it is non-indexed, otherwise creates a new, 1248 * non-indexed geometry. Returns the geometry with smooth normals everywhere except 1249 * faces that meet at an angle greater than the crease angle. 1250 * 1251 * @param {BufferGeometry} geometry 1252 * @param {number} [creaseAngle] 1253 * @return {BufferGeometry} 1254 */ 1255function toCreasedNormals( geometry, creaseAngle = Math.PI / 3 /* 60 degrees */ ) { 1256 1257 const creaseDot = Math.cos( creaseAngle ); 1258 const hashMultiplier = ( 1 + 1e-10 ) * 1e2; 1259 1260 // reusable vectors 1261 const verts = [ new Vector3(), new Vector3(), new Vector3() ]; 1262 const tempVec1 = new Vector3(); 1263 const tempVec2 = new Vector3(); 1264 const tempNorm = new Vector3(); 1265 const tempNorm2 = new Vector3(); 1266 1267 // hashes a vector 1268 function hashVertex( v ) { 1269 1270 const x = ~ ~ ( v.x * hashMultiplier ); 1271 const y = ~ ~ ( v.y * hashMultiplier ); 1272 const z = ~ ~ ( v.z * hashMultiplier ); 1273 return `${x},${y},${z}`; 1274 1275 } 1276 1277 // BufferGeometry.toNonIndexed() warns if the geometry is non-indexed 1278 // and returns the original geometry 1279 const resultGeometry = geometry.index ? geometry.toNonIndexed() : geometry; 1280 const posAttr = resultGeometry.attributes.position; 1281 const vertexMap = {}; 1282 1283 // find all the normals shared by commonly located vertices 1284 for ( let i = 0, l = posAttr.count / 3; i < l; i ++ ) { 1285 1286 const i3 = 3 * i; 1287 const a = verts[ 0 ].fromBufferAttribute( posAttr, i3 + 0 ); 1288 const b = verts[ 1 ].fromBufferAttribute( posAttr, i3 + 1 ); 1289 const c = verts[ 2 ].fromBufferAttribute( posAttr, i3 + 2 ); 1290 1291 tempVec1.subVectors( c, b ); 1292 tempVec2.subVectors( a, b ); 1293 1294 // add the normal to the map for all vertices 1295 const normal = new Vector3().crossVectors( tempVec1, tempVec2 ).normalize(); 1296 for ( let n = 0; n < 3; n ++ ) { 1297 1298 const vert = verts[ n ]; 1299 const hash = hashVertex( vert ); 1300 if ( ! ( hash in vertexMap ) ) { 1301 1302 vertexMap[ hash ] = []; 1303 1304 } 1305 1306 vertexMap[ hash ].push( normal ); 1307 1308 } 1309 1310 } 1311 1312 // average normals from all vertices that share a common location if they are within the 1313 // provided crease threshold 1314 const normalArray = new Float32Array( posAttr.count * 3 ); 1315 const normAttr = new BufferAttribute( normalArray, 3, false ); 1316 for ( let i = 0, l = posAttr.count / 3; i < l; i ++ ) { 1317 1318 // get the face normal for this vertex 1319 const i3 = 3 * i; 1320 const a = verts[ 0 ].fromBufferAttribute( posAttr, i3 + 0 ); 1321 const b = verts[ 1 ].fromBufferAttribute( posAttr, i3 + 1 ); 1322 const c = verts[ 2 ].fromBufferAttribute( posAttr, i3 + 2 ); 1323 1324 tempVec1.subVectors( c, b ); 1325 tempVec2.subVectors( a, b ); 1326 1327 tempNorm.crossVectors( tempVec1, tempVec2 ).normalize(); 1328 1329 // average all normals that meet the threshold and set the normal value 1330 for ( let n = 0; n < 3; n ++ ) { 1331 1332 const vert = verts[ n ]; 1333 const hash = hashVertex( vert ); 1334 const otherNormals = vertexMap[ hash ]; 1335 tempNorm2.set( 0, 0, 0 ); 1336 1337 for ( let k = 0, lk = otherNormals.length; k < lk; k ++ ) { 1338 1339 const otherNorm = otherNormals[ k ]; 1340 if ( tempNorm.dot( otherNorm ) > creaseDot ) { 1341 1342 tempNorm2.add( otherNorm ); 1343 1344 } 1345 1346 } 1347 1348 tempNorm2.normalize(); 1349 normAttr.setXYZ( i3 + n, tempNorm2.x, tempNorm2.y, tempNorm2.z ); 1350 1351 } 1352 1353 } 1354
1355 resultGeometry.setAttribute( 'normal', normAttr ); 1356 return resultGeometry; 1357 1358} 1359 1360export { 1361 computeMikkTSpaceTangents, 1362 mergeGeometries, 1363 mergeAttributes, 1364 interleaveAttributes, 1365 estimateBytesUsed, 1366 mergeVertices, 1367 toTrianglesDrawMode, 1368 computeMorphedAttributes, 1369 mergeGroups, 1370 toCreasedNormals 1371};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.