Skip to content

⬅️ Back to Table of Contents

📄 LOD.js

📊 Analysis Summary

Metric Count
🔧 Functions 8
🧱 Classes 1
📦 Imports 2
📊 Variables & Constants 19

📚 Table of Contents

🛠️ File Location:

📂 src/objects/LOD.js

📦 Imports

Name Source
Vector3 ../math/Vector3.js
Object3D ../core/Object3D.js

Variables & Constants

Name Type Kind Value Exported
_v1 Vector3 let/var new Vector3()
_v2 Vector3 let/var new Vector3()
levels any let/var source.levels
level any let/var levels[ i ]
levels any let/var this.levels
l any let/var *not shown*
levels any let/var this.levels
levels any let/var this.levels
i any let/var *not shown*
l any let/var *not shown*
levelDistance any let/var levels[ i ].distance
levels any let/var this.levels
levels any let/var this.levels
distance number let/var _v1.distanceTo( _v2 ) / camera.zoom
i any let/var *not shown*
l any let/var *not shown*
levelDistance any let/var levels[ i ].distance
levels any let/var this.levels
level any let/var levels[ i ]

Functions

LOD.copy(source: any): this

Parameters:

  • source any

Returns: this

Calls:

  • super.copy
  • this.addLevel
  • level.object.clone
Code
copy( source ) {

        super.copy( source, false );

        const levels = source.levels;

        for ( let i = 0, l = levels.length; i < l; i ++ ) {

            const level = levels[ i ];

            this.addLevel( level.object.clone(), level.distance, level.hysteresis );

        }

        this.autoUpdate = source.autoUpdate;

        return this;

    }

LOD.addLevel(object: Object3D, distance: number, hysteresis: number): LOD

JSDoc:

/**
     * Adds a mesh that will display at a certain distance and greater. Typically
     * the further away the distance, the lower the detail on the mesh.
     *
     * @param {Object3D} object - The 3D object to display at this level.
     * @param {number} [distance=0] - The distance at which to display this level of detail.
     * @param {number} [hysteresis=0] - Threshold used to avoid flickering at LOD boundaries, as a fraction of distance.
     * @return {LOD} A reference to this instance.
     */

Parameters:

  • object Object3D
  • distance number
  • hysteresis number

Returns: LOD

Calls:

  • Math.abs
  • levels.splice
  • this.add
Code
addLevel( object, distance = 0, hysteresis = 0 ) {

        distance = Math.abs( distance );

        const levels = this.levels;

        let l;

        for ( l = 0; l < levels.length; l ++ ) {

            if ( distance < levels[ l ].distance ) {

                break;

            }

        }

        levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } );

        this.add( object );

        return this;

    }

LOD.removeLevel(distance: number): boolean

JSDoc:

/**
     * Removes an existing level, based on the distance from the camera.
     * Returns `true` when the level has been removed. Otherwise `false`.
     *
     * @param {number} distance - Distance of the level to remove.
     * @return {boolean} Whether the level has been removed or not.
     */

Parameters:

  • distance number

Returns: boolean

Calls:

  • levels.splice
  • this.remove
Code
removeLevel( distance ) {

        const levels = this.levels;

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

            if ( levels[ i ].distance === distance ) {

                const removedElements = levels.splice( i, 1 );
                this.remove( removedElements[ 0 ].object );

                return true;

            }

        }

        return false;

    }

LOD.getCurrentLevel(): number

JSDoc:

/**
     * Returns the currently active LOD level index.
     *
     * @return {number} The current active LOD level index.
     */

Returns: number

Code
getCurrentLevel() {

        return this._currentLevel;

    }

LOD.getObjectForDistance(distance: number): Object3D

JSDoc:

/**
     * Returns a reference to the first 3D object that is greater than
     * the given distance.
     *
     * @param {number} distance - The LOD distance.
     * @return {Object3D|null} The found 3D object. `null` if no 3D object has been found.
     */

Parameters:

  • distance number

Returns: Object3D

