import { SmcErr } from './errors.js'; export { SmcProgramBuilder }; class SmcProgramBuilder { #gl; #program; #isBuilt = false; #hasVertexShader = false; #hasFragmentShader = false; #defaultVertexShader = ` attribute vec4 aVertex; void main() { gl_Position = aVertex; } `; // TODO: reset the sample fragment shader back to the rainbow #sampleFragmentShader = ` precision mediump float; // uniform float uTime; uniform vec2 uResolution; void main() { vec2 uv = gl_FragCoord.xy / uResolution.xy; // vec3 col = 0.5 + 0.5 * cos(uTime + uv.xyx + vec3(0, 2, 4)); // gl_FragColor = vec4(col, 1.0); // gl_FragColor = vec4(216., 43., 72., 255.) / 255.; // float maxfc = max(gl_FragCoord.x, gl_FragCoord.y); // gl_FragColor = vec4(gl_FragCoord.xy, maxfc, maxfc) / maxfc; float maxuv = max(uv.x, uv.y); gl_FragColor = vec4(216. * maxuv, 43., 72., 255.) / 255.; // vec3 col = 0.5 + 0.5*cos(uv.xyx+vec3(0,2,4)); // gl_FragColor = vec4(col, 1.); } `; constructor(gl, raiseError) { this.#gl = gl; this.#program = this.#gl.createProgram(); this.raiseError = raiseError; } addVertexShader(source) { this.#gl.attachShader( this.#program, this.#newShader( this.#gl.VERTEX_SHADER, source ) ) this.#hasVertexShader = true; return this; } addFragmentShader(source) { this.#gl.attachShader( this.#program, this.#newShader( this.#gl.FRAGMENT_SHADER, source ) ) console.log(source) this.#hasFragmentShader = true; return this; } // fetchVertexShader(uri) { // (async () => this.#fetchShader(uri, (source) => this.addVertexShader(source)))(); // return this; // } // async fetchFragmentShader(uri) { // var delegate = (source) => this.addFragmentShader(source); // var source = await this.#fetchShader(uri); // return this; // } build() { // avoid user accidental calls to build() if (!this.#isBuilt) { if (!this.#hasVertexShader) this.addVertexShader(this.#defaultVertexShader) if (!this.#hasFragmentShader) this.addFragmentShader(this.#sampleFragmentShader); this.#gl.linkProgram(this.#program); this.#gl.useProgram(this.#program); } return this.#program; } // Creates a shader of the given type, uploads the source and compiles #newShader(type, source) { const shader = this.#gl.createShader(type); this.#gl.shaderSource(shader, source); this.#gl.compileShader(shader); if (!this.#gl.getShaderParameter(shader, this.#gl.COMPILE_STATUS)) { this.#gl.deleteShader(shader); const infoLog = this.#gl.getShaderInfoLog(shader); this.raiseError( SmcErr.SHADER_COMPILATION, new Error(`An error occurred while compiling the shader: ${infoLog}`) ); } return shader; } // async #fetchShader(uri, delegate) { // const res = await fetch(uri); // if (res.ok) // return await res.text(); // this.raiseError( // SmcErr.FETCH_SHADER, // `Failed to load shader source ${url}: ${res.status} ${res.json()}`); // return "" // } }