1
0

SimplifyModifier.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import {
  2. BufferAttribute,
  3. BufferGeometry
  4. } from 'three';
  5. import { MeshoptSimplifier } from '../libs/meshopt_simplifier.module.js';
  6. import { mergeVertices } from '../utils/BufferGeometryUtils.js';
  7. /**
  8. * This class can be used to modify a geometry by simplifying it. A typical use
  9. * case for such a modifier is automatic LOD generation.
  10. *
  11. * The implementation is based on [meshoptimizer]{@link https://github.com/zeux/meshoptimizer}.
  12. * If you only need a simplified index buffer, use {@link MeshoptSimplifier} directly.
  13. *
  14. * ```js
  15. * const modifier = new SimplifyModifier();
  16. * geometry = await modifier.modify( geometry, count );
  17. * ```
  18. *
  19. * @three_import import { SimplifyModifier } from 'three/addons/modifiers/SimplifyModifier.js';
  20. */
  21. class SimplifyModifier {
  22. /**
  23. * Returns a new, simplified version of the given geometry. The vertex buffers
  24. * of the result only contain vertices referenced by the simplified index.
  25. *
  26. * @async
  27. * @param {BufferGeometry} geometry - The geometry to modify.
  28. * @param {number} count - The approximate number of vertices to remove.
  29. * @return {Promise<BufferGeometry>} A promise that resolves with the new, modified geometry.
  30. */
  31. async modify( geometry, count ) {
  32. await MeshoptSimplifier.ready;
  33. // non-indexed geometries must be welded so the simplifier can collapse edges
  34. if ( geometry.getIndex() === null ) geometry = mergeVertices( geometry );
  35. const index = geometry.getIndex();
  36. const positionAttribute = geometry.getAttribute( 'position' );
  37. // the simplifier requires tightly packed positions
  38. const positions = new Float32Array( positionAttribute.count * 3 );
  39. for ( let i = 0; i < positionAttribute.count; i ++ ) {
  40. positions[ i * 3 + 0 ] = positionAttribute.getX( i );
  41. positions[ i * 3 + 1 ] = positionAttribute.getY( i );
  42. positions[ i * 3 + 2 ] = positionAttribute.getZ( i );
  43. }
  44. const ratio = Math.min( 1, Math.max( 0, 1 - count / positionAttribute.count ) );
  45. const targetIndexCount = Math.max( 3, Math.floor( index.count * ratio / 3 ) * 3 );
  46. // normals and uvs, when present, contribute to the error metric so collapses
  47. // that would distort shading or texture seams are penalized
  48. const normalAttribute = geometry.getAttribute( 'normal' );
  49. const uvAttribute = geometry.getAttribute( 'uv' );
  50. const attributeStride = ( normalAttribute ? 3 : 0 ) + ( uvAttribute ? 2 : 0 );
  51. let indices;
  52. if ( attributeStride > 0 ) {
  53. const vertexAttributes = new Float32Array( positionAttribute.count * attributeStride );
  54. const attributeWeights = [];
  55. if ( normalAttribute ) attributeWeights.push( 0.25, 0.25, 0.25 );
  56. if ( uvAttribute ) attributeWeights.push( 0.5, 0.5 );
  57. for ( let i = 0, o = 0; i < positionAttribute.count; i ++ ) {
  58. if ( normalAttribute ) {
  59. vertexAttributes[ o ++ ] = normalAttribute.getX( i );
  60. vertexAttributes[ o ++ ] = normalAttribute.getY( i );
  61. vertexAttributes[ o ++ ] = normalAttribute.getZ( i );
  62. }
  63. if ( uvAttribute ) {
  64. vertexAttributes[ o ++ ] = uvAttribute.getX( i );
  65. vertexAttributes[ o ++ ] = uvAttribute.getY( i );
  66. }
  67. }
  68. indices = MeshoptSimplifier.simplifyWithAttributes( index.array, positions, 3, vertexAttributes, attributeStride, attributeWeights, null, targetIndexCount, 1 )[ 0 ];
  69. } else {
  70. indices = MeshoptSimplifier.simplify( index.array, positions, 3, targetIndexCount, 1 )[ 0 ];
  71. }
  72. // drop vertices that are no longer referenced and rebuild the attributes
  73. const [ remap, unique ] = MeshoptSimplifier.compactMesh( indices ); // rewrites indices in place
  74. const simplifiedGeometry = new BufferGeometry();
  75. for ( const name in geometry.attributes ) {
  76. const attribute = geometry.getAttribute( name );
  77. const itemSize = attribute.itemSize;
  78. const newAttribute = new BufferAttribute( new attribute.array.constructor( unique * itemSize ), itemSize, attribute.normalized );
  79. // scatter each vertex to its new slot; 0xffffffff marks unreferenced vertices
  80. for ( let i = 0; i < remap.length; i ++ ) {
  81. const target = remap[ i ];
  82. if ( target === 0xffffffff ) continue;
  83. for ( let k = 0; k < itemSize; k ++ ) newAttribute.setComponent( target, k, attribute.getComponent( i, k ) );
  84. }
  85. simplifiedGeometry.setAttribute( name, newAttribute );
  86. }
  87. simplifiedGeometry.setIndex( new BufferAttribute( indices, 1 ) );
  88. return simplifiedGeometry;
  89. }
  90. }
  91. export { SimplifyModifier };
粤ICP备19079148号