import { createWindow, getProcAddress, gl, } from "./deps.ts"; export {mainloop} from "./deps.ts"; import { COLOR, palette } from "./colors.ts"; export const gameWindow = createWindow({ title: "Faux", width: 512, height: 512, resizable: true, glVersion: [3, 2], gles: true, }); gl.load(getProcAddress); function loadShader(type: number, src: string) { const shader = gl.CreateShader(type); gl.ShaderSource( shader, 1, new Uint8Array( new BigUint64Array([ BigInt( Deno.UnsafePointer.value( Deno.UnsafePointer.of(new TextEncoder().encode(src)), ), ), ]).buffer, ), new Int32Array([src.length]), ); gl.CompileShader(shader); const status = new Int32Array(1); gl.GetShaderiv(shader, gl.COMPILE_STATUS, status); if (status[0] === gl.FALSE) { const logLength = new Int32Array(1); gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, logLength); const log = new Uint8Array(logLength[0]); gl.GetShaderInfoLog(shader, logLength[0], logLength, log); console.log(new TextDecoder().decode(log)); gl.DeleteShader(shader); return 0; } return shader; } const vShaderSrc = ` attribute vec4 vPosition; attribute vec4 vCol; varying vec4 color; void main() { gl_Position = vPosition; color = vCol; } `; const fShaderSrc = ` precision mediump float; varying vec4 color; void main() { gl_FragColor = color; } `; const vShader = loadShader(gl.VERTEX_SHADER, vShaderSrc); const fShader = loadShader(gl.FRAGMENT_SHADER, fShaderSrc); const program = gl.CreateProgram(); gl.AttachShader(program, vShader); gl.AttachShader(program, fShader); gl.BindAttribLocation(program, 0, new TextEncoder().encode("vPosition\0")); gl.BindAttribLocation(program, 1, new TextEncoder().encode("vCol\0")); gl.LinkProgram(program); const status = new Int32Array(1); gl.GetProgramiv(program, gl.LINK_STATUS, status); if (status[0] === gl.FALSE) { const logLength = new Int32Array(1); gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, logLength); const log = new Uint8Array(logLength[0]); gl.GetProgramInfoLog(program, logLength[0], logLength, log); console.log(new TextDecoder().decode(log)); gl.DeleteProgram(program); Deno.exit(1); } gl.ClearColor(0.0, 0.0, 0.0, 1.0); const pixelsPerRow = 128; const top = 1; const left = -1; const cell = 2/pixelsPerRow; const getHalfAsInt = (n: number) => Number(parseInt(Math.floor(n/2).toString())); addEventListener("resize", (event) => { const {width, height} = event; const min = Math.min(width, height); gl.Viewport(getHalfAsInt(width-min), getHalfAsInt(height-min), min, min); }); const px = (x: number, y: number) => { // deno-fmt-ignore return [ left + x*cell, top - y*cell, 0, left + (x+1)*cell, top - y*cell, 0, left + x*cell, top - (y+1)*cell, 0, left + (x+1)*cell, top - y*cell, 0, left + x*cell, top - (y+1)*cell, 0, left + (x+1)*cell, top - (y+1)*cell, 0, ]; } const paletteX6 = palette.map(rgba => [...rgba, ...rgba, ...rgba, ...rgba, ...rgba, ...rgba]); const c = (n: number) => { return paletteX6[n]; } const allPixelTriangles = new Float32Array( Array(pixelsPerRow*pixelsPerRow).fill(null).flatMap((_, i) => px(i%pixelsPerRow,Math.floor(i/pixelsPerRow))) ) const allPixelColors = new Float32Array( Array(pixelsPerRow*pixelsPerRow).fill(null).flatMap(() => c(1)) ) export const cameraPos = { x: 0, y: 0, } export const setPixelColorRaw = (x: number, y: number, color: number) => { if (x < 0 || y < 0 || x > 127 || y > 127) { return; } if (color !== 0) { const col = c(color); allPixelColors.set(col, 4*6*(128*y+x)); } } export const setPixelColor = (x: number, y: number, color: number) => { return setPixelColorRaw(Math.floor(x - cameraPos.x), Math.floor(y - cameraPos.y), color); } export const setPixelsInRect = (x: number, y: number, w: number, pixels: Array) => { for (let i = 0; i < pixels.length; i++) { setPixelColor(x+i%w, y+Math.floor(i/w), pixels[i]); } } export const setPixelsInRectRaw = (x: number, y: number, w: number, pixels: Array) => { for (let i = 0; i < pixels.length; i++) { setPixelColorRaw(x+i%w, y+Math.floor(i/w), pixels[i]); } } export const fillRect = (x: number, y: number, w: number, h: number, color: number) => { setPixelsInRect(x, y, w, Array(w*h).fill(color)); } export const fillCircle = (x: number, y: number, r: number, color: number) => { const left = Math.floor(x-r-1); const top = Math.floor(y-r-1); for (let i = left; i <= Math.ceil(x+r+1); i ++) { for (let j = top; j <= Math.ceil(y+r+1); j ++) { if (Math.sqrt((x-i)**2 + (y-j)**2) <= r+0.5) { setPixelColor(i, j, color); } } } } export const outlineCircle = (x: number, y: number, r: number, color: number) => { const left = Math.floor(x-r-1); const top = Math.floor(y-r-1); const inR = (d: number) => d <= r+0.5 && d > r-0.5; for (let i = left; i <= Math.ceil(x+r+1); i ++) { for (let j = top; j <= Math.ceil(y+r+1); j ++) { const d = Math.sqrt((x-i)**2 + (y-j)**2); if (inR(d)) { const dh = Math.sqrt((x-(i+Math.sign(i-x)))**2 + (y-j)**2); const dv = Math.sqrt((x-i)**2 + (y-(j+Math.sign(j-y)))**2); const h = Math.abs(x-i) > Math.abs(y-j); if (!inR(h ? dh : dv)) { setPixelColor(i, j, color); } } } } } export const fillEllipse = (x0: number, y0: number, x1: number, y1: number, color: number) => { const x = 0.5*(x0 + x1); const y = 0.5*(y0 + y1); const rx = Math.abs(x0-x1)/2; const ry = Math.abs(y0-y1)/2; const left = Math.floor(x-rx-1); const top = Math.floor(y-ry-1); for (let i = left; i <= Math.ceil(x+rx+1); i ++) { for (let j = top; j <= Math.ceil(y+ry+1); j ++) { if (Math.sqrt(((x-i)/rx)**2 + ((y-j)/ry)**2) <= 1+(1/(rx+ry))) { setPixelColor(i, j, color); } } } } export const outlineEllipse = (x0: number, y0: number, x1: number, y1: number, color: number) => { const x = 0.5*(x0 + x1); const y = 0.5*(y0 + y1); const rx = Math.abs(x0-x1)/2; const ry = Math.abs(y0-y1)/2; const left = Math.floor(x-rx-1); const top = Math.floor(y-ry-1); const inR = (d: number) => d <= 1+1/(rx+ry); for (let i = left; i <= Math.ceil(x+rx+1); i ++) { for (let j = top; j <= Math.ceil(y+ry+1); j ++) { const d = Math.sqrt(((x-i)/rx)**2 + ((y-j)/ry)**2); if (inR(d)) { const dh = Math.sqrt(((x-(i+Math.sign(i-x)))/rx)**2 + ((y-j)/ry)**2); const dv = Math.sqrt(((x-i)/rx)**2 + ((y-(j+Math.sign(j-y)))/ry)**2); if (!inR(dh) || !inR(dv)) { setPixelColor(i, j, color); } } } } } export const fillRectRaw = (x: number, y: number, w: number, h: number, color: number) => { setPixelsInRectRaw(x, y, w, Array(w*h).fill(color)); } export const clearScreen = (color?: number) => { fillRectRaw(0, 0, 128, 128, color ?? COLOR.BLACK); } export const frame = () => { gl.Clear(gl.COLOR_BUFFER_BIT); gl.UseProgram(program); gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 0, allPixelTriangles); gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 0, allPixelColors); gl.EnableVertexAttribArray(0); gl.EnableVertexAttribArray(1); gl.DrawArrays(gl.TRIANGLES, 0, 6*pixelsPerRow*pixelsPerRow); gameWindow.swapBuffers(); }