diff --git a/assets/workers/ShaderWorker.ts b/assets/workers/ShaderWorker.ts index 0891248..e27c7a6 100644 --- a/assets/workers/ShaderWorker.ts +++ b/assets/workers/ShaderWorker.ts @@ -1,214 +1,244 @@ -import noise from "~/assets/shaders/noise.frag?raw" +import noise from "~/assets/shaders/noise.frag?raw"; -import resolveLygia from "~/assets/workers/resolve-lygia.esm" +import resolveLygia from "~/assets/workers/resolve-lygia.esm"; -let interval: number -let timeout: number +let interval: number; +let timeout: number; -const shaderOptions = { quality: 1, framerate: 1000 / 60 } -const screen = { width: 0, height: 0, devicePixelRatio: 0 } +const shaderOptions = { quality: 1, framerate: 1000 / 60 }; +const screen = { width: 0, height: 0, devicePixelRatio: 0 }; -const _colorStart = [0, 0, 0] -const _colorEnd = [0, 0, 0] +const _colorStart = [0, 0, 0]; +const _colorEnd = [0, 0, 0]; let fragmentShader = resolveLygia(noise); -let canvas: OffscreenCanvas +let canvas: OffscreenCanvas; -let render: (time: number) => void +let render: (time: number) => void; -let must_resize = false +let must_resize = false; async function main(canvas: OffscreenCanvas) { - const gl = canvas.getContext("webgl"); - if (!gl) { - return; - } - // setup GLSL program - const program = gl.createProgram() - if (!program) { return } + const gl = canvas.getContext("webgl"); + if (!gl) { + return; + } + // setup GLSL program + const program = gl.createProgram(); + if (!program) { + return; + } - const fragment = gl.createShader(gl.FRAGMENT_SHADER); - if (!fragment) { return } - gl.shaderSource(fragment, fragmentShader) - gl.compileShader(fragment); - if (!gl.getShaderParameter(fragment, gl.COMPILE_STATUS)) { - const lastError = gl.getShaderInfoLog(fragment); - console.error(lastError) - gl.deleteShader(fragment); - return + const fragment = gl.createShader(gl.FRAGMENT_SHADER); + if (!fragment) { + return; + } + gl.shaderSource(fragment, fragmentShader); + gl.compileShader(fragment); + if (!gl.getShaderParameter(fragment, gl.COMPILE_STATUS)) { + const lastError = gl.getShaderInfoLog(fragment); + console.error(lastError); + gl.deleteShader(fragment); + return; + } + + const vertex = gl.createShader(gl.VERTEX_SHADER); + if (!vertex) { + return; + } + gl.shaderSource( + vertex, + `attribute vec4 a_position;void main() {gl_Position = a_position;}` + ); + gl.compileShader(vertex); + if (!gl.getShaderParameter(vertex, gl.COMPILE_STATUS)) { + const lastError = gl.getShaderInfoLog(vertex); + console.error(lastError); + gl.deleteShader(vertex); + return; + } + + gl.attachShader(program, fragment); + gl.attachShader(program, vertex); + gl.linkProgram(program); + const linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + return; + } + // const program = webglUtils.createProgramFromSources(gl, [vs, fragmentShader2]); + + // look up where the vertex data needs to go. + const positionAttributeLocation = gl.getAttribLocation(program, "a_position"); + + // look up uniform locations + const offset = gl.getUniformLocation(program, "u_offset"); + const resolutionLocation = gl.getUniformLocation(program, "u_resolution"); + const colorStart = gl.getUniformLocation(program, "u_color_start"); + const quality = gl.getUniformLocation(program, "u_quality"); + const colorEnd = gl.getUniformLocation(program, "u_color_end"); + const timeLocation = gl.getUniformLocation(program, "u_time"); + + // Create a buffer to put three 2d clip space points in + const positionBuffer = gl.createBuffer(); + + // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + + // fill it with a 2 triangles that cover clipspace + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array([ + -1, + -1, // first triangle + 1, + -1, + -1, + 1, + -1, + 1, // second triangle + 1, + -1, + 1, + 1, + ]), + gl.STATIC_DRAW + ); + + const gl2 = gl; + function render(time: number) { + if (must_resize) { + canvas.width = (screen.width * shaderOptions.quality) / 10; + canvas.height = (screen.height * shaderOptions.quality) / 10; + must_resize = false; } - const vertex = gl.createShader(gl.VERTEX_SHADER); - if (!vertex) { return } - gl.shaderSource(vertex, `attribute vec4 a_position;void main() {gl_Position = a_position;}`) - gl.compileShader(vertex); - if (!gl.getShaderParameter(vertex, gl.COMPILE_STATUS)) { - const lastError = gl.getShaderInfoLog(vertex); - console.error(lastError) - gl.deleteShader(vertex); - return + time *= 0.001; // convert to seconds + // Tell WebGL how to convert from clip space to pixels + gl2.viewport(0, 0, gl2.canvas.width, gl2.canvas.height); + + // Tell it to use our program (pair of shaders) + gl2.useProgram(program); + + // Turn on the attribute + gl2.enableVertexAttribArray(positionAttributeLocation); + + // Bind the position buffer. + gl2.bindBuffer(gl2.ARRAY_BUFFER, positionBuffer); + + // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER) + gl2.vertexAttribPointer( + positionAttributeLocation, + 2, // 2 components per iteration + gl2.FLOAT, // the data is 32bit floats + false, // don't normalize the data + 0, // 0 = move forward size * sizeof(type) each iteration to get the next position + 0 // start at the beginning of the buffer + ); + + const secondRatio = screen.width / screen.height; + + if (secondRatio < 1) { + const half = canvas.height / 2; + gl2.uniform2f( + resolutionLocation, + canvas.width, + canvas.height * secondRatio + ); + gl2.uniform2f(offset, 0, -half + half / (1 / secondRatio)); + } else { + const half = canvas.width / 2; + gl2.uniform2f( + resolutionLocation, + canvas.width / secondRatio, + canvas.height + ); + gl2.uniform2f(offset, -half + half / secondRatio, 0); } + gl2.uniform1i(quality, shaderOptions.quality); - gl.attachShader(program, fragment) - gl.attachShader(program, vertex) - gl.linkProgram(program) - const linked = gl.getProgramParameter(program, gl.LINK_STATUS); - if (!linked) { return } - // const program = webglUtils.createProgramFromSources(gl, [vs, fragmentShader2]); - - // look up where the vertex data needs to go. - const positionAttributeLocation = gl.getAttribLocation(program, "a_position"); - - // look up uniform locations - const offset = gl.getUniformLocation(program, "u_offset"); - const resolutionLocation = gl.getUniformLocation(program, "u_resolution"); - const colorStart = gl.getUniformLocation(program, "u_color_start"); - const quality = gl.getUniformLocation(program, "u_quality"); - const colorEnd = gl.getUniformLocation(program, "u_color_end"); - const timeLocation = gl.getUniformLocation(program, "u_time"); - - // Create a buffer to put three 2d clip space points in - const positionBuffer = gl.createBuffer(); - - // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) - gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); - - // fill it with a 2 triangles that cover clipspace - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ - -1, -1, // first triangle - 1, -1, - -1, 1, - -1, 1, // second triangle - 1, -1, - 1, 1, - ]), gl.STATIC_DRAW); - - - const gl2 = gl - function render(time: number) { - if (must_resize) { - canvas.width = screen.width * shaderOptions.quality / 10 - canvas.height = screen.height * shaderOptions.quality / 10 - must_resize = false; - } - - time *= 0.001; // convert to seconds - // Tell WebGL how to convert from clip space to pixels - gl2.viewport(0, 0, gl2.canvas.width, gl2.canvas.height); - - // Tell it to use our program (pair of shaders) - gl2.useProgram(program); - - // Turn on the attribute - gl2.enableVertexAttribArray(positionAttributeLocation); - - // Bind the position buffer. - gl2.bindBuffer(gl2.ARRAY_BUFFER, positionBuffer); - - // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER) - gl2.vertexAttribPointer( - positionAttributeLocation, - 2, // 2 components per iteration - gl2.FLOAT, // the data is 32bit floats - false, // don't normalize the data - 0, // 0 = move forward size * sizeof(type) each iteration to get the next position - 0, // start at the beginning of the buffer - ); - - const secondRatio = screen.width / screen.height - - if (secondRatio < 1) { - const half = canvas.height / 2 - gl2.uniform2f(resolutionLocation, canvas.width, canvas.height * secondRatio); - gl2.uniform2f(offset, 0, -half + ((half) / (1 / secondRatio))); - } else { - const half = canvas.width / 2 - gl2.uniform2f(resolutionLocation, canvas.width / secondRatio, canvas.height); - gl2.uniform2f(offset, -half + ((half) / secondRatio), 0); - } - gl2.uniform1i(quality, shaderOptions.quality) - - gl2.uniform3f(colorStart, _colorStart[0], _colorStart[1], _colorStart[2]); - gl2.uniform3f(colorEnd, _colorEnd[0], _colorEnd[1], _colorEnd[2]); - gl2.uniform1f(timeLocation, time); - gl2.drawArrays( - gl2.TRIANGLES, - 0, // offset - 6, // num vertices to process - ); - if (timeout) { - self.clearTimeout(timeout) - timeout = self.setTimeout(() => { - requestAnimationFrame(render) - }, 1000 / shaderOptions.framerate) - } + gl2.uniform3f(colorStart, _colorStart[0], _colorStart[1], _colorStart[2]); + gl2.uniform3f(colorEnd, _colorEnd[0], _colorEnd[1], _colorEnd[2]); + gl2.uniform1f(timeLocation, time); + gl2.drawArrays( + gl2.TRIANGLES, + 0, // offset + 6 // num vertices to process + ); + if (timeout) { + self.clearTimeout(timeout); + timeout = self.setTimeout(() => { + requestAnimationFrame(render); + }, 1000 / shaderOptions.framerate); } + } - requestAnimationFrame(render) + requestAnimationFrame(render); - // self.clearInterval(interval) - // interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate) - self.clearTimeout(timeout) - if (shaderOptions.framerate == 0) { - return render - } - timeout = self.setTimeout(() => { - requestAnimationFrame(render) - }, 1000 / shaderOptions.framerate) - return render + // self.clearInterval(interval) + // interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate) + self.clearTimeout(timeout); + if (shaderOptions.framerate == 0) { + return render; + } + timeout = self.setTimeout(() => { + requestAnimationFrame(render); + }, 1000 / shaderOptions.framerate); + return render; } self.addEventListener("message", (e) => { - const data = e.data - if (data.command == "initShader") { - canvas = data.canvas - shaderOptions.quality = data.shaderOptions.quality - shaderOptions.framerate = data.shaderOptions.framerate - if (shaderOptions.framerate == 65) { - shaderOptions.framerate == Infinity - } - screen.devicePixelRatio = data.screen.devicePixelRatio - screen.width = data.screen.width - screen.height = data.screen.height - loadShader() - } else if (data.command == "killShader") { - console.log("killShader") - self.clearInterval(interval) - self.clearTimeout(timeout) + const data = e.data; + if (data.command == "initShader") { + canvas = data.canvas; + shaderOptions.quality = data.shaderOptions.quality; + shaderOptions.framerate = data.shaderOptions.framerate; + if (shaderOptions.framerate == 65) { + shaderOptions.framerate == Infinity; } - else if (data.command == "resumeShader") { - self.clearInterval(interval) - self.clearTimeout(timeout) - shaderOptions.framerate = data.shaderOptions.framerate - if (shaderOptions.framerate == 65) { - shaderOptions.framerate == Infinity - } - timeout = self.setTimeout(() => { - requestAnimationFrame(render) - }, 1000 / shaderOptions.framerate) - // interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate) - } else if (data.command == "recolorShader") { - _colorStart[0] = data.colorStart[0] - _colorStart[1] = data.colorStart[1] - _colorStart[2] = data.colorStart[2] - _colorEnd[0] = data.colorEnd[0] - _colorEnd[1] = data.colorEnd[1] - _colorEnd[2] = data.colorEnd[2] - } else if (data.command == "resizeShader") { - shaderOptions.quality = data.shaderOptions.quality - screen.devicePixelRatio = data.screen.devicePixelRatio - screen.width = data.screen.width - screen.height = data.screen.height - must_resize = true + screen.devicePixelRatio = data.screen.devicePixelRatio; + screen.width = data.screen.width; + screen.height = data.screen.height; + loadShader(); + } else if (data.command == "killShader") { + console.log("killShader"); + self.clearInterval(interval); + self.clearTimeout(timeout); + } else if (data.command == "resumeShader") { + self.clearInterval(interval); + self.clearTimeout(timeout); + shaderOptions.framerate = data.shaderOptions.framerate; + if (shaderOptions.framerate == 65) { + shaderOptions.framerate == Infinity; } -}) + timeout = self.setTimeout(() => { + requestAnimationFrame(render); + }, 1000 / shaderOptions.framerate); + // interval = self.setInterval(() => requestAnimationFrame(render), 1000 / shaderOptions.framerate) + } else if (data.command == "recolorShader") { + _colorStart[0] = data.colorStart[0]; + _colorStart[1] = data.colorStart[1]; + _colorStart[2] = data.colorStart[2]; + _colorEnd[0] = data.colorEnd[0]; + _colorEnd[1] = data.colorEnd[1]; + _colorEnd[2] = data.colorEnd[2]; + } else if (data.command == "resizeShader") { + shaderOptions.quality = data.shaderOptions.quality; + screen.devicePixelRatio = data.screen.devicePixelRatio; + screen.width = data.screen.width; + screen.height = data.screen.height; + must_resize = true; + } +}); async function loadShader() { - if (!canvas) { return } - // let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + shaderOptions.quality); - const temp = await main(canvas); - if (temp) { - render = temp - } -} \ No newline at end of file + if (!canvas) { + return; + } + // let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + shaderOptions.quality); + const temp = await main(canvas); + if (temp) { + render = temp; + } + postMessage("ready"); +} diff --git a/components/AnimatedBackground.vue b/components/AnimatedBackground.vue index e29f0fc..f825c9f 100644 --- a/components/AnimatedBackground.vue +++ b/components/AnimatedBackground.vue @@ -3,7 +3,9 @@ const settingsStore = useSettingsStore() import gsap from 'gsap'; import ShaderWorker from '~/assets/workers/ShaderWorker?worker' -import goStore from '~/netcode'; +import type { Game } from '~/netcode/interfaces'; + +const emit = defineEmits(["ready"]) const worker = new ShaderWorker() const canvas = ref(null) @@ -27,18 +29,29 @@ const _colorEnd = reactive(props.colorEnd) function initShader() { - if (!canvas.value) { return } - const offscreen = canvas.value?.transferControlToOffscreen() - worker.postMessage({ - command: "initShader", - canvas: offscreen, - screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio }, - shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate }, - }, [offscreen]) - if (settingsStore.shaderFramerate == 0) { - killShader() - } - resizeShader() + return new Promise((resolve, reject) => { + if (!canvas.value) { + reject("canvas is null"); + return + } + const offscreen = canvas.value?.transferControlToOffscreen() + worker.postMessage({ + command: "initShader", + canvas: offscreen, + screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio }, + shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate }, + }, [offscreen]) + if (settingsStore.shaderFramerate == 0) { + killShader() + } + resizeShader() + worker.onmessage = (e) => { + if (e.data == "ready") { + resolve() + } + } + }) + } function recolorShader() { @@ -83,15 +96,15 @@ watch(() => [settingsStore.shaderQuality], () => { resizeShader() }) -watch(() => [goStore.current_game?.current_turn], () => { - if (goStore.current_game?.current_turn != goStore.self?.team) { - gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .2, 2: .3 }) - gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 }) - } else { - gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .3, 2: 0 }) - gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 }) - } -}, { immediate: true }) +// watch(() => [props.game.currentTurn], () => { +// if (props.game.teams.find() != goStore.self?.team) { +// gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .2, 2: .3 }) +// gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 }) +// } else { +// gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: .3, 2: 0 }) +// gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 }) +// } +// }, { immediate: true }) watch([_colorEnd, _colorStart], () => { @@ -99,18 +112,16 @@ watch([_colorEnd, _colorStart], () => { }) onMounted(async () => { - nextTick(() => { - recolorShader() - initShader() - window.addEventListener("resize", resizeShader, true) - }) + recolorShader() + await initShader() + window.addEventListener("resize", resizeShader, true) + emit("ready") }) onUnmounted(() => { window.removeEventListener("resize", resizeShader) killShader() }) -