280 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			280 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
									BufferAttribute,
							 | 
						||
| 
								 | 
							
									BufferGeometry,
							 | 
						||
| 
								 | 
							
									Vector3
							 | 
						||
| 
								 | 
							
								} from 'three';
							 | 
						||
| 
								 | 
							
								import * as BufferGeometryUtils from '../utils/BufferGeometryUtils.js';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const _A = new Vector3();
							 | 
						||
| 
								 | 
							
								const _B = new Vector3();
							 | 
						||
| 
								 | 
							
								const _C = new Vector3();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class EdgeSplitModifier {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									modify( geometry, cutOffAngle, tryKeepNormals = true ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										function computeNormals() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											normals = new Float32Array( indexes.length * 3 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( let i = 0; i < indexes.length; i += 3 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												let index = indexes[ i ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												_A.set(
							 | 
						||
| 
								 | 
							
													positions[ 3 * index ],
							 | 
						||
| 
								 | 
							
													positions[ 3 * index + 1 ],
							 | 
						||
| 
								 | 
							
													positions[ 3 * index + 2 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												index = indexes[ i + 1 ];
							 | 
						||
| 
								 | 
							
												_B.set(
							 | 
						||
| 
								 | 
							
													positions[ 3 * index ],
							 | 
						||
| 
								 | 
							
													positions[ 3 * index + 1 ],
							 | 
						||
| 
								 | 
							
													positions[ 3 * index + 2 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												index = indexes[ i + 2 ];
							 | 
						||
| 
								 | 
							
												_C.set(
							 | 
						||
| 
								 | 
							
													positions[ 3 * index ],
							 | 
						||
| 
								 | 
							
													positions[ 3 * index + 1 ],
							 | 
						||
| 
								 | 
							
													positions[ 3 * index + 2 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												_C.sub( _B );
							 | 
						||
| 
								 | 
							
												_A.sub( _B );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const normal = _C.cross( _A ).normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for ( let j = 0; j < 3; j ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													normals[ 3 * ( i + j ) ] = normal.x;
							 | 
						||
| 
								 | 
							
													normals[ 3 * ( i + j ) + 1 ] = normal.y;
							 | 
						||
| 
								 | 
							
													normals[ 3 * ( i + j ) + 2 ] = normal.z;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										function mapPositionsToIndexes() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											pointToIndexMap = Array( positions.length / 3 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( let i = 0; i < indexes.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const index = indexes[ i ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( pointToIndexMap[ index ] == null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													pointToIndexMap[ index ] = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												pointToIndexMap[ index ].push( i );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										function edgeSplitToGroups( indexes, cutOff, firstIndex ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											_A.set( normals[ 3 * firstIndex ], normals[ 3 * firstIndex + 1 ], normals[ 3 * firstIndex + 2 ] ).normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const result = {
							 | 
						||
| 
								 | 
							
												splitGroup: [],
							 | 
						||
| 
								 | 
							
												currentGroup: [ firstIndex ]
							 | 
						||
| 
								 | 
							
											};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( const j of indexes ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( j !== firstIndex ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													_B.set( normals[ 3 * j ], normals[ 3 * j + 1 ], normals[ 3 * j + 2 ] ).normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if ( _B.dot( _A ) < cutOff ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														result.splitGroup.push( j );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														result.currentGroup.push( j );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return result;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										function edgeSplit( indexes, cutOff, original = null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( indexes.length === 0 ) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const groupResults = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( const index of indexes ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												groupResults.push( edgeSplitToGroups( indexes, cutOff, index ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											let result = groupResults[ 0 ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( const groupResult of groupResults ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( groupResult.currentGroup.length > result.currentGroup.length ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													result = groupResult;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( original != null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												splitIndexes.push( {
							 | 
						||
| 
								 | 
							
													original: original,
							 | 
						||
| 
								 | 
							
													indexes: result.currentGroup
							 | 
						||
| 
								 | 
							
												} );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( result.splitGroup.length ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												edgeSplit( result.splitGroup, cutOff, original || result.currentGroup[ 0 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let hadNormals = false;
							 | 
						||
| 
								 | 
							
										let oldNormals = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( geometry.attributes.normal ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											hadNormals = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											geometry = geometry.clone();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( tryKeepNormals === true && geometry.index !== null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												oldNormals = geometry.attributes.normal.array;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											geometry.deleteAttribute( 'normal' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( geometry.index == null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											geometry = BufferGeometryUtils.mergeVertices( geometry );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const indexes = geometry.index.array;
							 | 
						||
| 
								 | 
							
										const positions = geometry.getAttribute( 'position' ).array;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let normals;
							 | 
						||
| 
								 | 
							
										let pointToIndexMap;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										computeNormals();
							 | 
						||
| 
								 | 
							
										mapPositionsToIndexes();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const splitIndexes = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( const vertexIndexes of pointToIndexMap ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											edgeSplit( vertexIndexes, Math.cos( cutOffAngle ) - 0.001 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const newAttributes = {};
							 | 
						||
| 
								 | 
							
										for ( const name of Object.keys( geometry.attributes ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const oldAttribute = geometry.attributes[ name ];
							 | 
						||
| 
								 | 
							
											const newArray = new oldAttribute.array.constructor( ( indexes.length + splitIndexes.length ) * oldAttribute.itemSize );
							 | 
						||
| 
								 | 
							
											newArray.set( oldAttribute.array );
							 | 
						||
| 
								 | 
							
											newAttributes[ name ] = new BufferAttribute( newArray, oldAttribute.itemSize, oldAttribute.normalized );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const newIndexes = new Uint32Array( indexes.length );
							 | 
						||
| 
								 | 
							
										newIndexes.set( indexes );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( let i = 0; i < splitIndexes.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const split = splitIndexes[ i ];
							 | 
						||
| 
								 | 
							
											const index = indexes[ split.original ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( const attribute of Object.values( newAttributes ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for ( let j = 0; j < attribute.itemSize; j ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													attribute.array[ ( indexes.length + i ) * attribute.itemSize + j ] =
							 | 
						||
| 
								 | 
							
														attribute.array[ index * attribute.itemSize + j ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for ( const j of split.indexes ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												newIndexes[ j ] = indexes.length + i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										geometry = new BufferGeometry();
							 | 
						||
| 
								 | 
							
										geometry.setIndex( new BufferAttribute( newIndexes, 1 ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( const name of Object.keys( newAttributes ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											geometry.setAttribute( name, newAttributes[ name ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( hadNormals ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											geometry.computeVertexNormals();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( oldNormals !== null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												const changedNormals = new Array( oldNormals.length / 3 ).fill( false );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for ( const splitData of splitIndexes )
							 | 
						||
| 
								 | 
							
													changedNormals[ splitData.original ] = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for ( let i = 0; i < changedNormals.length; i ++ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if ( changedNormals[ i ] === false ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														for ( let j = 0; j < 3; j ++ )
							 | 
						||
| 
								 | 
							
															geometry.attributes.normal.array[ 3 * i + j ] = oldNormals[ 3 * i + j ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return geometry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export { EdgeSplitModifier };
							 |