| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import {
- BufferAttribute,
- BufferGeometry
- } from 'three';
- import { MeshoptSimplifier } from '../libs/meshopt_simplifier.module.js';
- import { mergeVertices } from '../utils/BufferGeometryUtils.js';
- /**
- * This class can be used to modify a geometry by simplifying it. A typical use
- * case for such a modifier is automatic LOD generation.
- *
- * The implementation is based on [meshoptimizer]{@link https://github.com/zeux/meshoptimizer}.
- * If you only need a simplified index buffer, use {@link MeshoptSimplifier} directly.
- *
- * ```js
- * const modifier = new SimplifyModifier();
- * geometry = await modifier.modify( geometry, count );
- * ```
- *
- * @three_import import { SimplifyModifier } from 'three/addons/modifiers/SimplifyModifier.js';
- */
- class SimplifyModifier {
- /**
- * Returns a new, simplified version of the given geometry. The vertex buffers
- * of the result only contain vertices referenced by the simplified index.
- *
- * @async
- * @param {BufferGeometry} geometry - The geometry to modify.
- * @param {number} count - The approximate number of vertices to remove.
- * @return {Promise<BufferGeometry>} A promise that resolves with the new, modified geometry.
- */
- async modify( geometry, count ) {
- await MeshoptSimplifier.ready;
- // non-indexed geometries must be welded so the simplifier can collapse edges
- if ( geometry.getIndex() === null ) geometry = mergeVertices( geometry );
- const index = geometry.getIndex();
- const positionAttribute = geometry.getAttribute( 'position' );
- // the simplifier requires tightly packed positions
- const positions = new Float32Array( positionAttribute.count * 3 );
- for ( let i = 0; i < positionAttribute.count; i ++ ) {
- positions[ i * 3 + 0 ] = positionAttribute.getX( i );
- positions[ i * 3 + 1 ] = positionAttribute.getY( i );
- positions[ i * 3 + 2 ] = positionAttribute.getZ( i );
- }
- const ratio = Math.min( 1, Math.max( 0, 1 - count / positionAttribute.count ) );
- const targetIndexCount = Math.max( 3, Math.floor( index.count * ratio / 3 ) * 3 );
- // normals and uvs, when present, contribute to the error metric so collapses
- // that would distort shading or texture seams are penalized
- const normalAttribute = geometry.getAttribute( 'normal' );
- const uvAttribute = geometry.getAttribute( 'uv' );
- const attributeStride = ( normalAttribute ? 3 : 0 ) + ( uvAttribute ? 2 : 0 );
- let indices;
- if ( attributeStride > 0 ) {
- const vertexAttributes = new Float32Array( positionAttribute.count * attributeStride );
- const attributeWeights = [];
- if ( normalAttribute ) attributeWeights.push( 0.25, 0.25, 0.25 );
- if ( uvAttribute ) attributeWeights.push( 0.5, 0.5 );
- for ( let i = 0, o = 0; i < positionAttribute.count; i ++ ) {
- if ( normalAttribute ) {
- vertexAttributes[ o ++ ] = normalAttribute.getX( i );
- vertexAttributes[ o ++ ] = normalAttribute.getY( i );
- vertexAttributes[ o ++ ] = normalAttribute.getZ( i );
- }
- if ( uvAttribute ) {
- vertexAttributes[ o ++ ] = uvAttribute.getX( i );
- vertexAttributes[ o ++ ] = uvAttribute.getY( i );
- }
- }
- indices = MeshoptSimplifier.simplifyWithAttributes( index.array, positions, 3, vertexAttributes, attributeStride, attributeWeights, null, targetIndexCount, 1 )[ 0 ];
- } else {
- indices = MeshoptSimplifier.simplify( index.array, positions, 3, targetIndexCount, 1 )[ 0 ];
- }
- // drop vertices that are no longer referenced and rebuild the attributes
- const [ remap, unique ] = MeshoptSimplifier.compactMesh( indices ); // rewrites indices in place
- const simplifiedGeometry = new BufferGeometry();
- for ( const name in geometry.attributes ) {
- const attribute = geometry.getAttribute( name );
- const itemSize = attribute.itemSize;
- const newAttribute = new BufferAttribute( new attribute.array.constructor( unique * itemSize ), itemSize, attribute.normalized );
- // scatter each vertex to its new slot; 0xffffffff marks unreferenced vertices
- for ( let i = 0; i < remap.length; i ++ ) {
- const target = remap[ i ];
- if ( target === 0xffffffff ) continue;
- for ( let k = 0; k < itemSize; k ++ ) newAttribute.setComponent( target, k, attribute.getComponent( i, k ) );
- }
- simplifiedGeometry.setAttribute( name, newAttribute );
- }
- simplifiedGeometry.setIndex( new BufferAttribute( indices, 1 ) );
- return simplifiedGeometry;
- }
- }
- export { SimplifyModifier };
|