import { BufferAttribute , BufferGeometry , Vector3 } from "three" ; const tmpVectorA = new Vector3 ( ) ; const tmpVectorB = new Vector3 ( ) ; const tmpVectorC = new Vector3 ( ) ; /**
 * The [[SubdivisionModifier]] subdivides triangle mesh geometries.
 */ export abstract class SubdivisionModifier { /**
     * Constructs a new [[SubdivisionModifier]].
     */ constructor ( ) { } param geometry modify ( geometry ) { const positionAttr = geometry . getAttribute ( "position" ) as BufferAttribute ; const position = Array . from ( positionAttr . array ) ; const uvAttr = geometry . getAttribute ( "uv" ) as BufferAttribute ; const uv = uvAttr !== undefined ? Array . from ( uvAttr . array ) : undefined ; const edgeAttr = geometry . getAttribute ( "edge" ) as BufferAttribute ; const edge = edgeAttr !== undefined ? Array . from ( edgeAttr . array ) : undefined ; const wallAttr = geometry . getAttribute ( "wall" ) as BufferAttribute ; const wall = wallAttr !== undefined ? Array . from ( wallAttr . array ) : undefined ; const indexAttr = geometry . getIndex ( ) as BufferAttribute ; const indices = Array . from ( indexAttr . array ) ; const cache = new Map < string , number > ( ) ; /**
         * Returns the index of the vertex positioned in the middle of the given vertices.
         */ function middleVertex ( i , j ) { const key = `${ Math . min ( i , j ) }_${ Math . max ( i , j ) }` ; const h = cache . get ( key ) ; if ( h !== undefined ) { return h ; } tmpVectorA . set ( position [ i * 3 ] , position [ i * 3 + 1 ] , position [ i * 3 + 2 ] ) ; tmpVectorB . set ( position [ j * 3 ] , position [ j * 3 + 1 ] , position [ j * 3 + 2 ] ) ; tmpVectorC . lerpVectors ( tmpVectorA , tmpVectorB , 0.5 ) ; const index = position . length / 3 ; position . push ( ... tmpVectorC . toArray ( ) ) ; cache . set ( key , index ) ; if ( uv !== undefined ) { tmpVectorA . set ( uv [ i * 2 ] , uv [ i * 2 + 1 ] , 0 ) ; tmpVectorB . set ( uv [ j * 2 ] , uv [ j * 2 + 1 ] , 0 ) ; tmpVectorC . lerpVectors ( tmpVectorA , tmpVectorB , 0.5 ) ; uv . push ( tmpVectorC . x , tmpVectorC . y ) ; } if ( edge !== undefined ) { if ( edge [ i ] === j ) { edge . push ( j ) ; edge [ i ] = index ; } else if ( edge [ j ] === i ) { edge . push ( i ) ; edge [ j ] = index ; } else { edge . push ( - 1 ) ; } } if ( wall !== undefined ) { if ( wall [ i ] === j ) { wall . push ( j ) ; wall [ i ] = index ; } else if ( wall [ j ] === i ) { wall . push ( i ) ; wall [ j ] = index ; } else { wall . push ( - 1 ) ; } } return index ; } const newIndices = [ ] ; while ( indices . length >= 3 ) { const v0 = indices . shift ( ) ! ; const v1 = indices . shift ( ) ! ; const v2 = indices . shift ( ) ! ; tmpVectorA . set ( position [ v0 * 3 ] , position [ v0 * 3 + 1 ] , position [ v0 * 3 + 2 ] ) ; tmpVectorB . set ( position [ v1 * 3 ] , position [ v1 * 3 + 1 ] , position [ v1 * 3 + 2 ] ) ; tmpVectorC . set ( position [ v2 * 3 ] , position [ v2 * 3 + 1 ] , position [ v2 * 3 + 2 ] ) ; const edgeToSplit = this . shouldSplitTriangle ( tmpVectorA , tmpVectorB , tmpVectorC ) ; switch ( edgeToSplit ) { case 0 : { const v3 = middleVertex ( v0 , v1 ) ; indices . push ( v0 , v3 , v2 , v3 , v1 , v2 ) ; break ; } case 1 : { const v3 = middleVertex ( v1 , v2 ) ; indices . push ( v0 , v1 , v3 , v0 , v3 , v2 ) ; break ; } case 2 : { const v3 = middleVertex ( v2 , v0 ) ; indices . push ( v0 , v1 , v3 , v3 , v1 , v2 ) ; break ; } case undefined : { newIndices . push ( v0 , v1 , v2 ) ; break ; } default : throw new Error ( "failed to subdivide the given geometry" ) ; } } positionAttr . array = positionAttr . array instanceof Float32Array ? new Float32Array ( position ) : new Float64Array ( position ) ; positionAttr . count = position . length / positionAttr . itemSize ; positionAttr . needsUpdate = true ; geometry . setIndex ( newIndices ) ; if ( uv !== undefined ) { uvAttr . array = new Float32Array ( uv ) ; uvAttr . count = uv . length / uvAttr . itemSize ; uvAttr . needsUpdate = true ; } if ( edge !== undefined ) { edgeAttr . array = new Float32Array ( edge ) ; edgeAttr . count = edge . length / edgeAttr . itemSize ; edgeAttr . needsUpdate = true ; } return geometry ; } param a param b param c protected abstract shouldSplitTriangle ( a , b , c ) ; }