Accueil / npm / three@0.186.1 / examples / jsm / modifiers / SimplifyModifier.js

SimplifyModifier.js

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 };

Exemple