Skip to content

⬅️ Back to Table of Contents

📄 ClippingNode.js

📊 Analysis Summary

Metric Count
🔧 Functions 7
🧱 Classes 1
📦 Imports 11
📊 Variables & Constants 6

📚 Table of Contents

🛠️ File Location:

📂 src/nodes/accessors/ClippingNode.js

📦 Imports

Name Source
Node ../core/Node.js
nodeObject ../tsl/TSLBase.js
Fn ../tsl/TSLBase.js
bool ../tsl/TSLBase.js
float ../tsl/TSLBase.js
positionView ./Position.js
diffuseColor ../core/PropertyNode.js
Loop ../utils/LoopNode.js
smoothstep ../math/MathNode.js
uniformArray ./UniformArrayNode.js
builtin ./BuiltinNode.js

Variables & Constants

Name Type Kind Value Exported
clippingContext any let/var builder.clippingContext
numUnionPlanes number let/var unionPlanes.length
numIntersectionPlanes number let/var intersectionPlanes.length
numUnionPlanes number let/var unionPlanes.length
numIntersectionPlanes number let/var intersectionPlanes.length
numUnionPlanes number let/var unionPlanes.length

Functions

ClippingNode.setup(builder: NodeBuilder): Node

JSDoc:

/**
     * Setups the node depending on the selected scope.
     *
     * @param {NodeBuilder} builder - The current node builder.
     * @return {Node} The result node.
     */

Parameters:

  • builder NodeBuilder

Returns: Node

Calls:

  • super.setup
  • this.setupAlphaToCoverage
  • this.setupHardwareClipping
  • this.setupDefault
Code
setup( builder ) {

        super.setup( builder );

        const clippingContext = builder.clippingContext;
        const { intersectionPlanes, unionPlanes } = clippingContext;

        this.hardwareClipping = builder.material.hardwareClipping;

        if ( this.scope === ClippingNode.ALPHA_TO_COVERAGE ) {

            return this.setupAlphaToCoverage( intersectionPlanes, unionPlanes );

        } else if ( this.scope === ClippingNode.HARDWARE ) {

            return this.setupHardwareClipping( unionPlanes, builder );

        } else {

            return this.setupDefault( intersectionPlanes, unionPlanes );

        }

    }

ClippingNode.setupAlphaToCoverage(intersectionPlanes: Vector4[], unionPlanes: Vector4[]): Node

JSDoc:

/**
     * Setups alpha to coverage.
     *
     * @param {Array<Vector4>} intersectionPlanes - The intersection planes.
     * @param {Array<Vector4>} unionPlanes - The union planes.
     * @return {Node} The result node.
     */

Parameters:

  • intersectionPlanes Vector4[]
  • unionPlanes Vector4[]

Returns: Node

Calls:

  • complex_call_2280
Code
setupAlphaToCoverage( intersectionPlanes, unionPlanes ) {

        return Fn( () => {

            const distanceToPlane = float().toVar( 'distanceToPlane' );
            const distanceGradient = float().toVar( 'distanceToGradient' );

            const clipOpacity = float( 1 ).toVar( 'clipOpacity' );

            const numUnionPlanes = unionPlanes.length;

            if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {

                const clippingPlanes = uniformArray( unionPlanes );

                Loop( numUnionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );

                    distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
                    distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );

                    clipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ) );

                } );

            }

            const numIntersectionPlanes = intersectionPlanes.length;

            if ( numIntersectionPlanes > 0 ) {

                const clippingPlanes = uniformArray( intersectionPlanes );
                const intersectionClipOpacity = float( 1 ).toVar( 'intersectionClipOpacity' );

                Loop( numIntersectionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );

                    distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
                    distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );

                    intersectionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );

                } );

                clipOpacity.mulAssign( intersectionClipOpacity.oneMinus() );

            }

            diffuseColor.a.mulAssign( clipOpacity );

            diffuseColor.a.equal( 0.0 ).discard();

        } )();

    }

ClippingNode.setupDefault(intersectionPlanes: Vector4[], unionPlanes: Vector4[]): Node

JSDoc:

/**
     * Setups the default clipping.
     *
     * @param {Array<Vector4>} intersectionPlanes - The intersection planes.
     * @param {Array<Vector4>} unionPlanes - The union planes.
     * @return {Node} The result node.
     */

Parameters:

  • intersectionPlanes Vector4[]
  • unionPlanes Vector4[]

Returns: Node

Calls:

  • complex_call_4117
Code
setupDefault( intersectionPlanes, unionPlanes ) {

        return Fn( () => {

            const numUnionPlanes = unionPlanes.length;

            if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {

                const clippingPlanes = uniformArray( unionPlanes );

                Loop( numUnionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );
                    positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();

                } );

            }

            const numIntersectionPlanes = intersectionPlanes.length;

            if ( numIntersectionPlanes > 0 ) {

                const clippingPlanes = uniformArray( intersectionPlanes );
                const clipped = bool( true ).toVar( 'clipped' );

                Loop( numIntersectionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );
                    clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) );

                } );

                clipped.discard();

            }

        } )();

    }

