Merge pull request 'feat-shader-background' (#95) from feat-shader-background into main
release-tag / release-image (push) Successful in 33s
release-tag / release-image (push) Successful in 33s
Reviewed-on: #95
This commit was merged in pull request #95.
This commit is contained in:
@@ -16,6 +16,7 @@ uniform float u_time;
|
|||||||
void main(){
|
void main(){
|
||||||
vec2 st=(gl_FragCoord.xy+u_offset)/u_resolution;
|
vec2 st=(gl_FragCoord.xy+u_offset)/u_resolution;
|
||||||
|
|
||||||
|
|
||||||
vec2 q=vec2(0.);
|
vec2 q=vec2(0.);
|
||||||
q.x=fbm(st);
|
q.x=fbm(st);
|
||||||
q.y=fbm(st+vec2(1.));
|
q.y=fbm(st+vec2(1.));
|
||||||
@@ -31,4 +32,5 @@ void main(){
|
|||||||
vec3 color=mix(u_color_start,u_color_end,f*.3);
|
vec3 color=mix(u_color_start,u_color_end,f*.3);
|
||||||
|
|
||||||
gl_FragColor=vec4(color,1.);
|
gl_FragColor=vec4(color,1.);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
import noise from "~/assets/shaders/noise.frag?raw"
|
||||||
|
// @ts-ignore Import module
|
||||||
|
import resolveLygia from "https://lygia.xyz/resolve.esm.js"
|
||||||
|
|
||||||
|
|
||||||
|
declare const resolveLygia: (source: string) => string
|
||||||
|
|
||||||
|
let interval: number
|
||||||
|
let timeout: number
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
let fragmentShader = resolveLygia(noise);
|
||||||
|
|
||||||
|
let canvas: OffscreenCanvas
|
||||||
|
|
||||||
|
let render: (time: number) => void
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,151 +1,121 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import noise from "~/assets/shaders/noise.frag?raw"
|
|
||||||
|
|
||||||
const canvas = ref<HTMLCanvasElement | null>(null)
|
|
||||||
|
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
declare const webglUtils: {
|
import gsap from 'gsap';
|
||||||
createProgramFromSources: (gl: WebGLRenderingContext, array: [string, string]) => WebGLProgram,
|
import ShaderWorker from '~/assets/workers/ShaderWorker?worker'
|
||||||
resizeCanvasToDisplaySize: (e: HTMLCanvasElement | OffscreenCanvas) => any
|
import goStore from '~/netcode';
|
||||||
}
|
const worker = new ShaderWorker()
|
||||||
declare const resolveLygiaAsync: (source: string) => Promise<string>
|
|
||||||
|
const canvas = ref<HTMLCanvasElement | null>(null)
|
||||||
|
const canvas_resize_temp = ref<HTMLCanvasElement | null>(null)
|
||||||
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
colorStart?: [number, number, number]
|
colorStart?: [number, number, number]
|
||||||
colorEnd?: [number, number, number]
|
colorEnd?: [number, number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
colorStart: () => [0, .3, 0],
|
colorStart: () => [0, .3, 0],
|
||||||
colorEnd: () => [0, 1, 0]
|
colorEnd: () => [0, 1, 0]
|
||||||
})
|
})
|
||||||
|
|
||||||
const targetFramerate = 1000 / settingsStore.shaderFramerate
|
const _colorStart = reactive(props.colorStart)
|
||||||
|
const _colorEnd = reactive(props.colorEnd)
|
||||||
|
|
||||||
const interval = ref<ReturnType<typeof setInterval>>()
|
|
||||||
|
|
||||||
let fragmentShader = await resolveLygiaAsync(noise);
|
function initShader() {
|
||||||
|
if (!canvas.value) { return }
|
||||||
async function main(canvas: HTMLCanvasElement, fragmentShader2: string) {
|
const offscreen = canvas.value?.transferControlToOffscreen()
|
||||||
// Get A WebGL context
|
worker.postMessage({
|
||||||
/** @type {HTMLCanvasElement} */
|
command: "initShader",
|
||||||
const gl = canvas.getContext("webgl");
|
canvas: offscreen,
|
||||||
if (!gl) {
|
screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio },
|
||||||
return;
|
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
|
||||||
|
}, [offscreen])
|
||||||
|
if (settingsStore.shaderFramerate == 0) {
|
||||||
|
killShader()
|
||||||
}
|
}
|
||||||
|
resizeShader()
|
||||||
const vs = `attribute vec4 a_position;void main() {gl_Position = a_position;}`;
|
|
||||||
|
|
||||||
// setup GLSL program
|
|
||||||
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) {
|
|
||||||
time *= 0.001; // convert to seconds
|
|
||||||
webglUtils.resizeCanvasToDisplaySize(gl2.canvas);
|
|
||||||
|
|
||||||
// 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 bounds = canvas.getBoundingClientRect()
|
|
||||||
const aspectRatio = Math.max(window.screen.width, window.screen.height) * devicePixelRatio
|
|
||||||
|
|
||||||
gl2.uniform2f(offset, bounds.left, -bounds.bottom);
|
|
||||||
// console.log(canvas.getBoundingClientRect().left * window.devicePixelRatio)
|
|
||||||
gl2.uniform1i(quality, settingsStore.shaderQuality)
|
|
||||||
gl2.uniform2f(resolutionLocation, aspectRatio, aspectRatio);
|
|
||||||
gl2.uniform3f(colorStart, props.colorStart[0], props.colorStart[1], props.colorStart[2]);
|
|
||||||
gl2.uniform3f(colorEnd, props.colorEnd[0], props.colorEnd[1], props.colorEnd[2]);
|
|
||||||
gl2.uniform1f(timeLocation, time);
|
|
||||||
gl2.drawArrays(
|
|
||||||
gl2.TRIANGLES,
|
|
||||||
0, // offset
|
|
||||||
6, // num vertices to process
|
|
||||||
);
|
|
||||||
}
|
|
||||||
clearInterval(interval.value)
|
|
||||||
interval.value = setInterval(() => requestAnimationFrame(render), targetFramerate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recolorShader() {
|
||||||
async function reloadShader() {
|
worker.postMessage({
|
||||||
nextTick(() => {
|
command: 'recolorShader',
|
||||||
if (!canvas.value) { return }
|
colorStart: [..._colorStart],
|
||||||
let fragmentShader2 = fragmentShader.replace("#define FBM_OCTAVES 6", "#define FBM_OCTAVES " + settingsStore.shaderQuality);
|
colorEnd: [..._colorEnd]
|
||||||
clearInterval(interval.value)
|
|
||||||
main(canvas.value, fragmentShader2);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
watch(() => settingsStore.shaderQuality, (newValue, oldValue) => {
|
|
||||||
reloadShader()
|
function resizeShader() {
|
||||||
|
worker.postMessage({
|
||||||
|
command: 'resizeShader',
|
||||||
|
screen: { height: window.innerHeight * devicePixelRatio, width: window.innerWidth * devicePixelRatio, devicePixelRatio },
|
||||||
|
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function killShader() {
|
||||||
|
worker.postMessage({
|
||||||
|
command: 'killShader'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeShader() {
|
||||||
|
worker.postMessage({
|
||||||
|
command: 'resumeShader',
|
||||||
|
shaderOptions: { quality: settingsStore.shaderQuality, framerate: settingsStore.shaderFramerate },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => [settingsStore.shaderFramerate], () => {
|
||||||
|
if (settingsStore.shaderFramerate == 0) {
|
||||||
|
killShader()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resumeShader()
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => settingsStore.shaderFramerate, (newValue, oldValue) => {
|
watch(() => [settingsStore.shaderQuality], () => {
|
||||||
reloadShader()
|
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([_colorEnd, _colorStart], () => {
|
||||||
|
recolorShader()
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
reloadShader()
|
nextTick(() => {
|
||||||
|
recolorShader()
|
||||||
|
initShader()
|
||||||
|
window.addEventListener("resize", resizeShader, true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearInterval(interval.value)
|
window.removeEventListener("resize", resizeShader)
|
||||||
|
killShader()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<canvas v-if="settingsStore.shaderQuality != 0" ref="canvas" id="background">test</canvas>
|
<canvas ref="canvas" id="background" :class="{ pixelated: settingsStore.shaderPixelated }" width="480"
|
||||||
|
height="270">test</canvas>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -154,5 +124,10 @@ onUnmounted(() => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
image-rendering: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
#background.pixelated {
|
||||||
|
image-rendering: crisp-edges;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -6,6 +6,9 @@ export interface Props {
|
|||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _devicePixelRatio = computed(() => devicePixelRatio)
|
||||||
|
const _window = computed(() => window)
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -15,10 +18,6 @@ const close = () => {
|
|||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => settingsStore.shaderQuality, async () => {
|
|
||||||
console.log(settingsStore.shaderQuality)
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -40,27 +39,29 @@ watch(() => settingsStore.shaderQuality, async () => {
|
|||||||
<span>Shader Quality</span>
|
<span>Shader Quality</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<input type="range" min="0" max="10" v-model="settingsStore.shaderQuality">
|
<input type="range" min="1" max="10" v-model="settingsStore.shaderQuality"
|
||||||
|
:disabled="settingsStore.shaderFramerate == 0">
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<span>
|
<span>{{ Math.floor(_window.innerWidth * _devicePixelRatio * settingsStore.shaderQuality / 10) }}x{{
|
||||||
<template v-if="settingsStore.shaderQuality == 0">
|
Math.floor(_window.innerHeight *
|
||||||
Disabled
|
_devicePixelRatio * settingsStore.shaderQuality / 10) }}</span>
|
||||||
</template>
|
|
||||||
<template v-if="settingsStore.shaderQuality > 0">
|
|
||||||
{{ settingsStore.shaderQuality }}
|
|
||||||
</template>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<span>Shader Framerate</span>
|
<span>Shader Framerate</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<input type="range" min="10" max="60" step="5" v-model="settingsStore.shaderFramerate"
|
<input type="range" min="0" max="65" step="5" v-model="settingsStore.shaderFramerate">
|
||||||
:disabled="settingsStore.shaderQuality == 0">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<span>{{ settingsStore.shaderFramerate }} fps</span>
|
<span v-if="settingsStore.shaderFramerate == 65">vsync</span>
|
||||||
|
<span v-else>{{ settingsStore.shaderFramerate }} fps</span>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-header">
|
||||||
|
<span>Pixelated</span>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-body">
|
||||||
|
<input type="checkbox" v-model="settingsStore.shaderPixelated">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ function can_buy_fire_gem(): boolean {
|
|||||||
|
|
||||||
function checkEndTurn() {
|
function checkEndTurn() {
|
||||||
// TODO: Add a confirmation dialog if resources are left
|
// TODO: Add a confirmation dialog if resources are left
|
||||||
console.log("Checking end turn")
|
|
||||||
if (
|
if (
|
||||||
(!warning.value)
|
(!warning.value)
|
||||||
&& has_actions_remaining()) {
|
&& has_actions_remaining()) {
|
||||||
|
|||||||
@@ -12,13 +12,4 @@ export default defineNuxtConfig({
|
|||||||
'@pinia/nuxt',
|
'@pinia/nuxt',
|
||||||
'@pinia-plugin-persistedstate/nuxt',
|
'@pinia-plugin-persistedstate/nuxt',
|
||||||
],
|
],
|
||||||
app: {
|
|
||||||
head: {
|
|
||||||
script: [{
|
|
||||||
src: "/webgl-utils.js"
|
|
||||||
}, {
|
|
||||||
src: "https://lygia.xyz/resolve.js"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|||||||
Generated
+7
@@ -8,6 +8,7 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pinia/nuxt": "^0.5.1",
|
"@pinia/nuxt": "^0.5.1",
|
||||||
|
"gsap": "^3.12.5",
|
||||||
"nuxt": "^3.11.2",
|
"nuxt": "^3.11.2",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
@@ -5670,6 +5671,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/gsap": {
|
||||||
|
"version": "3.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz",
|
||||||
|
"integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ==",
|
||||||
|
"license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/"
|
||||||
|
},
|
||||||
"node_modules/gzip-size": {
|
"node_modules/gzip-size": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pinia/nuxt": "^0.5.1",
|
"@pinia/nuxt": "^0.5.1",
|
||||||
|
"gsap": "^3.12.5",
|
||||||
"nuxt": "^3.11.2",
|
"nuxt": "^3.11.2",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ enum CardStyles {
|
|||||||
export const useSettingsStore = defineStore("settingstore", {
|
export const useSettingsStore = defineStore("settingstore", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
cardStyle: CardStyles.HERON,
|
cardStyle: CardStyles.HERON,
|
||||||
shaderQuality: 0,
|
shaderQuality: 3,
|
||||||
shaderFramerate: 10,
|
shaderFramerate: 0,
|
||||||
|
shaderPixelated: true
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
switchCardStyles() {
|
switchCardStyles() {
|
||||||
|
|||||||
Reference in New Issue
Block a user