Просмотр исходного кода

TSL Transpiler: Support struct definitions and declarations (#32300)

Co-authored-by: sunag <sunagbrasil@gmail.com>
Caden Parker 3 месяцев назад
Родитель
Сommit
bd03102f60

+ 44 - 0
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();
+
+	}
+
+}

+ 61 - 4
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;
 

+ 2 - 0
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;

+ 44 - 2
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 ];

+ 3 - 3
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 );
 
 }
 

+ 27 - 0
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;

粤ICP备19079148号