import TempNode from '../core/TempNode.js'; import { addNodeClass } from '../core/Node.js'; import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; class OperatorNode extends TempNode { constructor( op, aNode, bNode, ...params ) { super(); this.op = op; if ( params.length > 0 ) { let finalBNode = bNode; for ( let i = 0; i < params.length; i ++ ) { finalBNode = new OperatorNode( op, finalBNode, params[ i ] ); } bNode = finalBNode; } this.aNode = aNode; this.bNode = bNode; } getNodeType( builder, output ) { const op = this.op; const aNode = this.aNode; const bNode = this.bNode; const typeA = aNode.getNodeType( builder ); const typeB = typeof bNode !== 'undefined' ? bNode.getNodeType( builder ) : null; if ( typeA === 'void' || typeB === 'void' ) { return 'void'; } else if ( op === '%' ) { return typeA; } else if ( op === '~' || op === '&' || op === '|' || op === '^' || op === '>>' || op === '<<' ) { return builder.getIntegerType( typeA ); } else if ( op === '!' || op === '==' || op === '&&' || op === '||' || op === '^^' ) { return 'bool'; } else if ( op === '<' || op === '>' || op === '<=' || op === '>=' ) { const typeLength = output ? builder.getTypeLength( output ) : Math.max( builder.getTypeLength( typeA ), builder.getTypeLength( typeB ) ); return typeLength > 1 ? `bvec${ typeLength }` : 'bool'; } else { if ( typeA === 'float' && builder.isMatrix( typeB ) ) { return typeB; } else if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) { // matrix x vector return builder.getVectorFromMatrix( typeA ); } else if ( builder.isVector( typeA ) && builder.isMatrix( typeB ) ) { // vector x matrix return builder.getVectorFromMatrix( typeB ); } else if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) { // anytype x anytype: use the greater length vector return typeB; } return typeA; } } generate( builder, output ) { const op = this.op; const aNode = this.aNode; const bNode = this.bNode; const type = this.getNodeType( builder, output ); let typeA = null; let typeB = null; if ( type !== 'void' ) { typeA = aNode.getNodeType( builder ); typeB = typeof bNode !== 'undefined' ? bNode.getNodeType( builder ) : null; if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' ) { if ( builder.isVector( typeA ) ) { typeB = typeA; } else { typeA = typeB = 'float'; } } else if ( op === '>>' || op === '<<' ) { typeA = type; typeB = builder.changeComponentType( typeB, 'uint' ); } else if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) { // matrix x vector typeB = builder.getVectorFromMatrix( typeA ); } else if ( builder.isVector( typeA ) && builder.isMatrix( typeB ) ) { // vector x matrix typeA = builder.getVectorFromMatrix( typeB ); } else { // anytype x anytype typeA = typeB = type; } } else { typeA = typeB = type; } const a = aNode.build( builder, typeA ); const b = typeof bNode !== 'undefined' ? bNode.build( builder, typeB ) : null; const outputLength = builder.getTypeLength( output ); const fnOpSnippet = builder.getFunctionOperator( op ); if ( output !== 'void' ) { if ( op === '<' && outputLength > 1 ) { return builder.format( `${ builder.getMethod( 'lessThan' ) }( ${ a }, ${ b } )`, type, output ); } else if ( op === '<=' && outputLength > 1 ) { return builder.format( `${ builder.getMethod( 'lessThanEqual' ) }( ${ a }, ${ b } )`, type, output ); } else if ( op === '>' && outputLength > 1 ) { return builder.format( `${ builder.getMethod( 'greaterThan' ) }( ${ a }, ${ b } )`, type, output ); } else if ( op === '>=' && outputLength > 1 ) { return builder.format( `${ builder.getMethod( 'greaterThanEqual' ) }( ${ a }, ${ b } )`, type, output ); } else if ( op === '!' || op === '~' ) { return builder.format( `(${op}${a})`, typeA, output ); } else if ( fnOpSnippet ) { return builder.format( `${ fnOpSnippet }( ${ a }, ${ b } )`, type, output ); } else { return builder.format( `( ${ a } ${ op } ${ b } )`, type, output ); } } else if ( typeA !== 'void' ) { if ( fnOpSnippet ) { return builder.format( `${ fnOpSnippet }( ${ a }, ${ b } )`, type, output ); } else { return builder.format( `${ a } ${ op } ${ b }`, type, output ); } } } serialize( data ) { super.serialize( data ); data.op = this.op; } deserialize( data ) { super.deserialize( data ); this.op = data.op; } } export default OperatorNode; export const add = nodeProxy( OperatorNode, '+' ); export const sub = nodeProxy( OperatorNode, '-' ); export const mul = nodeProxy( OperatorNode, '*' ); export const div = nodeProxy( OperatorNode, '/' ); export const remainder = nodeProxy( OperatorNode, '%' ); export const equal = nodeProxy( OperatorNode, '==' ); export const notEqual = nodeProxy( OperatorNode, '!=' ); export const lessThan = nodeProxy( OperatorNode, '<' ); export const greaterThan = nodeProxy( OperatorNode, '>' ); export const lessThanEqual = nodeProxy( OperatorNode, '<=' ); export const greaterThanEqual = nodeProxy( OperatorNode, '>=' ); export const and = nodeProxy( OperatorNode, '&&' ); export const or = nodeProxy( OperatorNode, '||' ); export const not = nodeProxy( OperatorNode, '!' ); export const xor = nodeProxy( OperatorNode, '^^' ); export const bitAnd = nodeProxy( OperatorNode, '&' ); export const bitNot = nodeProxy( OperatorNode, '~' ); export const bitOr = nodeProxy( OperatorNode, '|' ); export const bitXor = nodeProxy( OperatorNode, '^' ); export const shiftLeft = nodeProxy( OperatorNode, '<<' ); export const shiftRight = nodeProxy( OperatorNode, '>>' ); addNodeElement( 'add', add ); addNodeElement( 'sub', sub ); addNodeElement( 'mul', mul ); addNodeElement( 'div', div ); addNodeElement( 'remainder', remainder ); addNodeElement( 'equal', equal ); addNodeElement( 'notEqual', notEqual ); addNodeElement( 'lessThan', lessThan ); addNodeElement( 'greaterThan', greaterThan ); addNodeElement( 'lessThanEqual', lessThanEqual ); addNodeElement( 'greaterThanEqual', greaterThanEqual ); addNodeElement( 'and', and ); addNodeElement( 'or', or ); addNodeElement( 'not', not ); addNodeElement( 'xor', xor ); addNodeElement( 'bitAnd', bitAnd ); addNodeElement( 'bitNot', bitNot ); addNodeElement( 'bitOr', bitOr ); addNodeElement( 'bitXor', bitXor ); addNodeElement( 'shiftLeft', shiftLeft ); addNodeElement( 'shiftRight', shiftRight ); addNodeClass( 'OperatorNode', OperatorNode );