@@ -36,8 +36,7 @@ function shadergen(p5, fn) {
36
36
}
37
37
}
38
38
39
- // Transpiler
40
-
39
+ // AST Transpiler Callbacks and their helpers
41
40
function replaceBinaryOperator ( codeSource ) {
42
41
switch ( codeSource ) {
43
42
case '+' : return 'add' ;
@@ -50,8 +49,7 @@ function shadergen(p5, fn) {
50
49
51
50
const ASTCallbacks = {
52
51
// TODO: automatically making uniforms
53
- Literal ( node ) {
54
- } ,
52
+
55
53
56
54
// The callbacks for AssignmentExpression and BinaryExpression handle
57
55
// operator overloading including +=, *= assignment expressions
@@ -92,9 +90,8 @@ function shadergen(p5, fn) {
92
90
93
91
94
92
// Javascript Node API.
95
- // These classes are for expressing GLSL functions in Javascript with
93
+ // These classes are for expressing GLSL functions in Javascript without
96
94
// needing to transpile the user's code.
97
-
98
95
class BaseNode {
99
96
constructor ( isInternal ) {
100
97
if ( new . target === BaseNode ) {
@@ -125,7 +122,7 @@ function shadergen(p5, fn) {
125
122
toGLSLBase ( context ) {
126
123
if ( this . useTempVar ( ) ) {
127
124
return this . getTemp ( context ) ;
128
- }
125
+ }
129
126
else {
130
127
return this . toGLSL ( context ) ;
131
128
}
@@ -171,25 +168,6 @@ function shadergen(p5, fn) {
171
168
mult ( other ) { return new BinaryOperatorNode ( this , this . enforceType ( other ) , '*' ) ; }
172
169
div ( other ) { return new BinaryOperatorNode ( this , this . enforceType ( other ) , '/' ) ; }
173
170
mod ( other ) { return new ModulusNode ( this , this . enforceType ( other ) ) ; }
174
- sin ( ) { return new FunctionCallNode ( 'sin' , this , 'float' ) ; }
175
- cos ( ) { return new FunctionCallNode ( 'cos' , this , 'float' ) ; }
176
- radians ( ) { return new FunctionCallNode ( 'radians' , this , 'float' ) ; }
177
- abs ( ) { return new FunctionCallNode ( 'abs' , this , this . type ) } ;
178
- ceil ( ) { return new FunctionCallNode ( ) ; }
179
-
180
- // TODO:
181
- // Add a whole lot of these functions. Probably should take them out of the primitive node and just attach them to global instead.
182
- // https://docs.gl/el3/
183
-
184
- max ( ) { return new FunctionCallNode ( ) ; }
185
- min ( ) { return new FunctionCallNode ( ) ; }
186
- ceil ( ) { return new FunctionCallNode ( ) ; }
187
- round ( ) { return new FunctionCallNode ( ) ; }
188
- roundEven ( ) { return new FunctionCallNode ( ) ; }
189
- sqrt ( ) { return new FunctionCallNode ( ) ; }
190
- log ( ) { return new FunctionCallNode ( ) ; }
191
- exp ( ) { return new FunctionCallNode ( ) ; }
192
-
193
171
194
172
// Check that the types of the operands are compatible.
195
173
// TODO: improve this with less branching if elses
@@ -302,13 +280,10 @@ function shadergen(p5, fn) {
302
280
class VectorNode extends BaseNode {
303
281
constructor ( values , type , isInternal = false ) {
304
282
super ( isInternal ) ;
305
-
306
283
this . components = [ 'x' , 'y' , 'z' , 'w' ] . slice ( 0 , values . length ) ;
307
284
this . components . forEach ( ( component , i ) => {
308
285
this [ component ] = new FloatNode ( values [ i ] , true ) ;
309
- // this[component] = new ComponentNode(this, component, true);
310
286
} ) ;
311
-
312
287
this . type = type ;
313
288
}
314
289
@@ -468,16 +443,13 @@ function shadergen(p5, fn) {
468
443
}
469
444
470
445
// TODO: finish If Node
471
- class ConditionalNode {
446
+ class ConditionalNode extends BaseNode {
472
447
constructor ( value ) {
448
+ super ( value ) ;
473
449
this . value = value ;
474
450
this . condition = null ;
475
451
this . thenBranch = null ;
476
452
this . elseBranch = null ;
477
- }
478
- //helper
479
- checkType ( value ) {
480
-
481
453
}
482
454
// conditions
483
455
equalTo ( value ) { }
@@ -492,12 +464,21 @@ function shadergen(p5, fn) {
492
464
// returns
493
465
thenReturn ( value ) { }
494
466
elseReturn ( value ) { }
495
- // Then?
496
- then ( ) {
497
- GLOBAL_SHADER . context . declarations . push ( )
498
- }
467
+ //
468
+ thenDiscard ( ) {
469
+ new ConditionalDiscard ( this . condition ) ;
470
+ } ;
499
471
} ;
500
472
473
+ class ConditionalDiscard extends Base {
474
+ constructor ( condition ) {
475
+ this . condition = condition ;
476
+ }
477
+ toGLSL ( context ) {
478
+ context . discardConditions . push ( `if(${ this . condition } {discard;})` ) ;
479
+ }
480
+ }
481
+
501
482
fn . if = function ( value ) {
502
483
return new ConditionalNode ( value ) ;
503
484
}
@@ -531,47 +512,68 @@ function shadergen(p5, fn) {
531
512
// This class is responsible for converting the nodes into an object containing GLSL code, to be used by p5.Shader.modify
532
513
533
514
class ShaderGenerator {
534
- constructor ( modifyFunction , shaderToModify ) {
535
- this . modifyFunction = modifyFunction ;
536
- this . shaderToModify = shaderToModify ;
537
- shaderToModify . inspectHooks ( ) ;
515
+ constructor ( modifyFunction , originalShader ) {
538
516
GLOBAL_SHADER = this ;
539
- this . uniforms = { } ;
540
- this . functions = { } ;
517
+ this . modifyFunction = modifyFunction ;
518
+ this . generateHookBuilders ( originalShader ) ;
519
+ this . output = {
520
+ uniforms : { } ,
521
+ }
541
522
this . resetGLSLContext ( ) ;
542
523
}
524
+
543
525
callModifyFunction ( ) {
544
526
this . modifyFunction ( ) ;
545
- return {
546
- uniforms : this . uniforms ,
547
- functions : this . functions ,
548
- vertex : this . functions . getWorldPosition ,
549
- fragment : this . functions . getFinalColor ,
527
+ return this . output ;
528
+ }
529
+
530
+ generateHookBuilders ( originalShader ) {
531
+ const availableHooks = {
532
+ ...originalShader . hooks . vertex ,
533
+ ...originalShader . hooks . fragment ,
550
534
}
535
+
536
+ // Defines a function for each of the hooks for the shader we are modifying.
537
+ Object . keys ( availableHooks ) . forEach ( ( hookName ) => {
538
+ const hookTypes = originalShader . hookTypes ( hookName )
539
+ this [ hookTypes . name ] = function ( userOverride ) {
540
+ let hookArgs = [ ]
541
+ let argsArray = [ ] ;
542
+
543
+ hookTypes . parameters . forEach ( ( parameter , i ) => {
544
+ hookArgs . push (
545
+ new VariableNode ( parameter . name , parameter . type . typeName , true )
546
+ ) ;
547
+ if ( i === 0 ) {
548
+ argsArray . push ( `${ parameter . type . typeName } ${ parameter . name } ` ) ;
549
+ } else {
550
+ argsArray . push ( `, ${ parameter . type . typeName } ${ parameter . name } ` ) ;
551
+ }
552
+ } )
553
+
554
+ const toGLSLResult = userOverride ( ...hookArgs ) . toGLSLBase ( this . context ) ;
555
+ let codeLines = [ `(${ argsArray . join ( ', ' ) } ) {` , this . context . declarations . slice ( ) ] . flat ( ) ;
556
+ codeLines . push ( `\n${ hookTypes . returnType . typeName } finalReturnValue = ${ toGLSLResult } ;
557
+ \nreturn finalReturnValue;
558
+ \n}` ) ;
559
+ this . output [ hookName ] = codeLines . join ( '\n' ) ;
560
+ this . resetGLSLContext ( ) ;
561
+ }
562
+
563
+ // Expose the Functions to global scope for users to use
564
+ window [ hookTypes . name ] = function ( userOverride ) {
565
+ GLOBAL_SHADER [ hookTypes . name ] ( userOverride ) ;
566
+ } ;
567
+ } )
551
568
}
569
+
552
570
resetGLSLContext ( ) {
553
571
this . context = {
554
572
id : 0 ,
555
573
getNextID : function ( ) { return this . id ++ } ,
556
574
declarations : [ ] ,
557
575
}
558
576
}
559
- // TODO:
560
- buildFunction ( argumentName , argumentType , callback ) {
561
- let functionArgument = new VariableNode ( argumentName , argumentType , true ) ;
562
- const finalLine = callback ( functionArgument ) . toGLSLBase ( this . context ) ;
563
- let codeLines = this . context . declarations . slice ( ) ;
564
- codeLines . push ( `\n${ argumentName } = ${ finalLine } ; \nreturn ${ argumentName } ;` )
565
- this . resetGLSLContext ( ) ;
566
- return codeLines . join ( "\n" ) ;
567
- }
568
-
569
- getWorldPosition ( func ) {
570
- this . functions . getWorldPosition = this . buildFunction ( "pos" , "vec3" , func ) ;
571
- }
572
- getFinalColor ( func ) {
573
- this . functions . getFinalColor = this . buildFunction ( "col" , "vec3" , func ) ;
574
- }
575
577
}
576
578
577
579
// User functions
@@ -629,20 +631,77 @@ function shadergen(p5, fn) {
629
631
for ( const type in uniformFns ) {
630
632
const uniformFnVariant = `uniform${ uniformFns [ type ] } ` ;
631
633
ShaderGenerator . prototype [ uniformFnVariant ] = function ( name , defaultValue ) {
632
- this . uniforms [ `${ type } ${ name } ` ] = defaultValue ;
634
+ this . output . uniforms [ `${ type } ${ name } ` ] = defaultValue ;
633
635
return new VariableNode ( name , type ) ;
634
636
} ;
635
637
fn [ uniformFnVariant ] = function ( name , value ) {
636
638
return GLOBAL_SHADER [ uniformFnVariant ] ( name , value ) ;
637
639
} ;
638
640
}
639
641
640
- function getWorldPosition ( func ) {
641
- GLOBAL_SHADER . getWorldPosition ( func )
642
- }
643
-
644
- function getFinalColor ( func ) {
645
- GLOBAL_SHADER . getFinalColor ( func )
642
+ // GLSL Built in functions
643
+ // TODO:
644
+ // Add a whole lot of these functions.
645
+ // https://docs.gl/el3/abs
646
+ const builtInFunctions = {
647
+ // Trigonometry
648
+ 'acos' : { } ,
649
+ 'acosh' : { } ,
650
+ 'asin' : { } ,
651
+ 'asinh' : { } ,
652
+ 'atan' : { } ,
653
+ 'atanh' : { } ,
654
+ 'cos' : { } ,
655
+ 'cosh' : { } ,
656
+ 'degrees' : { } ,
657
+ 'radians' : { } ,
658
+ 'sin' : { } ,
659
+ 'sinh' : { } ,
660
+ 'tan' : { } ,
661
+ 'tanh' : { } ,
662
+ // Mathematics
663
+ 'abs' : { } ,
664
+ 'ceil' : { } ,
665
+ 'clamp' : { } ,
666
+ 'dFdx' : { } ,
667
+ 'dFdy' : { } ,
668
+ 'exp' : { } ,
669
+ 'exp2' : { } ,
670
+ 'floor' : { } ,
671
+ 'fma' : { } ,
672
+ 'fract' : { } ,
673
+ 'fwidth' : { } ,
674
+ 'inversesqrt' : { } ,
675
+ 'isinf' : { } ,
676
+ 'isnan' : { } ,
677
+ 'log' : { } ,
678
+ 'log2' : { } ,
679
+ 'max' : { } ,
680
+ 'min' : { } ,
681
+ 'mix' : { } ,
682
+ 'mod' : { } ,
683
+ 'modf' : { } ,
684
+ 'pow' : { } ,
685
+ 'round' : { } ,
686
+ 'roundEven' : { } ,
687
+ 'sign' : { } ,
688
+ 'smoothstep' : { } ,
689
+ 'sqrt' : { } ,
690
+ 'step' : { } ,
691
+ 'trunc' : { } ,
692
+ // Vector
693
+ 'cross' : { } ,
694
+ 'distance' : { } ,
695
+ 'dot' : { } ,
696
+ 'equal' : { } ,
697
+ 'faceforward' : { } ,
698
+ 'length' : { } ,
699
+ 'normalize' : { } ,
700
+ 'notEqual' : { } ,
701
+ 'reflect' : { } ,
702
+ 'refract' : { } ,
703
+ // Texture sampling
704
+ 'texture' : { } ,
646
705
}
647
706
648
707
const oldTexture = p5 . prototype . texture ;
0 commit comments