Code
getObjectForDistance( distance ) {

        const levels = this.levels;

        if ( levels.length > 0 ) {

            let i, l;

            for ( i = 1, l = levels.length; i < l; i ++ ) {

                let levelDistance = levels[ i ].distance;

                if ( levels[ i ].object.visible ) {

                    levelDistance -= levelDistance * levels[ i ].hysteresis;

                }

                if ( distance < levelDistance ) {

                    break;

                }

            }

            return levels[ i - 1 ].object;

        }

        return null;

    }

LOD.raycast(raycaster: Raycaster, intersects: any[]): void

JSDoc:

/**
     * Computes intersection points between a casted ray and this LOD.
     *
     * @param {Raycaster} raycaster - The raycaster.
     * @param {Array<Object>} intersects - The target array that holds the intersection points.
     */

Parameters:

  • raycaster Raycaster
  • intersects any[]

Returns: void

Calls:

  • _v1.setFromMatrixPosition
  • raycaster.ray.origin.distanceTo
  • this.getObjectForDistance( distance ).raycast
Code
raycast( raycaster, intersects ) {

        const levels = this.levels;

        if ( levels.length > 0 ) {

            _v1.setFromMatrixPosition( this.matrixWorld );

            const distance = raycaster.ray.origin.distanceTo( _v1 );

            this.getObjectForDistance( distance ).raycast( raycaster, intersects );

        }

    }

LOD.update(camera: Camera): void

JSDoc:

/**
     * Updates the LOD by computing which LOD level should be visible according
     * to the current distance of the given camera.
     *
     * @param {Camera} camera - The camera the scene is rendered with.
     */

Parameters:

  • camera Camera

Returns: void

Calls:

  • _v1.setFromMatrixPosition
  • _v2.setFromMatrixPosition
  • _v1.distanceTo
Code
update( camera ) {

        const levels = this.levels;

        if ( levels.length > 1 ) {

            _v1.setFromMatrixPosition( camera.matrixWorld );
            _v2.setFromMatrixPosition( this.matrixWorld );

            const distance = _v1.distanceTo( _v2 ) / camera.zoom;

            levels[ 0 ].object.visible = true;

            let i, l;

            for ( i = 1, l = levels.length; i < l; i ++ ) {

                let levelDistance = levels[ i ].distance;

                if ( levels[ i ].object.visible ) {

                    levelDistance -= levelDistance * levels[ i ].hysteresis;

                }

                if ( distance >= levelDistance ) {

                    levels[ i - 1 ].object.visible = false;
                    levels[ i ].object.visible = true;

                } else {

                    break;

                }

            }

            this._currentLevel = i - 1;

            for ( ; i < l; i ++ ) {

                levels[ i ].object.visible = false;

            }

        }

    }

LOD.toJSON(meta: any): any

Parameters:

  • meta any

Returns: any

Calls:

  • super.toJSON
  • data.object.levels.push
Code
toJSON( meta ) {

        const data = super.toJSON( meta );

        if ( this.autoUpdate === false ) data.object.autoUpdate = false;

        data.object.levels = [];

        const levels = this.levels;

        for ( let i = 0, l = levels.length; i < l; i ++ ) {

            const level = levels[ i ];

            data.object.levels.push( {
                object: level.object.uuid,
                distance: level.distance,
                hysteresis: level.hysteresis
            } );

        }

        return data;

    }

Classes

LOD

Class Code
class LOD extends Object3D {

    /**
     * Constructs a new LOD.
     */
    constructor() {

        super();

        /**
         * This flag can be used for type testing.
         *
         * @type {boolean}
         * @readonly
         * @default true
         */
        this.isLOD = true;

        /**
         * The current LOD index.
         *
         * @private
         * @type {number}
         * @default 0
         */
        this._currentLevel = 0;

        this.type = 'LOD';

        Object.defineProperties( this, {
            /**
             * This array holds the LOD levels.
             *
             * @name LOD#levels
             * @type {Array<{object:Object3D,distance:number,hysteresis:number}>}
             */
            levels: {
                enumerable: true,
                value: []
            }
        } );

        /**
         * Whether the LOD object is updated automatically by the renderer per frame
         * or not. If set to `false`, you have to call {@link LOD#update} in the
         * render loop by yourself.
         *
         * @type {boolean}
         * @default true
         */
        this.autoUpdate = true;

    }

