Skip to content

⬅️ Back to Table of Contents

📄 WebGLMorphtargets.js

📊 Analysis Summary

Metric Count
🔧 Functions 3
📦 Imports 4
📊 Variables & Constants 24

📚 Table of Contents

🛠️ File Location:

📂 src/renderers/webgl/WebGLMorphtargets.js

📦 Imports

Name Source
FloatType ../../constants.js
DataArrayTexture ../../textures/DataArrayTexture.js
Vector4 ../../math/Vector4.js
Vector2 ../../math/Vector2.js

Variables & Constants

Name Type Kind Value Exported
morphTextures WeakMap<WeakKey, any> let/var new WeakMap()
morph Vector4 let/var new Vector4()
objectInfluences any let/var object.morphTargetInfluences
morphAttribute any let/var geometry.morphAttributes.position \|\| geometry.morphAttributes.normal \|\| g...
morphTargetsCount any let/var ( morphAttribute !== undefined ) ? morphAttribute.length : 0
hasMorphPosition boolean let/var geometry.morphAttributes.position !== undefined
hasMorphNormals boolean let/var geometry.morphAttributes.normal !== undefined
hasMorphColors boolean let/var geometry.morphAttributes.color !== undefined
morphTargets any let/var geometry.morphAttributes.position \|\| []
morphNormals any let/var geometry.morphAttributes.normal \|\| []
morphColors any let/var geometry.morphAttributes.color \|\| []
vertexDataCount number let/var 0
width number let/var geometry.attributes.position.count * vertexDataCount
height number let/var 1
buffer Float32Array<ArrayBuffer> let/var new Float32Array( width * height * 4 * morphTargetsCount )
texture DataArrayTexture let/var new DataArrayTexture( buffer, width, height, morphTargetsCount )
vertexDataStride number let/var vertexDataCount * 4
morphTarget any let/var morphTargets[ i ]
morphNormal any let/var morphNormals[ i ]
morphColor any let/var morphColors[ i ]
offset number let/var width * height * 4 * i
stride number let/var j * vertexDataStride
morphInfluencesSum number let/var 0
morphBaseInfluence number let/var geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum

Functions

WebGLMorphtargets(gl: any, capabilities: any, textures: any): { update: (object: any, geometry: any, program: any) => void; }

Parameters:

  • gl any
  • capabilities any
  • textures any

Returns: { update: (object: any, geometry: any, program: any) => void; }

Calls:

  • morphTextures.get
  • entry.texture.dispose
  • Math.ceil
  • morph.fromBufferAttribute
  • morphTextures.set
  • texture.dispose
  • morphTextures.delete
  • geometry.removeEventListener
  • geometry.addEventListener
  • program.getUniforms().setValue

Internal Comments:

// the following encodes morph targets into an array of data textures. Each layer represents a single morph target. (x2)
// fill buffer (x2)
//

Code
function WebGLMorphtargets( gl, capabilities, textures ) {

    const morphTextures = new WeakMap();
    const morph = new Vector4();

    function update( object, geometry, program ) {

        const objectInfluences = object.morphTargetInfluences;

        // the following encodes morph targets into an array of data textures. Each layer represents a single morph target.

        const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;
        const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;

        let entry = morphTextures.get( geometry );

        if ( entry === undefined || entry.count !== morphTargetsCount ) {

            if ( entry !== undefined ) entry.texture.dispose();

            const hasMorphPosition = geometry.morphAttributes.position !== undefined;
            const hasMorphNormals = geometry.morphAttributes.normal !== undefined;
            const hasMorphColors = geometry.morphAttributes.color !== undefined;

            const morphTargets = geometry.morphAttributes.position || [];
            const morphNormals = geometry.morphAttributes.normal || [];
            const morphColors = geometry.morphAttributes.color || [];

            let vertexDataCount = 0;

            if ( hasMorphPosition === true ) vertexDataCount = 1;
            if ( hasMorphNormals === true ) vertexDataCount = 2;
            if ( hasMorphColors === true ) vertexDataCount = 3;

            let width = geometry.attributes.position.count * vertexDataCount;
            let height = 1;

            if ( width > capabilities.maxTextureSize ) {

                height = Math.ceil( width / capabilities.maxTextureSize );
                width = capabilities.maxTextureSize;

            }

            const buffer = new Float32Array( width * height * 4 * morphTargetsCount );

            const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount );
            texture.type = FloatType;
            texture.needsUpdate = true;

            // fill buffer

            const vertexDataStride = vertexDataCount * 4;

            for ( let i = 0; i < morphTargetsCount; i ++ ) {

                const morphTarget = morphTargets[ i ];
                const morphNormal = morphNormals[ i ];
                const morphColor = morphColors[ i ];

                const offset = width * height * 4 * i;

                for ( let j = 0; j < morphTarget.count; j ++ ) {

                    const stride = j * vertexDataStride;

                    if ( hasMorphPosition === true ) {

                        morph.fromBufferAttribute( morphTarget, j );

                        buffer[ offset + stride + 0 ] = morph.x;
                        buffer[ offset + stride + 1 ] = morph.y;
                        buffer[ offset + stride + 2 ] = morph.z;
                        buffer[ offset + stride + 3 ] = 0;

                    }

                    if ( hasMorphNormals === true ) {

                        morph.fromBufferAttribute( morphNormal, j );

                        buffer[ offset + stride + 4 ] = morph.x;
                        buffer[ offset + stride + 5 ] = morph.y;
                        buffer[ offset + stride + 6 ] = morph.z;
                        buffer[ offset + stride + 7 ] = 0;

                    }

                    if ( hasMorphColors === true ) {

                        morph.fromBufferAttribute( morphColor, j );

                        buffer[ offset + stride + 8 ] = morph.x;
                        buffer[ offset + stride + 9 ] = morph.y;
                        buffer[ offset + stride + 10 ] = morph.z;
                        buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1;

                    }

                }

            }

            entry = {
                count: morphTargetsCount,
                texture: texture,
                size: new Vector2( width, height )
            };

            morphTextures.set( geometry, entry );

            function disposeTexture() {

                texture.dispose();

                morphTextures.delete( geometry );

                geometry.removeEventListener( 'dispose', disposeTexture );

            }

            geometry.addEventListener( 'dispose', disposeTexture );

        }

        //
        if ( object.isInstancedMesh === true && object.morphTexture !== null ) {

            program.getUniforms().setValue( gl, 'morphTexture', object.morphTexture, textures );

        } else {

            let morphInfluencesSum = 0;

            for ( let i = 0; i < objectInfluences.length; i ++ ) {

                morphInfluencesSum += objectInfluences[ i ];

            }

            const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;


            program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );
            program.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences );

        }

        program.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures );
        program.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size );

    }

    return {

        update: update

    };

}

