MCP 3D Printer Server

by DMontgomery40
Verified
import NodeMaterial from './NodeMaterial.js'; import { property } from '../../nodes/core/PropertyNode.js'; import { materialReference } from '../../nodes/accessors/MaterialReferenceNode.js'; import { modelWorldMatrixInverse } from '../../nodes/accessors/ModelNode.js'; import { cameraPosition } from '../../nodes/accessors/Camera.js'; import { positionGeometry } from '../../nodes/accessors/Position.js'; import { Fn, varying, float, vec2, vec3, vec4 } from '../../nodes/tsl/TSLBase.js'; import { min, max } from '../../nodes/math/MathNode.js'; import { Loop, Break } from '../../nodes/utils/LoopNode.js'; import { texture3D } from '../../nodes/accessors/Texture3DNode.js'; import { Color } from '../../math/Color.js'; /** @module VolumeNodeMaterial **/ /** * Node material intended for volume rendering. The volumetric data are * defined with an instance of {@link Data3DTexture}. * * @augments NodeMaterial */ class VolumeNodeMaterial extends NodeMaterial { static get type() { return 'VolumeNodeMaterial'; } /** * Constructs a new volume node material. * * @param {Object?} parameters - The configuration parameter. */ constructor( parameters ) { super(); /** * This flag can be used for type testing. * * @type {Boolean} * @readonly * @default true */ this.isVolumeNodeMaterial = true; /** * The base color of the volume. * * @type {Color} * @default 100 */ this.base = new Color( 0xffffff ); /** * A 3D data texture holding the volumetric data. * * @type {Data3DTexture?} * @default null */ this.map = null; /** * This number of samples for each ray that hits the mesh's surface * and travels through the volume. * * @type {Number} * @default 100 */ this.steps = 100; /** * Callback for {@link VolumeNodeMaterial#testNode}. * * @callback testNodeCallback * @param {Data3DTexture<float>} map - The 3D texture. * @param {Node<float>} mapValue - The sampled value inside the volume. * @param {Node<vec3>} probe - The probe which is the entry point of the ray on the mesh's surface. * @param {Node<vec4>} finalColor - The final color. */ /** * The volume rendering of this material works by shooting rays * from the camera position through each fragment of the mesh's * surface and sample the inner volume in a raymarching fashion * multiple times. * * This node can be used to assign a callback function of type `Fn` * that will be executed per sample. The callback receives the * texture, the sampled texture value as well as position on the surface * where the rays enters the volume. The last parameter is a color * that allows the callback to determine the final color. * * @type {testNodeCallback?} * @default null */ this.testNode = null; this.setValues( parameters ); } /** * Setups the vertex and fragment stage of this node material. * * @param {NodeBuilder} builder - The current node builder. */ setup( builder ) { const map = texture3D( this.map, null, 0 ); const hitBox = Fn( ( { orig, dir } ) => { const box_min = vec3( - 0.5 ); const box_max = vec3( 0.5 ); const inv_dir = dir.reciprocal(); const tmin_tmp = box_min.sub( orig ).mul( inv_dir ); const tmax_tmp = box_max.sub( orig ).mul( inv_dir ); const tmin = min( tmin_tmp, tmax_tmp ); const tmax = max( tmin_tmp, tmax_tmp ); const t0 = max( tmin.x, max( tmin.y, tmin.z ) ); const t1 = min( tmax.x, min( tmax.y, tmax.z ) ); return vec2( t0, t1 ); } ); this.fragmentNode = Fn( () => { const vOrigin = varying( vec3( modelWorldMatrixInverse.mul( vec4( cameraPosition, 1.0 ) ) ) ); const vDirection = varying( positionGeometry.sub( vOrigin ) ); const rayDir = vDirection.normalize(); const bounds = vec2( hitBox( { orig: vOrigin, dir: rayDir } ) ).toVar(); bounds.x.greaterThan( bounds.y ).discard(); bounds.assign( vec2( max( bounds.x, 0.0 ), bounds.y ) ); const p = vec3( vOrigin.add( bounds.x.mul( rayDir ) ) ).toVar(); const inc = vec3( rayDir.abs().reciprocal() ).toVar(); const delta = float( min( inc.x, min( inc.y, inc.z ) ) ).toVar( 'delta' ); // used 'delta' name in loop delta.divAssign( materialReference( 'steps', 'float' ) ); const ac = vec4( materialReference( 'base', 'color' ), 0.0 ).toVar(); Loop( { type: 'float', start: bounds.x, end: bounds.y, update: '+= delta' }, () => { const d = property( 'float', 'd' ).assign( map.sample( p.add( 0.5 ) ).r ); if ( this.testNode !== null ) { this.testNode( { map: map, mapValue: d, probe: p, finalColor: ac } ).append(); } else { // default to show surface of mesh ac.a.assign( 1 ); Break(); } p.addAssign( rayDir.mul( delta ) ); } ); ac.a.equal( 0 ).discard(); return vec4( ac ); } )(); super.setup( builder ); } } export default VolumeNodeMaterial;