MCP 3D Printer Server

by DMontgomery40
Verified
import Node from '../core/Node.js'; import { NodeUpdateType } from '../core/constants.js'; import { nodeObject } from '../tsl/TSLBase.js'; import { attribute } from '../core/AttributeNode.js'; import { reference, referenceBuffer } from './ReferenceNode.js'; import { add } from '../math/OperatorNode.js'; import { normalLocal } from './Normal.js'; import { positionLocal, positionPrevious } from './Position.js'; import { tangentLocal } from './Tangent.js'; import { uniform } from '../core/UniformNode.js'; import { buffer } from './BufferNode.js'; import { getDataFromObject } from '../core/NodeUtils.js'; /** @module SkinningNode **/ const _frameId = new WeakMap(); /** * This node implements the vertex transformation shader logic which is required * for skinning/skeletal animation. * * @augments Node */ class SkinningNode extends Node { static get type() { return 'SkinningNode'; } /** * Constructs a new skinning node. * * @param {SkinnedMesh} skinnedMesh - The skinned mesh. * @param {Boolean} [useReference=false] - Whether to use reference nodes for internal skinned mesh related data or not. */ constructor( skinnedMesh, useReference = false ) { super( 'void' ); /** * The skinned mesh. * * @type {SkinnedMesh} */ this.skinnedMesh = skinnedMesh; /** * Whether to use reference nodes for internal skinned mesh related data or not. * TODO: Explain the purpose of the property. * * @type {Boolean} */ this.useReference = useReference; /** * The update type overwritten since skinning nodes are updated per object. * * @type {String} */ this.updateType = NodeUpdateType.OBJECT; // /** * The skin index attribute. * * @type {AttributeNode} */ this.skinIndexNode = attribute( 'skinIndex', 'uvec4' ); /** * The skin weight attribute. * * @type {AttributeNode} */ this.skinWeightNode = attribute( 'skinWeight', 'vec4' ); let bindMatrixNode, bindMatrixInverseNode, boneMatricesNode; if ( useReference ) { bindMatrixNode = reference( 'bindMatrix', 'mat4' ); bindMatrixInverseNode = reference( 'bindMatrixInverse', 'mat4' ); boneMatricesNode = referenceBuffer( 'skeleton.boneMatrices', 'mat4', skinnedMesh.skeleton.bones.length ); } else { bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' ); bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' ); boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length ); } /** * The bind matrix node. * * @type {Node<mat4>} */ this.bindMatrixNode = bindMatrixNode; /** * The bind matrix inverse node. * * @type {Node<mat4>} */ this.bindMatrixInverseNode = bindMatrixInverseNode; /** * The bind matrices as a uniform buffer node. * * @type {Node} */ this.boneMatricesNode = boneMatricesNode; /** * The previous bind matrices as a uniform buffer node. * Required for computing motion vectors. * * @type {Node?} * @default null */ this.previousBoneMatricesNode = null; } /** * Transforms the given vertex position via skinning. * * @param {Node} [boneMatrices=this.boneMatricesNode] - The bone matrices * @param {Node<vec3>} [position=positionLocal] - The vertex position in local space. * @return {Node<vec3>} The transformed vertex position. */ getSkinnedPosition( boneMatrices = this.boneMatricesNode, position = positionLocal ) { const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode } = this; const boneMatX = boneMatrices.element( skinIndexNode.x ); const boneMatY = boneMatrices.element( skinIndexNode.y ); const boneMatZ = boneMatrices.element( skinIndexNode.z ); const boneMatW = boneMatrices.element( skinIndexNode.w ); // POSITION const skinVertex = bindMatrixNode.mul( position ); const skinned = add( boneMatX.mul( skinWeightNode.x ).mul( skinVertex ), boneMatY.mul( skinWeightNode.y ).mul( skinVertex ), boneMatZ.mul( skinWeightNode.z ).mul( skinVertex ), boneMatW.mul( skinWeightNode.w ).mul( skinVertex ) ); return bindMatrixInverseNode.mul( skinned ).xyz; } /** * Transforms the given vertex normal via skinning. * * @param {Node} [boneMatrices=this.boneMatricesNode] - The bone matrices * @param {Node<vec3>} [normal=normalLocal] - The vertex normal in local space. * @return {Node<vec3>} The transformed vertex normal. */ getSkinnedNormal( boneMatrices = this.boneMatricesNode, normal = normalLocal ) { const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode } = this; const boneMatX = boneMatrices.element( skinIndexNode.x ); const boneMatY = boneMatrices.element( skinIndexNode.y ); const boneMatZ = boneMatrices.element( skinIndexNode.z ); const boneMatW = boneMatrices.element( skinIndexNode.w ); // NORMAL let skinMatrix = add( skinWeightNode.x.mul( boneMatX ), skinWeightNode.y.mul( boneMatY ), skinWeightNode.z.mul( boneMatZ ), skinWeightNode.w.mul( boneMatW ) ); skinMatrix = bindMatrixInverseNode.mul( skinMatrix ).mul( bindMatrixNode ); return skinMatrix.transformDirection( normal ).xyz; } /** * Transforms the given vertex normal via skinning. * * @param {NodeBuilder} builder - The current node builder. * @return {Node<vec3>} The skinned position from the previous frame. */ getPreviousSkinnedPosition( builder ) { const skinnedMesh = builder.object; if ( this.previousBoneMatricesNode === null ) { skinnedMesh.skeleton.previousBoneMatrices = new Float32Array( skinnedMesh.skeleton.boneMatrices ); this.previousBoneMatricesNode = referenceBuffer( 'skeleton.previousBoneMatrices', 'mat4', skinnedMesh.skeleton.bones.length ); } return this.getSkinnedPosition( this.previousBoneMatricesNode, positionPrevious ); } /** * Returns `true` if bone matrices from the previous frame are required. * * @param {NodeBuilder} builder - The current node builder. * @return {Boolean} Whether bone matrices from the previous frame are required or not. */ needsPreviousBoneMatrices( builder ) { const mrt = builder.renderer.getMRT(); return ( mrt && mrt.has( 'velocity' ) ) || getDataFromObject( builder.object ).useVelocity === true; } /** * Setups the skinning node by assigning the transformed vertex data to predefined node variables. * * @param {NodeBuilder} builder - The current node builder. */ setup( builder ) { if ( this.needsPreviousBoneMatrices( builder ) ) { positionPrevious.assign( this.getPreviousSkinnedPosition( builder ) ); } const skinPosition = this.getSkinnedPosition(); positionLocal.assign( skinPosition ); if ( builder.hasGeometryAttribute( 'normal' ) ) { const skinNormal = this.getSkinnedNormal(); normalLocal.assign( skinNormal ); if ( builder.hasGeometryAttribute( 'tangent' ) ) { tangentLocal.assign( skinNormal ); } } } /** * Generates the code snippet of the skinning node. * * @param {NodeBuilder} builder - The current node builder. * @param {String} output - The current output. * @return {String} The generated code snippet. */ generate( builder, output ) { if ( output !== 'void' ) { return positionLocal.build( builder, output ); } } /** * Updates the state of the skinned mesh by updating the skeleton once per frame. * * @param {NodeFrame} frame - The current node frame. */ update( frame ) { const object = this.useReference ? frame.object : this.skinnedMesh; const skeleton = object.skeleton; if ( _frameId.get( skeleton ) === frame.frameId ) return; _frameId.set( skeleton, frame.frameId ); if ( this.previousBoneMatricesNode !== null ) skeleton.previousBoneMatrices.set( skeleton.boneMatrices ); skeleton.update(); } } export default SkinningNode; /** * TSL function for creating a skinning node. * * @function * @param {SkinnedMesh} skinnedMesh - The skinned mesh. * @returns {SkinningNode} */ export const skinning = ( skinnedMesh ) => nodeObject( new SkinningNode( skinnedMesh ) ); /** * TSL function for creating a skinning node with reference usage. * * @function * @param {SkinnedMesh} skinnedMesh - The skinned mesh. * @returns {SkinningNode} */ export const skinningReference = ( skinnedMesh ) => nodeObject( new SkinningNode( skinnedMesh, true ) );