update(object: any, geometry: any, program: any): void

Parameters:

  • object any
  • geometry any
  • program any

Returns: void

Calls:

  • morphTextures.get
  • entry.texture.dispose
  • Math.ceil
  • morph.fromBufferAttribute
  • morphTextures.set
  • texture.dispose
  • morphTextures.delete
  • geometry.removeEventListener
  • geometry.addEventListener
  • program.getUniforms().setValue

Internal Comments:

// the following encodes morph targets into an array of data textures. Each layer represents a single morph target. (x2)
// fill buffer (x2)
//

Code
function update( object, geometry, program ) {

        const objectInfluences = object.morphTargetInfluences;

        // the following encodes morph targets into an array of data textures. Each layer represents a single morph target.

        const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;
        const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;

        let entry = morphTextures.get( geometry );

        if ( entry === undefined || entry.count !== morphTargetsCount ) {

            if ( entry !== undefined ) entry.texture.dispose();

            const hasMorphPosition = geometry.morphAttributes.position !== undefined;
            const hasMorphNormals = geometry.morphAttributes.normal !== undefined;
            const hasMorphColors = geometry.morphAttributes.color !== undefined;

            const morphTargets = geometry.morphAttributes.position || [];
            const morphNormals = geometry.morphAttributes.normal || [];
            const morphColors = geometry.morphAttributes.color || [];

            let vertexDataCount = 0;

            if ( hasMorphPosition === true ) vertexDataCount = 1;
            if ( hasMorphNormals === true ) vertexDataCount = 2;
            if ( hasMorphColors === true ) vertexDataCount = 3;

            let width = geometry.attributes.position.count * vertexDataCount;
            let height = 1;

            if ( width > capabilities.maxTextureSize ) {

                height = Math.ceil( width / capabilities.maxTextureSize );
                width = capabilities.maxTextureSize;

            }

            const buffer = new Float32Array( width * height * 4 * morphTargetsCount );

            const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount );
            texture.type = FloatType;
            texture.needsUpdate = true;

            // fill buffer

            const vertexDataStride = vertexDataCount * 4;

            for ( let i = 0; i < morphTargetsCount; i ++ ) {

                const morphTarget = morphTargets[ i ];
                const morphNormal = morphNormals[ i ];
                const morphColor = morphColors[ i ];

                const offset = width * height * 4 * i;

                for ( let j = 0; j < morphTarget.count; j ++ ) {

                    const stride = j * vertexDataStride;

                    if ( hasMorphPosition === true ) {

                        morph.fromBufferAttribute( morphTarget, j );

                        buffer[ offset + stride + 0 ] = morph.x;
                        buffer[ offset + stride + 1 ] = morph.y;
                        buffer[ offset + stride + 2 ] = morph.z;
                        buffer[ offset + stride + 3 ] = 0;

                    }

                    if ( hasMorphNormals === true ) {

                        morph.fromBufferAttribute( morphNormal, j );

                        buffer[ offset + stride + 4 ] = morph.x;
                        buffer[ offset + stride + 5 ] = morph.y;
                        buffer[ offset + stride + 6 ] = morph.z;
                        buffer[ offset + stride + 7 ] = 0;

                    }

                    if ( hasMorphColors === true ) {

                        morph.fromBufferAttribute( morphColor, j );

                        buffer[ offset + stride + 8 ] = morph.x;
                        buffer[ offset + stride + 9 ] = morph.y;
                        buffer[ offset + stride + 10 ] = morph.z;
                        buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1;

                    }

                }

            }

            entry = {
                count: morphTargetsCount,
                texture: texture,
                size: new Vector2( width, height )
            };

            morphTextures.set( geometry, entry );

            function disposeTexture() {

                texture.dispose();

                morphTextures.delete( geometry );

                geometry.removeEventListener( 'dispose', disposeTexture );

            }

            geometry.addEventListener( 'dispose', disposeTexture );

        }

        //
        if ( object.isInstancedMesh === true && object.morphTexture !== null ) {

            program.getUniforms().setValue( gl, 'morphTexture', object.morphTexture, textures );

        } else {

            let morphInfluencesSum = 0;

            for ( let i = 0; i < objectInfluences.length; i ++ ) {

                morphInfluencesSum += objectInfluences[ i ];

            }

            const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;


            program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );
            program.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences );

        }

        program.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures );
        program.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size );

    }

disposeTexture(): void

Returns: void

Calls:

  • texture.dispose
  • morphTextures.delete
  • geometry.removeEventListener
Code
function disposeTexture() {

                texture.dispose();

                morphTextures.delete( geometry );

                geometry.removeEventListener( 'dispose', disposeTexture );

            }