Spaces:
Running
Running
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 }; | |