    copy( source ) {

        super.copy( source, false );

        const levels = source.levels;

        for ( let i = 0, l = levels.length; i < l; i ++ ) {

            const level = levels[ i ];

            this.addLevel( level.object.clone(), level.distance, level.hysteresis );

        }

        this.autoUpdate = source.autoUpdate;

        return this;

    }

    /**
     * Adds a mesh that will display at a certain distance and greater. Typically
     * the further away the distance, the lower the detail on the mesh.
     *
     * @param {Object3D} object - The 3D object to display at this level.
     * @param {number} [distance=0] - The distance at which to display this level of detail.
     * @param {number} [hysteresis=0] - Threshold used to avoid flickering at LOD boundaries, as a fraction of distance.
     * @return {LOD} A reference to this instance.
     */
    addLevel( object, distance = 0, hysteresis = 0 ) {

        distance = Math.abs( distance );

        const levels = this.levels;

        let l;

        for ( l = 0; l < levels.length; l ++ ) {

            if ( distance < levels[ l ].distance ) {

                break;

            }

        }

        levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } );

        this.add( object );

        return this;

    }

    /**
     * Removes an existing level, based on the distance from the camera.
     * Returns `true` when the level has been removed. Otherwise `false`.
     *
     * @param {number} distance - Distance of the level to remove.
     * @return {boolean} Whether the level has been removed or not.
     */
    removeLevel( distance ) {

        const levels = this.levels;

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

            if ( levels[ i ].distance === distance ) {

                const removedElements = levels.splice( i, 1 );
                this.remove( removedElements[ 0 ].object );

                return true;

            }

        }

        return false;

    }

    /**
     * Returns the currently active LOD level index.
     *
     * @return {number} The current active LOD level index.
     */
    getCurrentLevel() {

        return this._currentLevel;

    }

    /**
     * Returns a reference to the first 3D object that is greater than
     * the given distance.
     *
     * @param {number} distance - The LOD distance.
     * @return {Object3D|null} The found 3D object. `null` if no 3D object has been found.
     */
    getObjectForDistance( distance ) {

        const levels = this.levels;

        if ( levels.length > 0 ) {

            let i, l;

            for ( i = 1, l = levels.length; i < l; i ++ ) {

                let levelDistance = levels[ i ].distance;

                if ( levels[ i ].object.visible ) {

                    levelDistance -= levelDistance * levels[ i ].hysteresis;

                }

                if ( distance < levelDistance ) {

                    break;

                }

            }

            return levels[ i - 1 ].object;

        }

        return null;

    }

    /**
     * Computes intersection points between a casted ray and this LOD.
     *
     * @param {Raycaster} raycaster - The raycaster.
     * @param {Array<Object>} intersects - The target array that holds the intersection points.
     */
    raycast( raycaster, intersects ) {

        const levels = this.levels;

        if ( levels.length > 0 ) {

            _v1.setFromMatrixPosition( this.matrixWorld );

            const distance = raycaster.ray.origin.distanceTo( _v1 );

            this.getObjectForDistance( distance ).raycast( raycaster, intersects );

        }

    }

    /**
     * Updates the LOD by computing which LOD level should be visible according
     * to the current distance of the given camera.
     *
     * @param {Camera} camera - The camera the scene is rendered with.
     */
    update( camera ) {

        const levels = this.levels;

        if ( levels.length > 1 ) {

            _v1.setFromMatrixPosition( camera.matrixWorld );
            _v2.setFromMatrixPosition( this.matrixWorld );

            const distance = _v1.distanceTo( _v2 ) / camera.zoom;

            levels[ 0 ].object.visible = true;

            let i, l;

            for ( i = 1, l = levels.length; i < l; i ++ ) {

                let levelDistance = levels[ i ].distance;

                if ( levels[ i ].object.visible ) {

                    levelDistance -= levelDistance * levels[ i ].hysteresis;

                }

                if ( distance >= levelDistance ) {

                    levels[ i - 1 ].object.visible = false;
                    levels[ i ].object.visible = true;

                } else {

                    break;

                }

            }

            this._currentLevel = i - 1;

            for ( ; i < l; i ++ ) {

                levels[ i ].object.visible = false;

            }

        }

    }

    toJSON( meta ) {

        const data = super.toJSON( meta );

        if ( this.autoUpdate === false ) data.object.autoUpdate = false;

        data.object.levels = [];

        const levels = this.levels;

        for ( let i = 0, l = levels.length; i < l; i ++ ) {

            const level = levels[ i ];

            data.object.levels.push( {
                object: level.object.uuid,
                distance: level.distance,
                hysteresis: level.hysteresis
            } );

        }

        return data;

    }

}