ClippingNode.setupHardwareClipping(unionPlanes: Vector4[], builder: NodeBuilder): Node

JSDoc:

/**
     * Setups hardware clipping.
     *
     * @param {Array<Vector4>} unionPlanes - The union planes.
     * @param {NodeBuilder} builder - The current node builder.
     * @return {Node} The result node.
     */

Parameters:

  • unionPlanes Vector4[]
  • builder NodeBuilder

Returns: Node

Calls:

  • builder.enableHardwareClipping
  • complex_call_5297
Code
setupHardwareClipping( unionPlanes, builder ) {

        const numUnionPlanes = unionPlanes.length;

        builder.enableHardwareClipping( numUnionPlanes );

        return Fn( () => {

            const clippingPlanes = uniformArray( unionPlanes );
            const hw_clip_distances = builtin( builder.getClipDistance() );

            Loop( numUnionPlanes, ( { i } ) => {

                const plane = clippingPlanes.element( i );

                const distance = positionView.dot( plane.xyz ).sub( plane.w ).negate();
                hw_clip_distances.element( i ).assign( distance );

            } );

        } )();

    }

clipping(): ClippingNode

Returns: ClippingNode

Calls:

  • nodeObject (from ../tsl/TSLBase.js)
Code
() => nodeObject( new ClippingNode() )

clippingAlpha(): ClippingNode

Returns: ClippingNode

Calls:

  • nodeObject (from ../tsl/TSLBase.js)
Code
() => nodeObject( new ClippingNode( ClippingNode.ALPHA_TO_COVERAGE ) )

hardwareClipping(): ClippingNode

Returns: ClippingNode

Calls:

  • nodeObject (from ../tsl/TSLBase.js)
Code
() => nodeObject( new ClippingNode( ClippingNode.HARDWARE ) )

Classes

ClippingNode

Class Code
class ClippingNode extends Node {

    static get type() {

        return 'ClippingNode';

    }

    /**
     * Constructs a new clipping node.
     *
     * @param {('default'|'hardware'|'alphaToCoverage')} [scope='default'] - The node's scope. Similar to other nodes,
     * the selected scope influences the behavior of the node and what type of code is generated.
     */
    constructor( scope = ClippingNode.DEFAULT ) {

        super();

        /**
         * The node's scope. Similar to other nodes, the selected scope influences
         * the behavior of the node and what type of code is generated.
         *
         * @type {('default'|'hardware'|'alphaToCoverage')}
         */
        this.scope = scope;

    }

    /**
     * Setups the node depending on the selected scope.
     *
     * @param {NodeBuilder} builder - The current node builder.
     * @return {Node} The result node.
     */
    setup( builder ) {

        super.setup( builder );

        const clippingContext = builder.clippingContext;
        const { intersectionPlanes, unionPlanes } = clippingContext;

        this.hardwareClipping = builder.material.hardwareClipping;

        if ( this.scope === ClippingNode.ALPHA_TO_COVERAGE ) {

            return this.setupAlphaToCoverage( intersectionPlanes, unionPlanes );

        } else if ( this.scope === ClippingNode.HARDWARE ) {

            return this.setupHardwareClipping( unionPlanes, builder );

        } else {

            return this.setupDefault( intersectionPlanes, unionPlanes );

        }

    }

    /**
     * Setups alpha to coverage.
     *
     * @param {Array<Vector4>} intersectionPlanes - The intersection planes.
     * @param {Array<Vector4>} unionPlanes - The union planes.
     * @return {Node} The result node.
     */
    setupAlphaToCoverage( intersectionPlanes, unionPlanes ) {

        return Fn( () => {

            const distanceToPlane = float().toVar( 'distanceToPlane' );
            const distanceGradient = float().toVar( 'distanceToGradient' );

            const clipOpacity = float( 1 ).toVar( 'clipOpacity' );

            const numUnionPlanes = unionPlanes.length;

            if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {

                const clippingPlanes = uniformArray( unionPlanes );

                Loop( numUnionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );

                    distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
                    distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );

                    clipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ) );

                } );

            }

            const numIntersectionPlanes = intersectionPlanes.length;

            if ( numIntersectionPlanes > 0 ) {

                const clippingPlanes = uniformArray( intersectionPlanes );
                const intersectionClipOpacity = float( 1 ).toVar( 'intersectionClipOpacity' );

                Loop( numIntersectionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );

                    distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
                    distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );

                    intersectionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );

                } );

                clipOpacity.mulAssign( intersectionClipOpacity.oneMinus() );

            }

            diffuseColor.a.mulAssign( clipOpacity );

            diffuseColor.a.equal( 0.0 ).discard();

        } )();

    }

    /**
     * Setups the default clipping.
     *
     * @param {Array<Vector4>} intersectionPlanes - The intersection planes.
     * @param {Array<Vector4>} unionPlanes - The union planes.
     * @return {Node} The result node.
     */
    setupDefault( intersectionPlanes, unionPlanes ) {

        return Fn( () => {

            const numUnionPlanes = unionPlanes.length;

            if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {

                const clippingPlanes = uniformArray( unionPlanes );

                Loop( numUnionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );
                    positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();

                } );

            }

            const numIntersectionPlanes = intersectionPlanes.length;

            if ( numIntersectionPlanes > 0 ) {

                const clippingPlanes = uniformArray( intersectionPlanes );
                const clipped = bool( true ).toVar( 'clipped' );

                Loop( numIntersectionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );
                    clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) );

                } );

                clipped.discard();

            }

        } )();

    }

    /**
     * Setups hardware clipping.
     *
     * @param {Array<Vector4>} unionPlanes - The union planes.
     * @param {NodeBuilder} builder - The current node builder.
     * @return {Node} The result node.
     */
    setupHardwareClipping( unionPlanes, builder ) {

        const numUnionPlanes = unionPlanes.length;

        builder.enableHardwareClipping( numUnionPlanes );

        return Fn( () => {

            const clippingPlanes = uniformArray( unionPlanes );
            const hw_clip_distances = builtin( builder.getClipDistance() );

            Loop( numUnionPlanes, ( { i } ) => {

                const plane = clippingPlanes.element( i );

                const distance = positionView.dot( plane.xyz ).sub( plane.w ).negate();
                hw_clip_distances.element( i ).assign( distance );

            } );

        } )();

    }

}

