Buckets:
ktongue/docker_container / simsite /frontend /node_modules /three /examples /jsm /loaders /MaterialXLoader.js
| import { | |
| FileLoader, | |
| Loader, | |
| TextureLoader, | |
| RepeatWrapping | |
| } from 'three'; | |
| import { | |
| MeshPhysicalNodeMaterial, | |
| float, bool, int, vec2, vec3, vec4, color, texture, | |
| positionLocal, | |
| add, sub, mul, div, mod, abs, sign, floor, ceil, round, pow, sin, cos, tan, | |
| asin, acos, atan2, sqrt, exp, clamp, min, max, normalize, length, dot, cross, normalMap, | |
| remap, smoothstep, luminance, mx_rgbtohsv, mx_hsvtorgb, | |
| mix, | |
| mx_ramplr, mx_ramptb, mx_splitlr, mx_splittb, | |
| mx_fractal_noise_float, mx_noise_float, mx_cell_noise_float, mx_worley_noise_float, | |
| mx_transform_uv, | |
| mx_safepower, mx_contrast, | |
| mx_srgb_texture_to_lin_rec709, | |
| saturation | |
| } from '../nodes/Nodes.js'; | |
| const colorSpaceLib = { | |
| mx_srgb_texture_to_lin_rec709 | |
| }; | |
| class MXElement { | |
| constructor( name, nodeFunc, params = null ) { | |
| this.name = name; | |
| this.nodeFunc = nodeFunc; | |
| this.params = params; | |
| } | |
| } | |
| // Ref: https://github.com/mrdoob/three.js/issues/24674 | |
| const MXElements = [ | |
| // << Math >> | |
| new MXElement( 'add', add, [ 'in1', 'in2' ] ), | |
| new MXElement( 'subtract', sub, [ 'in1', 'in2' ] ), | |
| new MXElement( 'multiply', mul, [ 'in1', 'in2' ] ), | |
| new MXElement( 'divide', div, [ 'in1', 'in2' ] ), | |
| new MXElement( 'modulo', mod, [ 'in1', 'in2' ] ), | |
| new MXElement( 'absval', abs, [ 'in1', 'in2' ] ), | |
| new MXElement( 'sign', sign, [ 'in1', 'in2' ] ), | |
| new MXElement( 'floor', floor, [ 'in1', 'in2' ] ), | |
| new MXElement( 'ceil', ceil, [ 'in1', 'in2' ] ), | |
| new MXElement( 'round', round, [ 'in1', 'in2' ] ), | |
| new MXElement( 'power', pow, [ 'in1', 'in2' ] ), | |
| new MXElement( 'sin', sin, [ 'in' ] ), | |
| new MXElement( 'cos', cos, [ 'in' ] ), | |
| new MXElement( 'tan', tan, [ 'in' ] ), | |
| new MXElement( 'asin', asin, [ 'in' ] ), | |
| new MXElement( 'acos', acos, [ 'in' ] ), | |
| new MXElement( 'atan2', atan2, [ 'in1', 'in2' ] ), | |
| new MXElement( 'sqrt', sqrt, [ 'in' ] ), | |
| //new MtlXElement( 'ln', ... ), | |
| new MXElement( 'exp', exp, [ 'in' ] ), | |
| new MXElement( 'clamp', clamp, [ 'in', 'low', 'high' ] ), | |
| new MXElement( 'min', min, [ 'in1', 'in2' ] ), | |
| new MXElement( 'max', max, [ 'in1', 'in2' ] ), | |
| new MXElement( 'normalize', normalize, [ 'in' ] ), | |
| new MXElement( 'magnitude', length, [ 'in1', 'in2' ] ), | |
| new MXElement( 'dotproduct', dot, [ 'in1', 'in2' ] ), | |
| new MXElement( 'crossproduct', cross, [ 'in' ] ), | |
| //new MtlXElement( 'transformpoint', ... ), | |
| //new MtlXElement( 'transformvector', ... ), | |
| //new MtlXElement( 'transformnormal', ... ), | |
| //new MtlXElement( 'transformmatrix', ... ), | |
| new MXElement( 'normalmap', normalMap, [ 'in', 'scale' ] ), | |
| //new MtlXElement( 'transpose', ... ), | |
| //new MtlXElement( 'determinant', ... ), | |
| //new MtlXElement( 'invertmatrix', ... ), | |
| //new MtlXElement( 'rotate2d', rotateUV, [ 'in', radians( 'amount' )** ] ), | |
| //new MtlXElement( 'rotate3d', ... ), | |
| //new MtlXElement( 'arrayappend', ... ), | |
| //new MtlXElement( 'dot', ... ), | |
| // << Adjustment >> | |
| new MXElement( 'remap', remap, [ 'in', 'inlow', 'inhigh', 'outlow', 'outhigh' ] ), | |
| new MXElement( 'smoothstep', smoothstep, [ 'in', 'low', 'high' ] ), | |
| //new MtlXElement( 'curveadjust', ... ), | |
| //new MtlXElement( 'curvelookup', ... ), | |
| new MXElement( 'luminance', luminance, [ 'in', 'lumacoeffs' ] ), | |
| new MXElement( 'rgbtohsv', mx_rgbtohsv, [ 'in' ] ), | |
| new MXElement( 'hsvtorgb', mx_hsvtorgb, [ 'in' ] ), | |
| // << Mix >> | |
| new MXElement( 'mix', mix, [ 'bg', 'fg', 'mix' ] ), | |
| // << Channel >> | |
| new MXElement( 'combine2', vec2, [ 'in1', 'in2' ] ), | |
| new MXElement( 'combine3', vec3, [ 'in1', 'in2', 'in3' ] ), | |
| new MXElement( 'combine4', vec4, [ 'in1', 'in2', 'in3', 'in4' ] ), | |
| // << Procedural >> | |
| new MXElement( 'ramplr', mx_ramplr, [ 'valuel', 'valuer', 'texcoord' ] ), | |
| new MXElement( 'ramptb', mx_ramptb, [ 'valuet', 'valueb', 'texcoord' ] ), | |
| new MXElement( 'splitlr', mx_splitlr, [ 'valuel', 'valuer', 'texcoord' ] ), | |
| new MXElement( 'splittb', mx_splittb, [ 'valuet', 'valueb', 'texcoord' ] ), | |
| new MXElement( 'noise2d', mx_noise_float, [ 'texcoord', 'amplitude', 'pivot' ] ), | |
| new MXElement( 'noise3d', mx_noise_float, [ 'texcoord', 'amplitude', 'pivot' ] ), | |
| new MXElement( 'fractal3d', mx_fractal_noise_float, [ 'position', 'octaves', 'lacunarity', 'diminish', 'amplitude' ] ), | |
| new MXElement( 'cellnoise2d', mx_cell_noise_float, [ 'texcoord' ] ), | |
| new MXElement( 'cellnoise3d', mx_cell_noise_float, [ 'texcoord' ] ), | |
| new MXElement( 'worleynoise2d', mx_worley_noise_float, [ 'texcoord', 'jitter' ] ), | |
| new MXElement( 'worleynoise3d', mx_worley_noise_float, [ 'texcoord', 'jitter' ] ), | |
| // << Supplemental >> | |
| //new MtlXElement( 'tiledimage', ... ), | |
| //new MtlXElement( 'triplanarprojection', triplanarTextures, [ 'filex', 'filey', 'filez' ] ), | |
| //new MtlXElement( 'ramp4', ... ), | |
| //new MtlXElement( 'place2d', mx_place2d, [ 'texcoord', 'pivot', 'scale', 'rotate', 'offset' ] ), | |
| new MXElement( 'safepower', mx_safepower, [ 'in1', 'in2' ] ), | |
| new MXElement( 'contrast', mx_contrast, [ 'in', 'amount', 'pivot' ] ), | |
| //new MtlXElement( 'hsvadjust', ... ), | |
| new MXElement( 'saturate', saturation, [ 'in', 'amount' ] ), | |
| //new MtlXElement( 'extract', ... ), | |
| //new MtlXElement( 'separate2', ... ), | |
| //new MtlXElement( 'separate3', ... ), | |
| //new MtlXElement( 'separate4', ... ) | |
| ]; | |
| const MtlXLibrary = {}; | |
| MXElements.forEach( element => MtlXLibrary[ element.name ] = element ); | |
| class MaterialXLoader extends Loader { | |
| constructor( manager ) { | |
| super( manager ); | |
| } | |
| load( url, onLoad, onProgress, onError ) { | |
| new FileLoader( this.manager ) | |
| .setPath( this.path ) | |
| .load( url, async ( text ) => { | |
| try { | |
| onLoad( this.parse( text ) ); | |
| } catch ( e ) { | |
| onError( e ); | |
| } | |
| }, onProgress, onError ); | |
| return this; | |
| } | |
| parse( text ) { | |
| return new MaterialX( this.manager, this.path ).parse( text ); | |
| } | |
| } | |
| class MaterialXNode { | |
| constructor( materialX, nodeXML, nodePath = '' ) { | |
| this.materialX = materialX; | |
| this.nodeXML = nodeXML; | |
| this.nodePath = nodePath ? nodePath + '/' + this.name : this.name; | |
| this.parent = null; | |
| this.node = null; | |
| this.children = []; | |
| } | |
| get element() { | |
| return this.nodeXML.nodeName; | |
| } | |
| get nodeGraph() { | |
| return this.getAttribute( 'nodegraph' ); | |
| } | |
| get nodeName() { | |
| return this.getAttribute( 'nodename' ); | |
| } | |
| get interfaceName() { | |
| return this.getAttribute( 'interfacename' ); | |
| } | |
| get output() { | |
| return this.getAttribute( 'output' ); | |
| } | |
| get name() { | |
| return this.getAttribute( 'name' ); | |
| } | |
| get type() { | |
| return this.getAttribute( 'type' ); | |
| } | |
| get value() { | |
| return this.getAttribute( 'value' ); | |
| } | |
| getNodeGraph() { | |
| let nodeX = this; | |
| while ( nodeX !== null ) { | |
| if ( nodeX.element === 'nodegraph' ) { | |
| break; | |
| } | |
| nodeX = nodeX.parent; | |
| } | |
| return nodeX; | |
| } | |
| getRoot() { | |
| let nodeX = this; | |
| while ( nodeX.parent !== null ) { | |
| nodeX = nodeX.parent; | |
| } | |
| return nodeX; | |
| } | |
| get referencePath() { | |
| let referencePath = null; | |
| if ( this.nodeGraph !== null && this.output !== null ) { | |
| referencePath = this.nodeGraph + '/' + this.output; | |
| } else if ( this.nodeName !== null || this.interfaceName !== null ) { | |
| referencePath = this.getNodeGraph().nodePath + '/' + ( this.nodeName || this.interfaceName ); | |
| } | |
| return referencePath; | |
| } | |
| get hasReference() { | |
| return this.referencePath !== null; | |
| } | |
| get isConst() { | |
| return this.element === 'input' && this.value !== null && this.type !== 'filename'; | |
| } | |
| getColorSpaceNode() { | |
| const csSource = this.getAttribute( 'colorspace' ); | |
| const csTarget = this.getRoot().getAttribute( 'colorspace' ); | |
| const nodeName = `mx_${ csSource }_to_${ csTarget }`; | |
| return colorSpaceLib[ nodeName ]; | |
| } | |
| getTexture() { | |
| const filePrefix = this.getRecursiveAttribute( 'fileprefix' ) || ''; | |
| const texture = this.materialX.textureLoader.load( filePrefix + this.value ); | |
| texture.wrapS = texture.wrapT = RepeatWrapping; | |
| texture.flipY = false; | |
| return texture; | |
| } | |
| getClassFromType( type ) { | |
| let nodeClass = null; | |
| if ( type === 'integer' ) nodeClass = int; | |
| else if ( type === 'float' ) nodeClass = float; | |
| else if ( type === 'vector2' ) nodeClass = vec2; | |
| else if ( type === 'vector3' ) nodeClass = vec3; | |
| else if ( type === 'vector4' || type === 'color4' ) nodeClass = vec4; | |
| else if ( type === 'color3' ) nodeClass = color; | |
| else if ( type === 'boolean' ) nodeClass = bool; | |
| return nodeClass; | |
| } | |
| getNode() { | |
| let node = this.node; | |
| if ( node !== null ) { | |
| return node; | |
| } | |
| // | |
| const type = this.type; | |
| if ( this.isConst ) { | |
| const nodeClass = this.getClassFromType( type ); | |
| node = nodeClass( ...this.getVector() ); | |
| } else if ( this.hasReference ) { | |
| node = this.materialX.getMaterialXNode( this.referencePath ).getNode(); | |
| } else { | |
| const element = this.element; | |
| if ( element === 'convert' ) { | |
| const nodeClass = this.getClassFromType( type ); | |
| node = nodeClass( this.getNodeByName( 'in' ) ); | |
| } else if ( element === 'constant' ) { | |
| node = this.getNodeByName( 'value' ); | |
| } else if ( element === 'position' ) { | |
| node = positionLocal; | |
| } else if ( element === 'tiledimage' ) { | |
| const file = this.getChildByName( 'file' ); | |
| const textureFile = file.getTexture(); | |
| const uvTiling = mx_transform_uv( ...this.getNodesByNames( [ 'uvtiling', 'uvoffset' ] ) ); | |
| node = texture( textureFile, uvTiling ); | |
| const colorSpaceNode = file.getColorSpaceNode(); | |
| if ( colorSpaceNode ) { | |
| node = colorSpaceNode( node ); | |
| } | |
| } else if ( element === 'image' ) { | |
| const file = this.getChildByName( 'file' ); | |
| const uvNode = this.getNodeByName( 'texcoord' ); | |
| const textureFile = file.getTexture(); | |
| node = texture( textureFile, uvNode ); | |
| const colorSpaceNode = file.getColorSpaceNode(); | |
| if ( colorSpaceNode ) { | |
| node = colorSpaceNode( node ); | |
| } | |
| } else if ( MtlXLibrary[ element ] !== undefined ) { | |
| const nodeElement = MtlXLibrary[ element ]; | |
| node = nodeElement.nodeFunc( ...this.getNodesByNames( ...nodeElement.params ) ); | |
| } | |
| } | |
| // | |
| if ( node === null ) { | |
| console.warn( `THREE.MaterialXLoader: Unexpected node ${ new XMLSerializer().serializeToString( this.nodeXML ) }.` ); | |
| node = float( 0 ); | |
| } | |
| // | |
| const nodeToTypeClass = this.getClassFromType( type ); | |
| if ( nodeToTypeClass !== null ) { | |
| node = nodeToTypeClass( node ); | |
| } | |
| node.name = this.name; | |
| this.node = node; | |
| return node; | |
| } | |
| getChildByName( name ) { | |
| for ( const input of this.children ) { | |
| if ( input.name === name ) { | |
| return input; | |
| } | |
| } | |
| } | |
| getNodes() { | |
| const nodes = {}; | |
| for ( const input of this.children ) { | |
| const node = input.getNode(); | |
| nodes[ node.name ] = node; | |
| } | |
| return nodes; | |
| } | |
| getNodeByName( name ) { | |
| const child = this.getChildByName( name ); | |
| return child ? child.getNode() : undefined; | |
| } | |
| getNodesByNames( ...names ) { | |
| const nodes = []; | |
| for ( const name of names ) { | |
| const node = this.getNodeByName( name ); | |
| if ( node ) nodes.push( node ); | |
| } | |
| return nodes; | |
| } | |
| getValue() { | |
| return this.value.trim(); | |
| } | |
| getVector() { | |
| const vector = []; | |
| for ( const val of this.getValue().split( /[,|\s]/ ) ) { | |
| if ( val !== '' ) { | |
| vector.push( Number( val.trim() ) ); | |
| } | |
| } | |
| return vector; | |
| } | |
| getAttribute( name ) { | |
| return this.nodeXML.getAttribute( name ); | |
| } | |
| getRecursiveAttribute( name ) { | |
| let attribute = this.nodeXML.getAttribute( name ); | |
| if ( attribute === null && this.parent !== null ) { | |
| attribute = this.parent.getRecursiveAttribute( name ); | |
| } | |
| return attribute; | |
| } | |
| setStandardSurfaceToGltfPBR( material ) { | |
| const inputs = this.getNodes(); | |
| // | |
| let colorNode = null; | |
| if ( inputs.base && inputs.base_color ) colorNode = mul( inputs.base, inputs.base_color ); | |
| else if ( inputs.base ) colorNode = inputs.base; | |
| else if ( inputs.base_color ) colorNode = inputs.base_color; | |
| // | |
| let roughnessNode = null; | |
| if ( inputs.specular_roughness ) roughnessNode = inputs.specular_roughness; | |
| // | |
| let metalnessNode = null; | |
| if ( inputs.metalness ) metalnessNode = inputs.metalness; | |
| // | |
| let clearcoatNode = null; | |
| let clearcoatRoughnessNode = null; | |
| if ( inputs.coat ) clearcoatNode = inputs.coat; | |
| if ( inputs.coat_roughness ) clearcoatRoughnessNode = inputs.coat_roughness; | |
| if ( inputs.coat_color ) { | |
| colorNode = colorNode ? mul( colorNode, inputs.coat_color ) : colorNode; | |
| } | |
| // | |
| let normalNode = null; | |
| if ( inputs.normal ) normalNode = inputs.normal; | |
| // | |
| let emissiveNode = null; | |
| if ( inputs.emission ) emissiveNode = inputs.emission; | |
| if ( inputs.emissionColor ) { | |
| emissiveNode = emissiveNode ? mul( emissiveNode, inputs.emissionColor ) : emissiveNode; | |
| } | |
| // | |
| material.colorNode = colorNode || color( 0.8, 0.8, 0.8 ); | |
| material.roughnessNode = roughnessNode || float( 0.2 ); | |
| material.metalnessNode = metalnessNode || float( 0 ); | |
| material.clearcoatNode = clearcoatNode || float( 0 ); | |
| material.clearcoatRoughnessNode = clearcoatRoughnessNode || float( 0 ); | |
| if ( normalNode ) material.normalNode = normalNode; | |
| if ( emissiveNode ) material.emissiveNode = emissiveNode; | |
| } | |
| /*setGltfPBR( material ) { | |
| const inputs = this.getNodes(); | |
| console.log( inputs ); | |
| }*/ | |
| setMaterial( material ) { | |
| const element = this.element; | |
| if ( element === 'gltf_pbr' ) { | |
| //this.setGltfPBR( material ); | |
| } else if ( element === 'standard_surface' ) { | |
| this.setStandardSurfaceToGltfPBR( material ); | |
| } | |
| } | |
| toMaterial() { | |
| const material = new MeshPhysicalNodeMaterial(); | |
| material.name = this.name; | |
| for ( const nodeX of this.children ) { | |
| const shaderProperties = this.materialX.getMaterialXNode( nodeX.nodeName ); | |
| shaderProperties.setMaterial( material ); | |
| } | |
| return material; | |
| } | |
| toMaterials() { | |
| const materials = {}; | |
| for ( const nodeX of this.children ) { | |
| if ( nodeX.element === 'surfacematerial' ) { | |
| const material = nodeX.toMaterial(); | |
| materials[ material.name ] = material; | |
| } | |
| } | |
| return materials; | |
| } | |
| add( materialXNode ) { | |
| materialXNode.parent = this; | |
| this.children.push( materialXNode ); | |
| } | |
| } | |
| class MaterialX { | |
| constructor( manager, path ) { | |
| this.manager = manager; | |
| this.path = path; | |
| this.resourcePath = ''; | |
| this.nodesXLib = new Map(); | |
| //this.nodesXRefLib = new WeakMap(); | |
| this.textureLoader = new TextureLoader( manager ); | |
| } | |
| addMaterialXNode( materialXNode ) { | |
| this.nodesXLib.set( materialXNode.nodePath, materialXNode ); | |
| } | |
| /*getMaterialXNodeFromXML( xmlNode ) { | |
| return this.nodesXRefLib.get( xmlNode ); | |
| }*/ | |
| getMaterialXNode( ...names ) { | |
| return this.nodesXLib.get( names.join( '/' ) ); | |
| } | |
| parseNode( nodeXML, nodePath = '' ) { | |
| const materialXNode = new MaterialXNode( this, nodeXML, nodePath ); | |
| if ( materialXNode.nodePath ) this.addMaterialXNode( materialXNode ); | |
| for ( const childNodeXML of nodeXML.children ) { | |
| const childMXNode = this.parseNode( childNodeXML, materialXNode.nodePath ); | |
| materialXNode.add( childMXNode ); | |
| } | |
| return materialXNode; | |
| } | |
| parse( text ) { | |
| const rootXML = new DOMParser().parseFromString( text, 'application/xml' ).documentElement; | |
| this.textureLoader.setPath( this.path ); | |
| // | |
| const materials = this.parseNode( rootXML ).toMaterials(); | |
| return { materials }; | |
| } | |
| } | |
| export { MaterialXLoader }; | |
Xet Storage Details
- Size:
- 15.3 kB
- Xet hash:
- 53e371a8f3167b9466017ee2f5ee204c7d2add8e235e0f1de71048d0d3a8cc10
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.