Methods

copy(source: any): this
Code
copy( source ) {

        super.copy( source, false );

        const levels = source.levels;

        for ( let i = 0, l = levels.length; i < l; i ++ ) {

            const level = levels[ i ];

            this.addLevel( level.object.clone(), level.distance, level.hysteresis );

        }

        this.autoUpdate = source.autoUpdate;

        return this;

    }
addLevel(object: Object3D, distance: number, hysteresis: number): LOD
Code
addLevel( object, distance = 0, hysteresis = 0 ) {

        distance = Math.abs( distance );

        const levels = this.levels;

        let l;

        for ( l = 0; l < levels.length; l ++ ) {

            if ( distance < levels[ l ].distance ) {

                break;

            }

        }

        levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } );

        this.add( object );

        return this;

    }
removeLevel(distance: number): boolean
Code
removeLevel( distance ) {

        const levels = this.levels;

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

            if ( levels[ i ].distance === distance ) {

                const removedElements = levels.splice( i, 1 );
                this.remove( removedElements[ 0 ].object );

                return true;

            }

        }

        return false;

    }
getCurrentLevel(): number
Code
getCurrentLevel() {

        return this._currentLevel;

    }
getObjectForDistance(distance: number): Object3D
Code
getObjectForDistance( distance ) {

        const levels = this.levels;

        if ( levels.length > 0 ) {

            let i, l;

            for ( i = 1, l = levels.length; i < l; i ++ ) {

                let levelDistance = levels[ i ].distance;

                if ( levels[ i ].object.visible ) {

                    levelDistance -= levelDistance * levels[ i ].hysteresis;

                }

                if ( distance < levelDistance ) {

                    break;

                }

            }

            return levels[ i - 1 ].object;

        }

        return null;

    }
raycast(raycaster: Raycaster, intersects: any[]): void
Code
raycast( raycaster, intersects ) {

        const levels = this.levels;

        if ( levels.length > 0 ) {

            _v1.setFromMatrixPosition( this.matrixWorld );

            const distance = raycaster.ray.origin.distanceTo( _v1 );

            this.getObjectForDistance( distance ).raycast( raycaster, intersects );

        }

    }
update(camera: Camera): void
Code
update( camera ) {

        const levels = this.levels;

        if ( levels.length > 1 ) {

            _v1.setFromMatrixPosition( camera.matrixWorld );
            _v2.setFromMatrixPosition( this.matrixWorld );

            const distance = _v1.distanceTo( _v2 ) / camera.zoom;

            levels[ 0 ].object.visible = true;

            let i, l;

            for ( i = 1, l = levels.length; i < l; i ++ ) {

                let levelDistance = levels[ i ].distance;

                if ( levels[ i ].object.visible ) {

                    levelDistance -= levelDistance * levels[ i ].hysteresis;

                }

                if ( distance >= levelDistance ) {

                    levels[ i - 1 ].object.visible = false;
                    levels[ i ].object.visible = true;

                } else {

                    break;

                }

            }

            this._currentLevel = i - 1;

            for ( ; i < l; i ++ ) {

                levels[ i ].object.visible = false;

            }

        }

    }
toJSON(meta: any): any
Code
toJSON( meta ) {

        const data = super.toJSON( meta );

        if ( this.autoUpdate === false ) data.object.autoUpdate = false;

        data.object.levels = [];

        const levels = this.levels;

        for ( let i = 0, l = levels.length; i < l; i ++ ) {

            const level = levels[ i ];

            data.object.levels.push( {
                object: level.object.uuid,
                distance: level.distance,
                hysteresis: level.hysteresis
            } );

        }

        return data;

    }