Methods

setup(builder: NodeBuilder): Node
Code
setup( builder ) {

        super.setup( builder );

        const clippingContext = builder.clippingContext;
        const { intersectionPlanes, unionPlanes } = clippingContext;

        this.hardwareClipping = builder.material.hardwareClipping;

        if ( this.scope === ClippingNode.ALPHA_TO_COVERAGE ) {

            return this.setupAlphaToCoverage( intersectionPlanes, unionPlanes );

        } else if ( this.scope === ClippingNode.HARDWARE ) {

            return this.setupHardwareClipping( unionPlanes, builder );

        } else {

            return this.setupDefault( intersectionPlanes, unionPlanes );

        }

    }
setupAlphaToCoverage(intersectionPlanes: Vector4[], unionPlanes: Vector4[]): Node
Code
setupAlphaToCoverage( intersectionPlanes, unionPlanes ) {

        return Fn( () => {

            const distanceToPlane = float().toVar( 'distanceToPlane' );
            const distanceGradient = float().toVar( 'distanceToGradient' );

            const clipOpacity = float( 1 ).toVar( 'clipOpacity' );

            const numUnionPlanes = unionPlanes.length;

            if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {

                const clippingPlanes = uniformArray( unionPlanes );

                Loop( numUnionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );

                    distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
                    distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );

                    clipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ) );

                } );

            }

            const numIntersectionPlanes = intersectionPlanes.length;

            if ( numIntersectionPlanes > 0 ) {

                const clippingPlanes = uniformArray( intersectionPlanes );
                const intersectionClipOpacity = float( 1 ).toVar( 'intersectionClipOpacity' );

                Loop( numIntersectionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );

                    distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
                    distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );

                    intersectionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );

                } );

                clipOpacity.mulAssign( intersectionClipOpacity.oneMinus() );

            }

            diffuseColor.a.mulAssign( clipOpacity );

            diffuseColor.a.equal( 0.0 ).discard();

        } )();

    }
setupDefault(intersectionPlanes: Vector4[], unionPlanes: Vector4[]): Node
Code
setupDefault( intersectionPlanes, unionPlanes ) {

        return Fn( () => {

            const numUnionPlanes = unionPlanes.length;

            if ( this.hardwareClipping === false && numUnionPlanes > 0 ) {

                const clippingPlanes = uniformArray( unionPlanes );

                Loop( numUnionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );
                    positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();

                } );

            }

            const numIntersectionPlanes = intersectionPlanes.length;

            if ( numIntersectionPlanes > 0 ) {

                const clippingPlanes = uniformArray( intersectionPlanes );
                const clipped = bool( true ).toVar( 'clipped' );

                Loop( numIntersectionPlanes, ( { i } ) => {

                    const plane = clippingPlanes.element( i );
                    clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) );

                } );

                clipped.discard();

            }

        } )();

    }
setupHardwareClipping(unionPlanes: Vector4[], builder: NodeBuilder): Node
Code
setupHardwareClipping( unionPlanes, builder ) {

        const numUnionPlanes = unionPlanes.length;

        builder.enableHardwareClipping( numUnionPlanes );

        return Fn( () => {

            const clippingPlanes = uniformArray( unionPlanes );
            const hw_clip_distances = builtin( builder.getClipDistance() );

            Loop( numUnionPlanes, ( { i } ) => {

                const plane = clippingPlanes.element( i );

                const distance = positionView.dot( plane.xyz ).sub( plane.w ).negate();
                hw_clip_distances.element( i ).assign( distance );

            } );

        } )();

    }