three
Version:
JavaScript 3D library
152 lines (82 loc) • 3.4 kB
JavaScript
function numericalSort( a, b ) {
return a[ 0 ] - b[ 0 ];
}
function absNumericalSort( a, b ) {
return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
}
function WebGLMorphtargets( gl ) {
const influencesList = {};
const morphInfluences = new Float32Array( 8 );
const workInfluences = [];
for ( let i = 0; i < 8; i ++ ) {
workInfluences[ i ] = [ i, 0 ];
}
function update( object, geometry, material, program ) {
const objectInfluences = object.morphTargetInfluences;
// When object doesn't have morph target influences defined, we treat it as a 0-length array
// This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences
const length = objectInfluences === undefined ? 0 : objectInfluences.length;
let influences = influencesList[ geometry.id ];
if ( influences === undefined || influences.length !== length ) {
// initialise list
influences = [];
for ( let i = 0; i < length; i ++ ) {
influences[ i ] = [ i, 0 ];
}
influencesList[ geometry.id ] = influences;
}
// Collect influences
for ( let i = 0; i < length; i ++ ) {
const influence = influences[ i ];
influence[ 0 ] = i;
influence[ 1 ] = objectInfluences[ i ];
}
influences.sort( absNumericalSort );
for ( let i = 0; i < 8; i ++ ) {
if ( i < length && influences[ i ][ 1 ] ) {
workInfluences[ i ][ 0 ] = influences[ i ][ 0 ];
workInfluences[ i ][ 1 ] = influences[ i ][ 1 ];
} else {
workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER;
workInfluences[ i ][ 1 ] = 0;
}
}
workInfluences.sort( numericalSort );
const morphTargets = material.morphTargets && geometry.morphAttributes.position;
const morphNormals = material.morphNormals && geometry.morphAttributes.normal;
let morphInfluencesSum = 0;
for ( let i = 0; i < 8; i ++ ) {
const influence = workInfluences[ i ];
const index = influence[ 0 ];
const value = influence[ 1 ];
if ( index !== Number.MAX_SAFE_INTEGER && value ) {
if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) {
geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] );
}
if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) {
geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] );
}
morphInfluences[ i ] = value;
morphInfluencesSum += value;
} else {
if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) {
geometry.deleteAttribute( 'morphTarget' + i );
}
if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) {
geometry.deleteAttribute( 'morphNormal' + i );
}
morphInfluences[ i ] = 0;
}
}
// GLSL shader uses formula baseinfluence * base + sum(target * influence)
// This allows us to switch between absolute morphs and relative morphs without changing shader code
// When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence)
const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;
program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );
program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );
}
return {
update: update
};
}
export { WebGLMorphtargets };