From 4fe88e99ca7f149641e5059e14b368a321d07a10 Mon Sep 17 00:00:00 2001 From: rockwillck Date: Tue, 22 Oct 2024 09:45:22 -0500 Subject: [PATCH] 3D Demo (#2486) * Add files via upload * Add files via upload * Rename 3D Demo.js to 3d_demo.js * Rename 3D Demo.png to 3d_demo.png * Update 3d_demo.js * Update 3d_demo.js --------- Co-authored-by: Cheru Berhanu --- games/3d_demo.js | 322 ++++++++++++++++++++++++++++++++++++++++++ games/img/3d_demo.png | Bin 0 -> 5395 bytes 2 files changed, 322 insertions(+) create mode 100644 games/3d_demo.js create mode 100644 games/img/3d_demo.png diff --git a/games/3d_demo.js b/games/3d_demo.js new file mode 100644 index 0000000000..5439336fa6 --- /dev/null +++ b/games/3d_demo.js @@ -0,0 +1,322 @@ +/* +@title: 3D Demo +@author: William Choi-Kim +@tags: [] +@addedOn: 2024-10-22 +*/ + +// rendering framework +setLegend( + [ "0", bitmap` +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000 +0000000000000000` ], + [ "1", bitmap` +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL +LLLLLLLLLLLLLLLL` ], + [ "2", bitmap` +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111` ], + [ "3", bitmap` +................ +................ +................ +................ +................ +................ +................ +................ +................ +................ +................ +................ +................ +................ +................ +................` ], + [ "e", bitmap` +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333 +3333333333333333` ] +) + +function cleanCanvas() { + let c = new Array(128) + for (let j = 0; j < c.length; j++) { + c[j] = new Array(160).fill(3) + } + return c +} + +var globalCanvas = cleanCanvas() +function renderCanvas() { + let outputMap = globalCanvas.map(x => (x == undefined) ? 4 : x).map(x => x.join("")) + let cropped = outputMap.map(row => row.slice(0, 160)).join("\n") + setMap(map`${cropped}`) +} +renderCanvas(cleanCanvas()) + +function clearCanvas() { + globalCanvas = cleanCanvas() +} +function drawLine(pt1, pt2, color=0) { + let x1 = pt1[0] + let y1 = pt1[1] + let x2 = pt2[0] + let y2 = pt2[1] + + let biggerX = Math.max(x1, x2) + let smallerX = Math.min(x1, x2) + let biggerY = Math.max(y1, y2) + let smallerY = Math.min(y1, y2) + + if (y2 == y1) { + // horizontal + for (let x = Math.min(x1, x2); x <= Math.max(x1, x2); x++) { + try { + globalCanvas[y1][x] = color + } catch (e) { + break + } + } + } else if (x2 == x1) { + // vertical + for (let y = Math.min(y1, y2); y <= Math.max(y1, y2); y++) { + try { + globalCanvas[y][x1] = color + } catch (e) { + break + } + } + } else if ((biggerX - smallerX) > (biggerY - smallerY)) { + // less steep than y = x + for (let y = smallerY; y < biggerY; y++) { + let xi = Math.round(x1 + (y - y1)/(y2 - y1)*(x2 - x1)) + let xf = Math.round(x1 + (y + 1 - y1)/(y2 - y1)*(x2 - x1)) + + for (let x = Math.min(xi, xf); x <= Math.max(xi, xf); x++) { + try { + globalCanvas[y][x] = color + } catch (e) { + break + } + } + } + } else { + // more steep than y = x + for (let x = smallerX; x < biggerX; x++) { + let yi = Math.round(y1 + (x - x1)/(x2 - x1)*(y2 - y1)) + let yf = Math.round(y1 + (x + 1 - x1)/(x2 - x1)*(y2 - y1)) + + for (let y = Math.min(yi, yf); y <= Math.max(yi, yf); y++) { + try { + globalCanvas[y][x] = color + } catch (e) { + break + } + } + } + } + + try { + globalCanvas[y1][x1] = color + } catch (e) {} + try { + globalCanvas[y2][x2] = color + } catch (e) {} +} + +function translatePt(pt, x, y) { + return [pt[0] + x, pt[1] + y] +} + +// simple perspective 3d renderer + +function getProjectedPoints(pts, focalLength, scale=1) { + let resultingPts = new Array(pts.length) + let cameraLoc = [0, -focalLength, 0] + + for (let i = 0; i < pts.length; i++) { + let pt = pts[i] + let dist = pt[2] + focalLength + resultingPts[i] = [ + Math.round(pt[0]/dist*scale), + Math.round(pt[1]/dist*scale) + ] + } + return resultingPts +} + +function drawFaces(faces, focalLength, scale=1, translate=[80,64], color=0) { + for (let face of faces) { + let ppts = getProjectedPoints(face, focalLength, scale) + for (let k = 0; k < face.length; k++) { + drawLine(translatePt(ppts[k], translate[0], translate[1]), translatePt(ppts[(k + 1) % face.length], translate[0], translate[1]), color) + } + } +} + +// game logic starts + +function rotateProfile(profile, resolution=10) { + let outputF = [] + let radii = profile.map(p => p[0]) + let maxrad = Math.max(...radii) + for (let j = 0; j < resolution; j++) { + let theta1 = 2*Math.PI/resolution * j + let theta2 = 2*Math.PI/resolution * ((j + 1) % resolution) + for (let p = 0; p < profile.length - 1; p++) { + outputF.push([ + [Math.cos(theta1)*profile[p][0], profile[p][1], maxrad + 1 + Math.sin(theta1)*profile[p][0]], + [Math.cos(theta2)*profile[p][0], profile[p][1], maxrad + 1 + Math.sin(theta2)*profile[p][0]], + [Math.cos(theta2)*profile[p + 1][0], profile[p + 1][1], maxrad + 1 + Math.sin(theta2)*profile[p + 1][0]], + [Math.cos(theta1)*profile[p + 1][0], profile[p + 1][1], maxrad + 1 + Math.sin(theta1)*profile[p + 1][0]] + ]) + } + } + return outputF +} + +function teapotAndLid(p1, p2) { + return [ + // pot + rotateProfile([ + [3 + p2, 0], + [4 + p2, 0.2], + [4.5 + p1, 1.3], + [4, 2.3], + [3, 2.5] + ], 20), + // lid + rotateProfile([ + [3.1 + p2, -.05], + [2.5 + p2, -.3], + [0.2, -.45], + [0.3, -.8], + [0.2, -.85] + ], 10) + ] +} +function cylinder(p1, p2) { + return [ + rotateProfile([ + [4 + p1, -1.5], + [4 + p2, 1.5] + ], 20) + ] +} +function cone(p1, p2) { + return [ + rotateProfile([ + [p1, -4], + [4 + p2, 2] + ], 20) + ] +} + +var polyI = 0 +var polyhedra = [ + teapotAndLid, + cylinder, + cone +] +var param1 = 0 +var param2 = 0 +var color = 0 +function updateScreen() { + clearCanvas() + + for (let p of polyhedra[polyI](param1, param2)) { + drawFaces(p, 1, 50, [80, 64], color) + } + renderCanvas() +} +updateScreen() + +onInput("s", () => { + color = (color + 2) % 3 +}) +onInput("w", () => { + color = (color + 1) % 3 +}) +onInput("d", () => { + polyI = (polyI + 1)%polyhedra.length + param1 = 0 + param2 = 0 +}) +onInput("a", () => { + polyI = (polyI - 1 + polyhedra.length)%polyhedra.length + param1 = 0 + param2 = 0 +}) + +onInput("i", () => { + param2 += 0.1 +}) +onInput("k", () => { + param2 -= 0.1 +}) +onInput("j", () => { + param1 -= 0.1 +}) +onInput("l", () => { + param1 += 0.1 +}) + +afterInput(() => { + updateScreen() +}) diff --git a/games/img/3d_demo.png b/games/img/3d_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..6aba5d223940b555ac82d91dc8fa9dd0d003d15d GIT binary patch literal 5395 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 Wgcf|+ZokNc9o&C-A7k(5U;G=5iqo