import { convertTypeName , convertConstant } from "./types" ; import { generateIntrinsicFunction } from "./intrinsics" ; import { Specification } from "stardust-core" ; import { Binding } from "stardust-core" ; import { Dictionary } from "stardust-core" ; export class LinesGenerator { private _lines : string [ ] ; private _currentIndent : string ; private _blocks : Dictionary < string > ; constructor ( ) { this . _lines = [ ] ; this . _currentIndent = "" ; this . _blocks = new Dictionary < string > ( ) ; } public addNamedBlock ( name , code ) { if ( this . _blocks . has ( name ) ) { this . _blocks . set ( name , this . _blocks . get ( name ) + "\n" + code ) ; } else { this . _blocks . set ( name , code ) ; } } public addLine ( code ) { this . _lines . push ( this . _currentIndent + code ) ; } public indent ( ) { this . _currentIndent += "    " ; } public unindent ( ) { this . _currentIndent = this . _currentIndent . slice ( 0 , this . _currentIndent . length - 4 ) ; } public getCode ( ) { return this . _lines . map ( ( line ) => { if ( line [ 0 ] == "@" ) { if ( this . _blocks . has ( line . substr ( 1 ) ) ) { return this . _blocks . get ( line . substr ( 1 ) ) ; } else { return "" ; } } else { return line ; } } ) . join ( "\n" ) ; } } export class ShaderGenerator extends LinesGenerator { constructor ( ) { super ( ) ; } public addAdditionalCode ( code ) { this . addNamedBlock ( "additionalCode" , code ) ; } public addDeclaration ( name , type , modifier = null ) { if ( modifier == null ) { this . addLine ( `${ convertTypeName ( type ) } ${ name };` ) ; } else { this . addLine ( `${ modifier } ${ convertTypeName ( type ) } ${ name };` ) ; } } public addArrayDeclaration ( name , type , count = 1 , modifier = null ) { if ( modifier == "null" ) { this . addLine ( `${ convertTypeName ( type ) }[${ count }] ${ name };` ) ; } else { this . addLine ( `${ modifier } ${ convertTypeName ( type ) }[${ count }] ${ name };` ) ; } } public addUniform ( name , type ) { this . addLine ( `uniform ${ convertTypeName ( type ) } ${ name };` ) ; if ( type == "Vector2Array" || type == "FloatArray" || type == "Vector3Array" || type == "Vector4Array" || type == "ColorArray" ) { this . addLine ( `uniform float ${ name }_length;` ) ; } if ( type == "Vector2Image" || type == "FloatImage" || type == "Vector3Image" || type == "Vector4Image" || type == "Image" ) { this . addLine ( `uniform float ${ name }_width;` ) ; this . addLine ( `uniform float ${ name }_height;` ) ; } } public addAttribute ( name , type ) { this . addLine ( `attribute ${ convertTypeName ( type ) } ${ name };` ) ; } public addVarying ( name , type ) { this . addLine ( `varying ${ convertTypeName ( type ) } ${ name };` ) ; } public addIn ( name , type ) { this . addLine ( `in ${ convertTypeName ( type ) } ${ name };` ) ; } public addOut ( name , type ) { this . addLine ( `out ${ convertTypeName ( type ) } ${ name };` ) ; } public generateExpression ( expr ) { switch ( expr . type ) { case "constant" : { let eConstant = expr as Specification . ExpressionConstant ; return convertConstant ( eConstant . valueType , eConstant . value as ( number | number [ ] ) ) ; } case "variable" : { let eVariable = expr as Specification . ExpressionVariable ; return eVariable . variableName ; } case "function" : { let eFunction = expr as Specification . ExpressionFunction ; let args = eFunction . arguments . map ( ( arg ) => this . generateExpression ( arg ) ) ; let { code , additionalCode } = generateIntrinsicFunction ( eFunction . functionName , args ) ; if ( additionalCode != null ) { this . addAdditionalCode ( additionalCode ) ; } return code ; } case "field" : { let eField = expr as Specification . ExpressionField ; return `${ this . generateExpression ( eField . value ) }.${ eField . fieldName }` ; } } } public addStatement ( stat ) { switch ( stat . type ) { case "assign" : { let sAssign = stat as Specification . StatementAssign ; let expr = this . generateExpression ( sAssign . expression ) ; this . addLine ( `${ sAssign . variableName } = ${ expr };` ) ; } break ; case "condition" : { let sCondition = stat as Specification . StatementCondition ; if ( sCondition . trueStatements . length > 0 && sCondition . falseStatements . length > 0 ) { this . addLine ( `if(${ this . generateExpression ( sCondition . condition ) }) {` ) ; this . indent ( ) ; this . addStatements ( sCondition . trueStatements ) ; this . unindent ( ) ; this . addLine ( `} else {` ) ; this . indent ( ) ; this . addStatements ( sCondition . falseStatements ) ; this . unindent ( ) ; this . addLine ( `}` ) ; } else if ( sCondition . trueStatements . length > 0 ) { this . addLine ( `if(${ this . generateExpression ( sCondition . condition ) }) {` ) ; this . indent ( ) ; this . addStatements ( sCondition . trueStatements ) ; this . unindent ( ) ; this . addLine ( `}` ) ; } else if ( sCondition . falseStatements . length > 0 ) { this . addLine ( `if(!${ this . generateExpression ( sCondition . condition ) }) {` ) ; this . indent ( ) ; this . addStatements ( sCondition . trueStatements ) ; this . unindent ( ) ; this . addLine ( `}` ) ; } } break ; case "for" : { let sForLoop = stat as Specification . StatementForLoop ; this . addLine ( `for(int ${ sForLoop . variableName } = ${ sForLoop . rangeMin }; ${ sForLoop . variableName } <= ${ sForLoop . rangeMax }; ${ sForLoop . variableName }++) {` ) ; this . indent ( ) ; this . addStatements ( sForLoop . statements ) ; this . unindent ( ) ; this . addLine ( `}` ) ; } break ; case "emit" : { let sEmit = stat as Specification . StatementEmit ; this . addEmitStatement ( sEmit ) ; } break ; } } public addStatements ( stat ) { stat . forEach ( ( s ) => this . addStatement ( s ) ) ; } public addEmitStatement ( s ) { this . addLine ( "// Emit Statement" ) ; } } export class ProgramGenerator { public _spec : Specification . Mark ; public _shader : Specification . Shader ; public _asUniform : ( name : string ) => boolean ; public _names : { [ name ] : boolean ; } ; constructor ( spec , shader , asUniform ) { this . _spec = spec ; this . _shader = shader ; this . _asUniform = asUniform ; this . _names = { } ; let record_names = ( map ) => { for ( let name in map ) { if ( map . hasOwnProperty ( name ) ) { this . _names [ name ] = true ; } } } ; record_names ( spec . input ) ; record_names ( spec . output ) ; record_names ( spec . variables ) ; record_names ( shader . input ) ; record_names ( shader . output ) ; } public getUnusedName ( hint ) { let index = 0 ; while ( true ) { let candidate = "s3" + hint + index . toString ( ) ; if ( this . _names [ candidate ] === true ) { index += 1 ; continue ; } this . _names [ candidate ] = true ; return candidate ; } } public fragmentPassthru ( name ) { return this . _spec . input . hasOwnProperty ( name ) && ! this . _spec . output . hasOwnProperty ( name ) ; } }