From adc3a38395d764bb5fbe7bf1cede28e802cac185 Mon Sep 17 00:00:00 2001 From: Noelle Calliope <1581674+mekanoe@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:17:15 -0400 Subject: [PATCH] switch to bun build instead of statics --- .gitignore | 1 + README.md | 2 +- build.ts | 30 +++++ bun.lockb | Bin 53575 -> 53631 bytes generate.ts | 37 ++++++ generators/generate.ts | 46 ------- generators/index.html.txt | 54 +++++++- generators/work.html.txt | 2 +- html/001-platform-provenance.html | 31 ----- html/002-rainbow-trinity.html | 31 ----- html/002-rainbow-trinity.js | 74 ----------- html/index.html | 37 ------ package.json | 5 +- .../001-platform-provenance/main.ts | 6 +- src/002-enter-the-third/main.ts | 36 ++++++ {html/lib => src}/app.js | 4 +- {html/lib => src}/basic-plane.js | 0 src/gltf-object.js | 12 ++ src/network-object.js | 23 ++++ {html/lib => src}/object.js | 0 src/ply-object.js | 116 ++++++++++++++++++ {html => src/public}/index.css | 0 {html => src/public}/work.css | 0 {html/lib => src}/shader.js | 0 {html/lib => src}/telemetry.js | 0 src/webgpu-app.js | 40 ++++++ 26 files changed, 357 insertions(+), 230 deletions(-) create mode 100644 build.ts create mode 100644 generate.ts delete mode 100644 generators/generate.ts delete mode 100644 html/001-platform-provenance.html delete mode 100644 html/002-rainbow-trinity.html delete mode 100644 html/002-rainbow-trinity.js delete mode 100644 html/index.html rename html/001-platform-provenance.js => src/001-platform-provenance/main.ts (93%) create mode 100644 src/002-enter-the-third/main.ts rename {html/lib => src}/app.js (97%) rename {html/lib => src}/basic-plane.js (100%) create mode 100644 src/gltf-object.js create mode 100644 src/network-object.js rename {html/lib => src}/object.js (100%) create mode 100644 src/ply-object.js rename {html => src/public}/index.css (100%) rename {html => src/public}/work.css (100%) rename {html/lib => src}/shader.js (100%) rename {html/lib => src}/telemetry.js (100%) create mode 100644 src/webgpu-app.js diff --git a/.gitignore b/.gitignore index ab5afb2..3194a2a 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,4 @@ dist # Finder (MacOS) folder config .DS_Store +html \ No newline at end of file diff --git a/README.md b/README.md index 882a947..3da9110 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ https://art.mekanoe.com ## Artworks - [./001-platform-provenance](https://art.mekanoe.com/001-platform-provenance) -- [./002-rainbow-trinity](https://art.mekanoe.com/002-rainbow-trinity) +- [./002-enter-the-third](https://art.mekanoe.com/002-enter-the-third) ## Development diff --git a/build.ts b/build.ts new file mode 100644 index 0000000..f9887e5 --- /dev/null +++ b/build.ts @@ -0,0 +1,30 @@ +import chalk from "chalk"; +import { generate } from "./generate"; +import { globSync } from "glob"; +import { rmSync, mkdirSync, cpSync } from "node:fs"; + +console.log(chalk.green`>> Cleaing up ./html ...`); +rmSync("html", { recursive: true, force: true }); +mkdirSync("html"); + +const works = globSync("src/*/main.ts"); + +console.log(chalk.green`>> Building ...`); +console.log(chalk.yellow(` Found ${works.length} works.`)); + +await Bun.build({ + entrypoints: works, + outdir: "html", + splitting: true, +}); + +console.log(chalk.green`>> Generating HTML and Markdown ...`); +await generate(works); + +console.log(chalk.green`>> Copying public files ...`); +const publics = globSync("src/public/*"); +for (const file of publics) { + const dest = file.replace("src/public/", "html/"); + cpSync(file, dest); + console.log(chalk.yellow(` -> html/${dest}...`)); +} diff --git a/bun.lockb b/bun.lockb index c72ae5c7b7ecb6695c3c5feca17a14b29b9bd895..01b6040677c82d9113b63ff9899372aaa9bd3227 100755 GIT binary patch delta 3480 zcmeH}&2Lmy7>B1bh@BP%lu}B?DO6j!pwJJ#6hBBsTd6cEn1E6!72C!d+lmNO9Z>PR z+InayC`u~O;tDf~(VaV2#;proxzLT#KfpwvXXZ{aCbcofW#8nP-#zbn?>Xnb_nmX@ zH&cl}rxK&PihldNqb?b~9NH4DEly^>N)A`2uaCuIQVKUg9F{>5l)(b10BtVyzRGM= zPK&v&j@q-SXO%IqaqC#8dGiF-4JWkCf(drMTI#L_Bo}E#<9o1C(ABuDr zsugN6?Z?1%?*-%cfu`!;Zv#U^X>&z(4V^hxY&La_1K_=fz;-uibEyT<1IG7)Hj8HG z|69j(1U>^M;aSiM)d2@U9R%Z^15I5B$H4`^2=>1WTA^ARr(?PKxyY69TSj533wh55 zHk-PD_p`k@b)qqF;xl0XIB12ce*)CA(fqNqG`#UVXoYGjKL#hb80i$M6{`K0z=1DE zdIi-~{nL@opjsGhSdg~kYPR{$YPTtq|9KN=j9IwVpv|T(kX;~~f%#2feiO)+^86-H z_~-YpWBb2u0;}fS1Tqzyni`f4?QxczolL|R#lv7vZ5ZfTmYM8nsVVw8{4laR?f$DB z1;=zjC7`_k`rPRjfp!e^xyvm9&HY=STksp8odA7~@4l}MgJZcVMF(LzyU82qrqT)q zxXtQI!bZ?ef<70w7PM2K&mC?RXs>`i7kn#duY$fCAO$Y|H7IhX8@ccsNjnV&E{2al zdmZ%owD|+ar_{F;j(|`3CYU|PGMoC|2IFo9GpFwz@FD#k>-0%YhbNXdmqGrN8s*xX zmxI?Xz!S^3a_9mV5Wu($uXWI37su{Nuyw?S!^GBOsd6VD*Jl&k+7H~or za1OlLG&`X_Pd$C6**M4e5cHX5JP*_U-yMeS0s# zAPj-uG~Mtd9EPXBn(csx;Stykd!QLM!lrO_tTJ8Cl}~7#6EF;Uzx6o=)|D~(RmuCut7x2o@kl37ne;AfTi73(irO8$CsSJ22GFmEVYirxEXt5Nlc&%VTigMAKfU#PNr9>2ph+MQzv8eS@ zJM}=Vmr86Y?Se$jB(h>f4AI2B3)jYl3*$ea&olFV$t)V;#$Dg!ncq3@_kQP`?>)Eq zXR7q-RB3iw^3wO)SEm|B5(TU7Sh4C#Nh0xPRFCEA%-|nG$+AR${SOC{xtY{xZKjN8 zvJ{p9ZzYyP64G!TtN`s=>U+26qYAp1a!pj9Pi?=}P+zQz0*f^nE;m-K_d!GSv{ ztIObY-dj)yJRH?k2NG|Jk*NP7M_~Y;L@%gzlm$Bu-~i8nR;>EZf;tYG>U*DC!HU&^ zO?L8Q2j?RF2Gxqy=hI-r=Odj#HC6wGNN3SRc(u7ApV&ETSge-Kqx{diyRgS<>=9EP zW-a(Rb)XgN!tPEa7L${O-Axi)B*nX$YQDa(yYsvIM)MKx>bYEHYfJMo_t)@`KF}(A zOTyv4hS1-a&Q0`ftWRDHzm0Teyw0>Yz#NMwf#&IIBOX{cgLVS+d04#%+DXv22t27Z zPh{WoWQnhbB+Mtp^&Ef=o4!hPb)x56kmfHAO>+n^g9Kz!d+P|`Z)sW|{IB?C?Kr=af(&}W*Bvk|kV&ompS?<>$(1rE%1CqZ8| z=(CNl0pF6uueOGDhM%&v;je7Eae$gj#oP?SDR8ETpbz%L%Wwe7Li5=2Koy4I0(+Vv+SfgiElwa90vb+^uS(t33|b0x(#-~PIv}7p#|2#z0eRYkFCu3Q6|R^ z@k6WOE^s-R{Y}sghrnc-JdCPI1cV#t=HcKA71cW58y@EhNs`+Iz@JoVjd!CfAvXWPT?vpuP{ Oxq|20+|IeC)&BzDR}PK< diff --git a/generate.ts b/generate.ts new file mode 100644 index 0000000..e2a395f --- /dev/null +++ b/generate.ts @@ -0,0 +1,37 @@ +import indexTemplate from "./generators/index.html.txt"; +import workTemplate from "./generators/work.html.txt"; +import readmeTemplate from "./generators/README.md.txt"; +import chalk from "chalk"; + +export const generate = async (works: string[]) => { + const allWorks = works + .map((file) => file.replace("src/", "").replace("/main.ts", "")) + .sort() + .reverse(); + + for (const work of allWorks) { + const html = `${workTemplate}`.replace(/##name##/g, work); + + await Bun.write(`html/${work}/index.html`, html); + console.log(chalk.yellow(` -> html/${work}/index.html...`)); + } + + const index = indexTemplate.replace( + "", + allWorks + .map((work) => `
  • ./${work}
  • `) + .join("\n ") + ); + await Bun.write("html/index.html", index); + console.log(chalk.yellow(` -> html/index.html...`)); + + const readme = readmeTemplate.replace( + "", + allWorks + .reverse() + .map((work) => `- [./${work}](https://art.mekanoe.com/${work})`) + .join("\n") + ); + await Bun.write("README.md", readme); + console.log(chalk.yellow(` -> README.md...`)); +}; diff --git a/generators/generate.ts b/generators/generate.ts deleted file mode 100644 index f5aee09..0000000 --- a/generators/generate.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { unlinkSync } from "node:fs"; -import { globSync } from "glob"; -import indexTemplate from "./index.html.txt"; -import workTemplate from "./work.html.txt"; -import readmeTemplate from "./README.md.txt"; - -const allHtmls = globSync("html/*.html").filter( - (file) => file !== "html/index.html" -); -for (const htmlFile of allHtmls) { - unlinkSync(htmlFile); -} - -const allWorks = globSync("html/*.js") - .map((file) => file.replace("html/", "").replace(".js", "")) - .sort() - .reverse(); - -console.log({ allWorks }); - -for (const work of allWorks) { - const html = `${workTemplate}`.replace(/##name##/g, work); - - await Bun.write(`html/${work}.html`, html); -} - -const index = indexTemplate.replace( - "", - allWorks - .map((work) => `
  • ./${work}
  • `) - .join("\n ") -); -await Bun.write("html/index.html", index); - -const readme = readmeTemplate.replace( - "", - allWorks - .reverse() - .map((work) => `- [./${work}](https://art.mekanoe.com/${work})`) - .join("\n") -); -await Bun.write("README.md", readme); - -console.log( - `index.html & README.md generated. ${allWorks.length} works found.` -); diff --git a/generators/index.html.txt b/generators/index.html.txt index bfc50d2..76a8408 100644 --- a/generators/index.html.txt +++ b/generators/index.html.txt @@ -16,8 +16,60 @@ line-height: 1.5; text-shadow: 1px 1px 3px hsl(38, 45%, 22%); } + + * { + box-sizing: border-box; + transition: all 0.2s ease-in-out; + } + + header { + display: flex; + font-size: 2.5rem; + align-items: center; + + & .subtext { + font-size: 1rem; + margin-left: 0.5rem; + color: hsl(39, 68.6%, 31.2%); + & a:hover { + color: hsl(39, 100%, 80%); + } + } + } + + a { + color: inherit; + text-decoration: none; + } + + ul { + list-style: none; + padding: 0; + margin: 0; + padding-left: 3rem; + display: flex; + flex-direction: column; + font-size: 1.5rem; + } + + li { + & a { + text-decoration: underline; + text-decoration-color: transparent; + } + &:hover { + color: hsl(39, 100%, 80%); + & a { + text-decoration-color: inherit; + } + } + + &::before { + content: "▸"; + margin-right: 0.5rem; + } + } -
    com.mekanoe.art //
    diff --git a/generators/work.html.txt b/generators/work.html.txt index f0d72e3..3de1398 100644 --- a/generators/work.html.txt +++ b/generators/work.html.txt @@ -28,4 +28,4 @@ crossorigin="anonymous" referrerpolicy="no-referrer" > - + diff --git a/html/001-platform-provenance.html b/html/001-platform-provenance.html deleted file mode 100644 index e2f81e5..0000000 --- a/html/001-platform-provenance.html +++ /dev/null @@ -1,31 +0,0 @@ - - -com.mekanoe.art // 001-platform-provenance - - -
    - -
    XX.X FPS (XX.X ms)
    -
    - - diff --git a/html/002-rainbow-trinity.html b/html/002-rainbow-trinity.html deleted file mode 100644 index e43e463..0000000 --- a/html/002-rainbow-trinity.html +++ /dev/null @@ -1,31 +0,0 @@ - - -com.mekanoe.art // 002-rainbow-trinity - - -
    - -
    XX.X FPS (XX.X ms)
    -
    - - diff --git a/html/002-rainbow-trinity.js b/html/002-rainbow-trinity.js deleted file mode 100644 index 1402215..0000000 --- a/html/002-rainbow-trinity.js +++ /dev/null @@ -1,74 +0,0 @@ -import { Shader, colorUtils, commonFrag, commonVert } from "./lib/shader.js"; -import { BasicPlane } from "./lib/basic-plane.js"; -import { App } from "./lib/app.js"; - -const app = new App({ fov: 20 }); -const gl = app.gl; - -const shader = new Shader(app) - .attach( - gl.VERTEX_SHADER, - ` - ${commonVert} - - varying highp vec2 vTextureCoord; - - void main() { - gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; - vTextureCoord = aTextureCoord; - } - ` - ) - .attach( - gl.FRAGMENT_SHADER, - ` - ${commonFrag} - ${colorUtils} - - varying highp vec2 vTextureCoord; - - vec3 circle(vec2 pos, vec3 color) { - float radius = 0.75; - float dist = length(pos); - float circle = smoothstep(radius + 0.001, radius, dist); - return color * vec3(1.0 - circle); - } - - vec3 colorRotator(vec3 color, float phase, vec2 uv) { - color.x += uSinTime * 0.0001 + uv.y; - vec3 hsv = rgb2hsv(color); - hsv.x += uSinTime * 0.001 * uv.y - phase; - hsv.y = 1.0; - hsv.z = 0.5; - vec3 rgb = hsv2rgb(hsv); - return rgb; - } - - void main() { - float value = sin(uTime * 0.00025) * 0.5 + 0.5; - vec2 sv = vec2(1.0, 1.0); - vec3 triUpper = hsv2rgb(vec3(value, sv)); - vec3 triLeft = hsv2rgb(vec3(value + 0.33, sv)); - vec3 triRight = hsv2rgb(vec3(value + 0.66, sv)); - - vec2 uv = vTextureCoord * 2.0 - 1.0; - vec3 color = vec3(0.0); - - // place 3 circles in a triangle - color += circle(vec2(uv.x, uv.y + 0.5), colorRotator(triUpper, 1.0, uv)); - color += circle(vec2(uv.x - 0.5, uv.y - 0.33), colorRotator(triLeft, 0.667, uv)); - color += circle(vec2(uv.x + 0.5, uv.y - 0.33), colorRotator(triRight, 0.333, uv)); - - // color = 1.0 - color; - - gl_FragColor = vec4(color, 1.0); - gl_FragColor = clamp(gl_FragColor, 0.0, 1.0); - } - ` - ) - .link(); - -const plane = new BasicPlane(app); -plane.attachShader(shader); - -app.loop(); diff --git a/html/index.html b/html/index.html deleted file mode 100644 index f78e35d..0000000 --- a/html/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - -com.mekanoe.art // - - - - - -
    -
    -
    com.mekanoe.art //
    -
    - << noeidelon >> - [github] - [fedi] -
    a collection of 3D works -
    -
    -
    - -
    -
    diff --git a/package.json b/package.json index 80d6bc1..7e550c6 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "noeidelon", "type": "module", - "main": "./generators/generate.js", + "main": "./build.ts", "scripts": { - "build": "bun $BUNFLAGS ./generators/generate.ts", + "build": "bun $BUNFLAGS .", "build:watch": "BUNFLAGS=--watch bun run build", "serve": "serve ./html", "dev": "run-p serve build:watch" @@ -17,6 +17,7 @@ "typescript": "^5.2.2" }, "dependencies": { + "chalk": "^5.3.0", "glob": "^10.3.10", "serve": "^14.2.1" } diff --git a/html/001-platform-provenance.js b/src/001-platform-provenance/main.ts similarity index 93% rename from html/001-platform-provenance.js rename to src/001-platform-provenance/main.ts index 26bff08..9d3ffbb 100644 --- a/html/001-platform-provenance.js +++ b/src/001-platform-provenance/main.ts @@ -1,6 +1,6 @@ -import { Shader } from "./lib/shader.js"; -import { BasicPlane } from "./lib/basic-plane.js"; -import { App } from "./lib/app.js"; +import { Shader } from "../shader"; +import { BasicPlane } from "../basic-plane"; +import { App } from "../app"; const app = new App({ fov: 20 }); const gl = app.gl; diff --git a/src/002-enter-the-third/main.ts b/src/002-enter-the-third/main.ts new file mode 100644 index 0000000..646d921 --- /dev/null +++ b/src/002-enter-the-third/main.ts @@ -0,0 +1,36 @@ +import { Shader } from "../shader"; +import { BasicPlane } from "../basic-plane"; +import { App } from "../app"; + +const app = new App({ fov: 20 }); +const gl = app.gl; + +const shader = new Shader(app) + .attach( + gl.VERTEX_SHADER, + ` + attribute vec4 aVertexPosition; + attribute vec2 aTextureCoord; + + uniform mat4 uModelViewMatrix; + uniform mat4 uProjectionMatrix; + + void main() { + gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; + } + ` + ) + .attach( + gl.FRAGMENT_SHADER, + ` + void main() { + gl_FragColor = vec4(1.0); + } + ` + ) + .link(); + +const plane = new BasicPlane(app); +plane.attachShader(shader); + +app.loop(); diff --git a/html/lib/app.js b/src/app.js similarity index 97% rename from html/lib/app.js rename to src/app.js index 33bd0f5..30db9c5 100644 --- a/html/lib/app.js +++ b/src/app.js @@ -111,9 +111,7 @@ export class App { requestAnimationFrame(run); }; - requestAnimationFrame((now) => { - run(now); - }); + requestAnimationFrame(run); } now() { diff --git a/html/lib/basic-plane.js b/src/basic-plane.js similarity index 100% rename from html/lib/basic-plane.js rename to src/basic-plane.js diff --git a/src/gltf-object.js b/src/gltf-object.js new file mode 100644 index 0000000..2148503 --- /dev/null +++ b/src/gltf-object.js @@ -0,0 +1,12 @@ +import { NetworkObject } from "./network-object"; + +export class glTFObject extends NetworkObject { + constructor(app) { + super(app); + this.register(); + } + + async handleModelData(response) { + const raw = await response.json(); + } +} diff --git a/src/network-object.js b/src/network-object.js new file mode 100644 index 0000000..1281a62 --- /dev/null +++ b/src/network-object.js @@ -0,0 +1,23 @@ +import { Object } from "./object"; + +export class NetworkObject extends Object { + constructor(app) { + super(app); + + this.loaded = false; + } + + register() { + app.onUpdate(() => { + if (this.loaded) { + this.draw3D(); + } + }); + } + + async load(url) { + const response = await fetch(url); + await this.handleModelData(response); + this.loaded = true; + } +} diff --git a/html/lib/object.js b/src/object.js similarity index 100% rename from html/lib/object.js rename to src/object.js diff --git a/src/ply-object.js b/src/ply-object.js new file mode 100644 index 0000000..46c8ae4 --- /dev/null +++ b/src/ply-object.js @@ -0,0 +1,116 @@ +import { NetworkObject } from "./network-object"; + +export class PlyObject extends NetworkObject { + constructor(app) { + super(app); + this.register(); + } + + async handleModelData(response) { + const raw = await response.text(); + + const config = { + vertex: { + count: 0, + properties: [], + }, + face: { + count: 0, + properties: [], + }, + }; + + const data = { + vertexes: [], + faces: [], + }; + + let phase = "header"; // "vertex", "face", ... + let currentField = null; + + const headerHandlers = { + ply: () => {}, + format: (ascii, version) => { + if (ascii !== "ascii") { + throw new Error("Only ascii ply files are supported"); + } + + if (version !== "1.0") { + throw new Error("Only version 1.0 ply files are supported"); + } + }, + comment: () => {}, + element: (which, value) => { + currentField = which; + if (which === "vertex") { + config.vertex.count = Number(value); + } else if (which === "face") { + config.face.count = Number(value); + } + }, + property: (what, ...data) => { + if (what === "float") { + config[currentField].properties.push({ + name: data[1], + type: "float", + }); + } else if (what === "list") { + config[currentField].properties.push({ + name: data[2], + type: "list", + indexType: data[0], + valueType: data[1], + }); + } + }, + end_header: () => { + phase = "vertex"; + }, + }; + + const lines = raw.split("\n"); + for (const line of lines) { + const parts = line.split(" "); + + if (phase === "header") { + const handler = headerHandlers[parts[0]]; + if (handler) { + handler(...parts.slice(1)); + } + + continue; + } + + if (phase === "vertex") { + const vertex = {}; + for (let i = 0; i < config.vertex.properties.length; i++) { + const property = config.vertex.properties[i]; + if (property.type === "float") { + vertex[property.name] = Number(parts[i]); + } + } + data.vertexes.push(vertex); + + if (data.vertexes.length === config.vertex.count) { + phase = "face"; + } + + continue; + } + + if (phase === "face") { + const face = []; + for (let i = 1; i < parts.length; i++) { + face.push(Number(parts[i])); + } + data.faces.push(face); + + if (data.faces.length === config.face.count) { + break; + } + + continue; + } + } + } +} diff --git a/html/index.css b/src/public/index.css similarity index 100% rename from html/index.css rename to src/public/index.css diff --git a/html/work.css b/src/public/work.css similarity index 100% rename from html/work.css rename to src/public/work.css diff --git a/html/lib/shader.js b/src/shader.js similarity index 100% rename from html/lib/shader.js rename to src/shader.js diff --git a/html/lib/telemetry.js b/src/telemetry.js similarity index 100% rename from html/lib/telemetry.js rename to src/telemetry.js diff --git a/src/webgpu-app.js b/src/webgpu-app.js new file mode 100644 index 0000000..a2c3141 --- /dev/null +++ b/src/webgpu-app.js @@ -0,0 +1,40 @@ +import { Telemetry } from "./telemetry.js"; + +export class WebGPUApp { + constructor(config) { + this.config = config; + this.canvas = document.querySelector("canvas"); + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + this.telemetry = new Telemetry(this); + + this.init().catch((e) => { + console.error(e); + document.querySelector( + "main" + ).innerHTML = `
    your browser didn't let me set up webgpu. firefox nightly or enable dom.webgpu.enable.
    `; + throw new Error( + "Unable to initialize WebGPU. Your browser or machine may not support it.", + e + ); + }); + } + + async init() { + if (!navigator.gpu) { + throw new Error("WebGPU not supported"); + } + + this.adapter = await navigator.gpu.requestAdapter(); + if (!this.adapter) { + throw new Error("No GPU adapter found"); + } + + this.device = await this.adapter.requestDevice(); + if (!this.device) { + throw new Error("No GPU device found"); + } + + this.context = this.canvas.getContext("webgpu"); + } +}