|
|
|
@ -1,31 +1,54 @@
|
|
|
|
|
let gameState = {
|
|
|
|
|
running: false,
|
|
|
|
|
pixelSize: 0,
|
|
|
|
|
lastReported: null,
|
|
|
|
|
frameTimes: [],
|
|
|
|
|
frames: 0,
|
|
|
|
|
width: 0,
|
|
|
|
|
height: 0,
|
|
|
|
|
canvas: null,
|
|
|
|
|
ctx: null,
|
|
|
|
|
gameExports: null
|
|
|
|
|
gl: null,
|
|
|
|
|
shader: null,
|
|
|
|
|
gameExports: null,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const initialMessage = "Click the board above to start simulation"
|
|
|
|
|
|
|
|
|
|
export async function setup(params, wasmModule) {
|
|
|
|
|
const { canvas: canvasSelector } = params;
|
|
|
|
|
const { canvas: canvasSelector, fragmentSrc, vertexSrc } = params;
|
|
|
|
|
gameState.gameExports = wasmModule.exports
|
|
|
|
|
|
|
|
|
|
const canvas = gameState.canvas = document.querySelector(canvasSelector)
|
|
|
|
|
gameState.ctx = gameState.canvas.getContext("2d")
|
|
|
|
|
gameState.ctx.fillStyle = getComputedStyle(gameState.canvas).getPropertyValue('--foreground')
|
|
|
|
|
const gl = gameState.gl = gameState.canvas.getContext("webgl2")
|
|
|
|
|
|
|
|
|
|
const pixelSize = gameState.pixelSize = parseInt(canvas.getAttribute('data-pixelsize') || '2')
|
|
|
|
|
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
|
|
|
|
|
gl.shaderSource(vertexShader, await fetch(vertexSrc.path).then(res => res.text()))
|
|
|
|
|
gl.compileShader(vertexShader)
|
|
|
|
|
|
|
|
|
|
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
|
|
|
|
|
gl.shaderSource(fragmentShader, await fetch(fragmentSrc.path).then(res => res.text()))
|
|
|
|
|
gl.compileShader(fragmentShader)
|
|
|
|
|
|
|
|
|
|
const program = gameState.shader = gl.createProgram()
|
|
|
|
|
gl.attachShader(program, vertexShader)
|
|
|
|
|
gl.attachShader(program, fragmentShader)
|
|
|
|
|
gl.linkProgram(program)
|
|
|
|
|
gl.useProgram(program)
|
|
|
|
|
|
|
|
|
|
const renderQuad = new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1])
|
|
|
|
|
const vertexBuffer = gl.createBuffer()
|
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
|
|
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, renderQuad, gl.STATIC_DRAW)
|
|
|
|
|
|
|
|
|
|
const positionLocation = gl.getAttribLocation(program, "position")
|
|
|
|
|
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)
|
|
|
|
|
gl.enableVertexAttribArray(positionLocation);
|
|
|
|
|
|
|
|
|
|
const pixelSize = parseInt(canvas.getAttribute('data-pixelsize') || '2')
|
|
|
|
|
gameState.width = Math.floor(parseInt(canvas.width) / pixelSize)
|
|
|
|
|
gameState.height = Math.floor(parseInt(canvas.height) / pixelSize)
|
|
|
|
|
|
|
|
|
|
initialize()
|
|
|
|
|
onThemeChange()
|
|
|
|
|
drawBoard()
|
|
|
|
|
|
|
|
|
|
const frameTimesElem = document.getElementById('frameTimes')
|
|
|
|
@ -58,8 +81,16 @@ export async function setup(params, wasmModule) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function onThemeChange() {
|
|
|
|
|
const {gl, shader} = gameState
|
|
|
|
|
|
|
|
|
|
const drawColorString = getComputedStyle(gameState.canvas).getPropertyValue('color')
|
|
|
|
|
const [_match, ...colorValues] = /rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)/.exec(drawColorString)
|
|
|
|
|
const drawColor = colorValues.map(n => parseFloat(n) / 255)
|
|
|
|
|
|
|
|
|
|
const drawColorLocation = gl.getUniformLocation(shader, "drawColor")
|
|
|
|
|
gl.uniform3fv(drawColorLocation, drawColor)
|
|
|
|
|
|
|
|
|
|
drawBoard()
|
|
|
|
|
gameState.ctx.fillStyle = getComputedStyle(gameState.canvas).getPropertyValue('--foreground')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function cleanup() {
|
|
|
|
@ -122,25 +153,26 @@ function frameLoop() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawBoard() {
|
|
|
|
|
const { gameExports, width, height, pixelSize, ctx, canvas } = gameState
|
|
|
|
|
|
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
|
|
|
ctx.beginPath()
|
|
|
|
|
for (let row = 0; row < height; row++) {
|
|
|
|
|
for (let column = 0; column < width; column++) {
|
|
|
|
|
const alive = gameExports.getValueAtPosition(row, column)
|
|
|
|
|
|
|
|
|
|
if (!alive) continue
|
|
|
|
|
|
|
|
|
|
const x = column * pixelSize
|
|
|
|
|
const y = row * pixelSize
|
|
|
|
|
|
|
|
|
|
ctx.moveTo(x, y)
|
|
|
|
|
ctx.lineTo(x + pixelSize, y)
|
|
|
|
|
ctx.lineTo(x + pixelSize, y + pixelSize)
|
|
|
|
|
ctx.lineTo(x, y + pixelSize)
|
|
|
|
|
ctx.lineTo(x, y)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ctx.fill()
|
|
|
|
|
const { gameExports, width, height, gl } = gameState
|
|
|
|
|
const wasmBuffer = gameExports.shared_memory.buffer
|
|
|
|
|
const boardPointer = gameExports.getBoardPointer()
|
|
|
|
|
const boardLength = gameExports.getBoardLength()
|
|
|
|
|
const boardData = (new Uint8Array(wasmBuffer)).slice(boardPointer, boardPointer + boardLength)
|
|
|
|
|
|
|
|
|
|
const texture = gl.createTexture()
|
|
|
|
|
gl.activeTexture(gl.TEXTURE0)
|
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, texture)
|
|
|
|
|
gl.pixelStorei( gl.UNPACK_ALIGNMENT, 1 )
|
|
|
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, width, height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, boardData)
|
|
|
|
|
|
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gl.clearColor(1, 1, 1, 0)
|
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT)
|
|
|
|
|
gl.drawArrays(gl.TRIANGLES, 0, 6)
|
|
|
|
|
}
|
|
|
|
|