1
- import { LogicFns , UniformValue , shaderArgs , LogicProcesses } from "../types" ;
2
- import { domHandler } from "./base" ;
1
+ import { shaderArgs , UniformValue , ShaderHook } from "../types" ;
2
+ import { domHandler , MethodName } from "./base" ;
3
+
3
4
4
5
/** Shader Class
5
6
* @export
6
7
* @class Shader
7
8
* @extends {domHandler }
8
- * @param { LogicFns } logic
9
+ *
9
10
*/
10
- export class SimpleShaderCanvas extends domHandler {
11
- private logic : LogicFns = { } ;
12
- private gl : WebGLRenderingContext | null ;
11
+ export class Shader extends domHandler {
12
+ private hooks : Record < string , ShaderHook [ ] > = { } ;
13
+ private gl : WebGLRenderingContext ;
13
14
private shaderProgram : WebGLProgram ;
14
15
private vertexBuffer : WebGLBuffer ;
15
16
private uniforms : Array < UniformValue > | undefined
16
17
17
18
constructor ( container : HTMLCanvasElement , args : shaderArgs ) {
18
19
super ( container ) ;
19
- this . gl = container . getContext ( 'webgl' ) ;
20
- if ( ! this . gl ) throw new Error ( 'No shader context!' )
21
-
20
+ this . gl = container . getContext ( 'webgl' ) as WebGLRenderingContext ;
22
21
this . shaderProgram = this . initializeShader ( args . vertShader , args . fragShader ) ;
23
-
24
22
this . vertexBuffer = this . initBuffers ( ) ;
25
23
this . uniforms = args . uniforms
26
-
27
24
// Initialize custom logic if provided
28
- if ( args . logic ) this . initializeLogic ( args . logic ) ;
25
+ args . hooks ?. forEach ( ( hook ) => {
26
+ this . addHook ( hook . methodName , hook . hook )
27
+ } )
29
28
}
30
29
31
30
public init ( ) {
32
31
super . init ( ) ;
33
- this . logic . init ?. ( this ) ;
32
+ this . runHooks ( MethodName . INIT ) ;
34
33
35
34
this . resize ( )
36
35
this . startLoop ( 60 ) ;
@@ -40,12 +39,15 @@ export class SimpleShaderCanvas extends domHandler {
40
39
} )
41
40
}
42
41
43
- // Initializes custom logic from provided args
44
- private initializeLogic ( logic : { [ key in LogicProcesses ] ?: string } ) : void {
45
- Object . entries ( logic ) . forEach ( ( [ key , logicFunction ] ) => {
46
- const logicFn = new Function ( `return ${ logicFunction } ` ) ( ) as ( shader : SimpleShaderCanvas ) => void ;
47
- this . logic [ key as LogicProcesses ] = logicFn ;
48
- } ) ;
42
+ public addHook ( methodName : MethodName , hook : ShaderHook ) : void {
43
+ if ( ! this . hooks [ methodName ] ) this . hooks [ methodName ] = [ ] ;
44
+ this . hooks [ methodName ] . push ( hook ) ;
45
+ }
46
+
47
+ protected runHooks ( method : MethodName , ...args : any [ ] ) {
48
+ if ( this . hooks [ method ] ) {
49
+ this . hooks [ method ] . forEach ( ( hook ) => hook ( this , ...args ) ) ;
50
+ }
49
51
}
50
52
51
53
private initBuffers ( ) : WebGLBuffer {
@@ -59,18 +61,16 @@ export class SimpleShaderCanvas extends domHandler {
59
61
1.0 , 1.0 // Top-right
60
62
] ) ;
61
63
62
- const vertexBuffer = this . gl ? .createBuffer ( ) ;
64
+ const vertexBuffer = this . gl . createBuffer ( ) ;
63
65
if ( ! vertexBuffer ) throw new Error ( 'Failed to create vertex buffer' ) ;
64
66
65
- this . gl ? .bindBuffer ( this . gl . ARRAY_BUFFER , vertexBuffer ) ;
66
- this . gl ? .bufferData ( this . gl . ARRAY_BUFFER , vertices , this . gl . STATIC_DRAW ) ;
67
+ this . gl . bindBuffer ( this . gl . ARRAY_BUFFER , vertexBuffer ) ;
68
+ this . gl . bufferData ( this . gl . ARRAY_BUFFER , vertices , this . gl . STATIC_DRAW ) ;
67
69
68
70
return vertexBuffer ;
69
71
}
70
-
71
- private loadShader ( gl : WebGLRenderingContext | null , type ?: GLenum , source : string = '' ) : WebGLShader | null {
72
- if ( ! gl || ! type ) return null ;
73
72
73
+ private loadShader ( gl : WebGLRenderingContext , type : GLenum , source : string = '' ) : WebGLShader | null {
74
74
const shader = gl . createShader ( type ) ;
75
75
if ( ! shader ) return null ;
76
76
@@ -87,19 +87,19 @@ export class SimpleShaderCanvas extends domHandler {
87
87
}
88
88
89
89
private initializeShader ( vertShader ?: string , fragShader ?: string ) : WebGLProgram {
90
- const vertexShader = this . loadShader ( this . gl , this . gl ? .VERTEX_SHADER , vertShader ) ;
91
- const fragmentShader = this . loadShader ( this . gl , this . gl ? .FRAGMENT_SHADER , fragShader ) ;
90
+ const vertexShader = this . loadShader ( this . gl , this . gl . VERTEX_SHADER , vertShader ) ;
91
+ const fragmentShader = this . loadShader ( this . gl , this . gl . FRAGMENT_SHADER , fragShader ) ;
92
92
if ( ! vertexShader || ! fragmentShader ) throw new Error ( 'Shader compilation failed' ) ;
93
93
94
- const shaderProgram = this . gl ? .createProgram ( ) ;
94
+ const shaderProgram = this . gl . createProgram ( ) ;
95
95
if ( ! shaderProgram ) throw new Error ( 'Failed to create shader program' ) ;
96
96
97
- this . gl ? .attachShader ( shaderProgram , vertexShader ) ;
98
- this . gl ? .attachShader ( shaderProgram , fragmentShader ) ;
99
- this . gl ? .linkProgram ( shaderProgram ) ;
97
+ this . gl . attachShader ( shaderProgram , vertexShader ) ;
98
+ this . gl . attachShader ( shaderProgram , fragmentShader ) ;
99
+ this . gl . linkProgram ( shaderProgram ) ;
100
100
101
- if ( ! this . gl ? .getProgramParameter ( shaderProgram , this . gl . LINK_STATUS ) ) {
102
- console . error ( 'Shader program link error:' , this . gl ? .getProgramInfoLog ( shaderProgram ) ) ;
101
+ if ( ! this . gl . getProgramParameter ( shaderProgram , this . gl . LINK_STATUS ) ) {
102
+ console . error ( 'Shader program link error:' , this . gl . getProgramInfoLog ( shaderProgram ) ) ;
103
103
throw new Error ( 'Unable to initialize the shader program' ) ;
104
104
}
105
105
return shaderProgram ;
@@ -108,40 +108,42 @@ export class SimpleShaderCanvas extends domHandler {
108
108
// Main render loop
109
109
protected loop ( ) : void {
110
110
super . loop ( ) ;
111
- this . logic . loop ?. ( this ) ;
111
+ this . runHooks ( MethodName . LOOP ) ;
112
112
this . render ( ) ;
113
113
}
114
114
115
115
protected render ( ) : void {
116
116
const gl = this . gl ;
117
- gl ?. clear ( gl . COLOR_BUFFER_BIT ) ;
118
- gl ?. clear ( gl . DEPTH_BUFFER_BIT ) ;
119
- gl ?. clear ( gl . STENCIL_BUFFER_BIT ) ;
117
+ this . runHooks ( MethodName . RENDER ) ;
118
+
119
+ gl . clear ( gl . COLOR_BUFFER_BIT ) ;
120
+ gl . clear ( gl . DEPTH_BUFFER_BIT ) ;
121
+ gl . clear ( gl . STENCIL_BUFFER_BIT ) ;
120
122
121
- const vertexPosition = gl ? .getAttribLocation ( this . shaderProgram , 'a_position' ) ;
122
- gl ? .bindBuffer ( gl . ARRAY_BUFFER , this . vertexBuffer ) ;
123
- if ( vertexPosition ) gl ? .vertexAttribPointer ( vertexPosition , 2 , gl . FLOAT , false , 0 , 0 ) ;
124
- if ( vertexPosition ) gl ? .enableVertexAttribArray ( vertexPosition ) ;
123
+ const vertexPosition = gl . getAttribLocation ( this . shaderProgram , 'a_position' ) ;
124
+ gl . bindBuffer ( gl . ARRAY_BUFFER , this . vertexBuffer ) ;
125
+ gl . vertexAttribPointer ( vertexPosition , 2 , gl . FLOAT , false , 0 , 0 ) ;
126
+ gl . enableVertexAttribArray ( vertexPosition ) ;
125
127
126
- gl ? .useProgram ( this . shaderProgram ) ;
127
- gl ? .drawArrays ( gl . TRIANGLES , 0 , 6 ) ;
128
+ gl . useProgram ( this . shaderProgram ) ;
129
+ gl . drawArrays ( gl . TRIANGLES , 0 , 6 ) ;
128
130
}
129
131
130
132
/** Gets a uniform value from the shader */
131
- public getUniform ( name : string ) {
132
- const uLoc = this . gl ? .getUniformLocation ( this . shaderProgram , name ) ;
133
+ public getUniform ( name : string ) : UniformValue | undefined {
134
+ const uLoc = this . gl . getUniformLocation ( this . shaderProgram , name ) ;
133
135
if ( ! uLoc ) {
134
136
console . error ( `Uniform ${ name } not found.` ) ;
135
137
return ;
136
138
}
137
139
138
- return this . gl ? .getUniform ( this . shaderProgram , uLoc )
140
+ return this . gl . getUniform ( this . shaderProgram , uLoc )
139
141
}
140
142
141
143
/** Sets a uniform value for the shader */
142
144
public setUniform ( uniform : UniformValue ) : void {
143
145
const { name, type, value } = uniform
144
- const uLoc = this . gl ? .getUniformLocation ( this . shaderProgram , name ) ;
146
+ const uLoc = this . gl . getUniformLocation ( this . shaderProgram , name ) ;
145
147
146
148
if ( ! uLoc ) {
147
149
console . error ( `Uniform ${ name } not found.` ) ;
@@ -150,37 +152,37 @@ export class SimpleShaderCanvas extends domHandler {
150
152
151
153
switch ( type ) {
152
154
case "float" :
153
- this . gl ? .uniform1f ( uLoc , value as number ) ;
155
+ this . gl . uniform1f ( uLoc , value as number ) ;
154
156
break ;
155
157
case "vec2" :
156
- this . gl ? .uniform2fv ( uLoc , value as Float32Array ) ;
158
+ this . gl . uniform2fv ( uLoc , value as Float32Array ) ;
157
159
break ;
158
160
case "vec3" :
159
- this . gl ? .uniform3fv ( uLoc , value as Float32Array ) ;
161
+ this . gl . uniform3fv ( uLoc , value as Float32Array ) ;
160
162
break ;
161
163
case "vec4" :
162
- this . gl ? .uniform4fv ( uLoc , value as Float32Array ) ;
164
+ this . gl . uniform4fv ( uLoc , value as Float32Array ) ;
163
165
break ;
164
166
case "int" :
165
- this . gl ? .uniform1i ( uLoc , value as number ) ;
167
+ this . gl . uniform1i ( uLoc , value as number ) ;
166
168
break ;
167
169
case "ivec2" :
168
- this . gl ? .uniform2iv ( uLoc , value as Int32Array ) ;
170
+ this . gl . uniform2iv ( uLoc , value as Int32Array ) ;
169
171
break ;
170
172
case "ivec3" :
171
- this . gl ? .uniform3iv ( uLoc , value as Int32Array ) ;
173
+ this . gl . uniform3iv ( uLoc , value as Int32Array ) ;
172
174
break ;
173
175
case "ivec4" :
174
- this . gl ? .uniform4iv ( uLoc , value as Int32Array ) ;
176
+ this . gl . uniform4iv ( uLoc , value as Int32Array ) ;
175
177
break ;
176
178
case "mat2" :
177
- this . gl ? .uniformMatrix2fv ( uLoc , false , value as Float32Array ) ;
179
+ this . gl . uniformMatrix2fv ( uLoc , false , value as Float32Array ) ;
178
180
break ;
179
181
case "mat3" :
180
- this . gl ? .uniformMatrix3fv ( uLoc , false , value as Float32Array ) ;
182
+ this . gl . uniformMatrix3fv ( uLoc , false , value as Float32Array ) ;
181
183
break ;
182
184
case "mat4" :
183
- this . gl ? .uniformMatrix4fv ( uLoc , false , value as Float32Array ) ;
185
+ this . gl . uniformMatrix4fv ( uLoc , false , value as Float32Array ) ;
184
186
break ;
185
187
default :
186
188
console . error ( `Unsupported uniform type: ${ type } ` ) ;
@@ -193,19 +195,24 @@ export class SimpleShaderCanvas extends domHandler {
193
195
const { width, height } = this . container . getBoundingClientRect ( ) ;
194
196
this . container . width = width ;
195
197
this . container . height = height ;
196
- this . gl ?. viewport ( 0 , 0 , width , height ) ;
198
+ this . gl . viewport ( 0 , 0 , width , height ) ;
199
+ this . runHooks ( MethodName . RESIZE ) ;
197
200
198
201
this . render ( ) ; // Render immediately on resize to avoid jitter waiting for render call
199
202
}
200
203
201
204
// Handles input events
202
205
protected handleInput ( e : Event ) : void {
203
206
super . handleInput ( e ) ;
207
+ this . runHooks ( MethodName . INPUT ) ;
208
+
204
209
}
205
210
206
211
// Handles touch start events
207
212
protected touchStart ( e : Event ) : void {
208
213
super . touchStart ( e ) ;
209
- this . logic . touch ?.( this ) ;
214
+ this . runHooks ( MethodName . TOUCH ) ;
215
+
216
+
210
217
}
211
- }
218
+ }
0 commit comments