diff --git a/examples/jsm/transpiler/AST.js b/examples/jsm/transpiler/AST.js index 7473a23e04b7e2..ac89ff5899a62a 100644 --- a/examples/jsm/transpiler/AST.js +++ b/examples/jsm/transpiler/AST.js @@ -46,6 +46,20 @@ export class ASTNode { } + getProgram() { + + let current = this; + + while ( current.parent !== null ) { + + current = current.parent; + + } + + return current.isProgram === true ? current : null; + + } + getParent( parents = [] ) { if ( this.parent === null ) { @@ -114,6 +128,7 @@ export class Program extends ASTNode { super(); this.body = body; + this.structTypes = new Map(); this.isProgram = true; @@ -629,3 +644,32 @@ export class SwitchCase extends ASTNode { } } + +// helper class for StructDefinition +export class StructMember { + + constructor( type, name ) { + + this.type = type; + this.name = name; + this.isStructMember = true; + + } + +} + +export class StructDefinition extends ASTNode { + + constructor( name, members = [] ) { + + super(); + + this.name = name; + this.members = members; + this.isStructDefinition = true; + + this.initialize(); + + } + +} diff --git a/examples/jsm/transpiler/GLSLDecoder.js b/examples/jsm/transpiler/GLSLDecoder.js index 6661d818e5f03e..3055a465880c01 100644 --- a/examples/jsm/transpiler/GLSLDecoder.js +++ b/examples/jsm/transpiler/GLSLDecoder.js @@ -1,6 +1,6 @@ -import { Program, FunctionDeclaration, Switch, For, AccessorElements, Ternary, Varying, DynamicElement, StaticElement, FunctionParameter, Unary, Conditional, VariableDeclaration, Operator, Number, String, FunctionCall, Return, Accessor, Uniform, Discard, SwitchCase, Continue, Break, While, Comment } from './AST.js'; +import { Program, FunctionDeclaration, Switch, For, AccessorElements, Ternary, Varying, DynamicElement, StaticElement, FunctionParameter, Unary, Conditional, VariableDeclaration, Operator, Number, String, FunctionCall, Return, Accessor, Uniform, Discard, SwitchCase, Continue, Break, While, Comment, StructMember, StructDefinition } from './AST.js'; -import { isType } from './TranspilerUtils.js'; +import { isBuiltinType } from './TranspilerUtils.js'; const unaryOperators = [ '+', '-', '~', '!', '++', '--' @@ -255,6 +255,7 @@ class GLSLDecoder { this.index = 0; this.tokenizer = null; this.keywords = []; + this.structTypes = new Map(); this.addPolyfill( 'gl_FragCoord', 'vec3 gl_FragCoord = vec3( screenCoordinate.x, screenCoordinate.y.oneMinus(), screenCoordinate.z );' ); @@ -780,6 +781,54 @@ class GLSLDecoder { } + parseStructDefinition() { + + const tokens = this.readTokensUntil( ';' ); + + const structName = tokens[ 1 ].str; + + if ( tokens[ 2 ].str !== '{' ) { + + throw new Error( 'Expected \'{\' after struct name ' ); + + } + + const structMembers = []; + for ( let i = 3; i < tokens.length - 2; i += 3 ) { + + const typeToken = tokens[ i ]; + const nameToken = tokens[ i + 1 ]; + + if ( typeToken.type != 'literal' || nameToken.type != 'literal' ) { + + throw new Error( 'Invalid struct declaration' ); + + } + + if ( tokens[ i + 2 ].str !== ';' ) { + + throw new Error( 'Missing \';\' after struct member name' ); + + } + + const member = new StructMember( typeToken.str, nameToken.str ); + structMembers.push( member ); + + } + + if ( tokens[ tokens.length - 2 ].str !== '}' ) { + + throw new Error( 'Missing closing \'}\' for struct ' + structName ); + + } + + const definition = new StructDefinition( structName, structMembers ); + this.structTypes.set( structName, definition ); + + return definition; + + } + parseReturn() { this.readToken(); // skip 'return' @@ -827,7 +876,9 @@ class GLSLDecoder { let initialization; - if ( initializationTokens[ 0 ] && isType( initializationTokens[ 0 ].str ) ) { + const firstToken = initializationTokens[ 0 ]; + + if ( firstToken && ( isBuiltinType( firstToken.str ) || this.structTypes.has( firstToken.str ) ) ) { initialization = this.parseVariablesFromToken( initializationTokens ); @@ -1079,7 +1130,11 @@ class GLSLDecoder { statement = this.parseVarying(); - } else if ( isType( token.str ) ) { + } else if ( token.str === 'struct' ) { + + statement = this.parseStructDefinition(); + + } else if ( isBuiltinType( token.str ) || this.structTypes.has( token.str ) ) { if ( this.getToken( 2 ).str === '(' ) { @@ -1159,7 +1214,9 @@ class GLSLDecoder { this.tokenizer = new Tokenizer( polyfill + source ).tokenize(); const body = this.parseBlock(); + const program = new Program( body ); + program.structTypes = this.structTypes; return program; diff --git a/examples/jsm/transpiler/ShaderToyDecoder.js b/examples/jsm/transpiler/ShaderToyDecoder.js index f4fe871432e18c..ad0a04e70b0271 100644 --- a/examples/jsm/transpiler/ShaderToyDecoder.js +++ b/examples/jsm/transpiler/ShaderToyDecoder.js @@ -38,6 +38,8 @@ class ShaderToyDecoder extends GLSLDecoder { node.body.unshift( new VariableDeclaration( 'vec4', 'fragColor' ) ); node.body.push( new Return( fragColor ) ); + node.initialize(); + } return node; diff --git a/examples/jsm/transpiler/TSLEncoder.js b/examples/jsm/transpiler/TSLEncoder.js index 3b44654007d385..1039e9131036c5 100644 --- a/examples/jsm/transpiler/TSLEncoder.js +++ b/examples/jsm/transpiler/TSLEncoder.js @@ -314,6 +314,10 @@ class TSLEncoder { code = this.emitVarying( node ); + } else if ( node.isStructDefinition ) { + + code = this.emitStructDefinition( node ); + } else if ( node.isTernary ) { code = this.emitTernary( node ); @@ -669,9 +673,19 @@ ${ this.tab }} )`; } else { - varStr += ` = property( '${ type }' )`; + const program = node.getProgram(); + + if ( program.structTypes.has( type ) ) { + + varStr += ` = ${ type }()`; + + } else { + + varStr += ` = property( '${ type }' )`; - this.addImport( 'property' ); + this.addImport( 'property' ); + + } } @@ -702,6 +716,34 @@ ${ this.tab }} )`; } + emitStructDefinition( node ) { + + const { name, members } = node; + + this.addImport( 'struct' ); + + let structString = `const ${ name } = struct( {\n`; + + for ( let i = 0; i < members.length; i += 1 ) { + + const member = members[ i ]; + + structString += `${this.tab}\t${member.name}: '${member.type}'`; + + if ( i != members.length - 1 ) { + + structString += ',\n'; + + } + + } + + structString += `\n${this.tab}}, \'${name}\' )`; + + return structString; + + } + emitOverloadingFunction( nodes ) { const { name } = nodes[ 0 ]; diff --git a/examples/jsm/transpiler/TranspilerUtils.js b/examples/jsm/transpiler/TranspilerUtils.js index d9e74a32504183..880ce3e17085d8 100644 --- a/examples/jsm/transpiler/TranspilerUtils.js +++ b/examples/jsm/transpiler/TranspilerUtils.js @@ -1,6 +1,6 @@ export function isExpression( st ) { - return st.isFunctionDeclaration !== true && st.isFor !== true && st.isWhile !== true && st.isConditional !== true && st.isSwitch !== true; + return st.isFunctionDeclaration !== true && st.isFor !== true && st.isWhile !== true && st.isConditional !== true && st.isSwitch !== true && st.isStructDefinition !== true; } @@ -10,9 +10,9 @@ export function isPrimitive( value ) { } -export function isType( str ) { +export function isBuiltinType( str ) { - return /void|bool|float|u?int|mat[234]|mat[234]x[234]|(u|i|b)?vec[234]/.test( str ); + return /^(void|bool|float|u?int|mat[234]|mat[234]x[234]|(u|i|b)?vec[234])$/.test( str ); } diff --git a/examples/jsm/transpiler/WGSLEncoder.js b/examples/jsm/transpiler/WGSLEncoder.js index 3f588973a671ef..0588c36575491b 100644 --- a/examples/jsm/transpiler/WGSLEncoder.js +++ b/examples/jsm/transpiler/WGSLEncoder.js @@ -300,6 +300,10 @@ class WGSLEncoder { this.varyings.push( node ); return ''; // Defer emission to the header + } else if ( node.isStructDefinition ) { + + code = this.emitStructDefinition( node ); + } else if ( node.isTernary ) { const cond = this.emitExpression( node.cond ); @@ -584,6 +588,29 @@ class WGSLEncoder { } + emitStructDefinition( node ) { + + const { name, members } = node; + + let structString = `struct ${ name } {\n`; + + for ( let i = 0; i < members.length; i += 1 ) { + + const member = members[ i ]; + + structString += `${ this.tab }\t${ member.name }: ${ this.getWgslType( member.type ) }`; + + const delimiter = ( i != members.length - 1 ) ? ',\n' : '\n'; + structString += delimiter; + + } + + structString += this.tab + '}'; + + return structString; + + } + emitFunction( node ) { const name = node.name;