MCP 3D Printer Server

by DMontgomery40
Verified
import Node from '../core/Node.js'; import { nodeObject, Fn, bool, float } from '../tsl/TSLBase.js'; import { positionView } from './Position.js'; import { diffuseColor } from '../core/PropertyNode.js'; import { Loop } from '../utils/LoopNode.js'; import { smoothstep } from '../math/MathNode.js'; import { uniformArray } from './UniformArrayNode.js'; import { builtin } from './BuiltinNode.js'; /** @module ClippingNode **/ /** * ``` * This node is used in {@link NodeMaterial} to setup the clipping * which can happen hardware-accelerated (if supported) and optionally * use alpha-to-coverage for anti-aliasing clipped edges. * ``` * @augments Node */ 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 ); } ); } )(); } } ClippingNode.ALPHA_TO_COVERAGE = 'alphaToCoverage'; ClippingNode.DEFAULT = 'default'; ClippingNode.HARDWARE = 'hardware'; export default ClippingNode; /** * TSL function for setting up the default clipping logic. * * @function * @returns {ClippingNode} */ export const clipping = () => nodeObject( new ClippingNode() ); /** * TSL function for setting up alpha to coverage. * * @function * @returns {ClippingNode} */ export const clippingAlpha = () => nodeObject( new ClippingNode( ClippingNode.ALPHA_TO_COVERAGE ) ); /** * TSL function for setting up hardware-based clipping. * * @function * @returns {ClippingNode} */ export const hardwareClipping = () => nodeObject( new ClippingNode( ClippingNode.HARDWARE ) );