switch to bun build instead of statics
This commit is contained in:
parent
8cbe93d74d
commit
adc3a38395
26 changed files with 357 additions and 230 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -174,3 +174,4 @@ dist
|
||||||
# Finder (MacOS) folder config
|
# Finder (MacOS) folder config
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
html
|
|
@ -9,7 +9,7 @@ https://art.mekanoe.com
|
||||||
## Artworks
|
## Artworks
|
||||||
|
|
||||||
- [./001-platform-provenance](https://art.mekanoe.com/001-platform-provenance)
|
- [./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
|
## Development
|
||||||
|
|
||||||
|
|
30
build.ts
Normal file
30
build.ts
Normal file
|
@ -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}...`));
|
||||||
|
}
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
37
generate.ts
Normal file
37
generate.ts
Normal file
|
@ -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(
|
||||||
|
"<!--/INSERT/-->",
|
||||||
|
allWorks
|
||||||
|
.map((work) => `<li><a href="/${work}">./${work}</a></li>`)
|
||||||
|
.join("\n ")
|
||||||
|
);
|
||||||
|
await Bun.write("html/index.html", index);
|
||||||
|
console.log(chalk.yellow(` -> html/index.html...`));
|
||||||
|
|
||||||
|
const readme = readmeTemplate.replace(
|
||||||
|
"<!--/INSERT/-->",
|
||||||
|
allWorks
|
||||||
|
.reverse()
|
||||||
|
.map((work) => `- [./${work}](https://art.mekanoe.com/${work})`)
|
||||||
|
.join("\n")
|
||||||
|
);
|
||||||
|
await Bun.write("README.md", readme);
|
||||||
|
console.log(chalk.yellow(` -> README.md...`));
|
||||||
|
};
|
|
@ -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(
|
|
||||||
"<!--/INSERT/-->",
|
|
||||||
allWorks
|
|
||||||
.map((work) => `<li><a href="/${work}">./${work}</a></li>`)
|
|
||||||
.join("\n ")
|
|
||||||
);
|
|
||||||
await Bun.write("html/index.html", index);
|
|
||||||
|
|
||||||
const readme = readmeTemplate.replace(
|
|
||||||
"<!--/INSERT/-->",
|
|
||||||
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.`
|
|
||||||
);
|
|
|
@ -16,8 +16,60 @@
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
text-shadow: 1px 1px 3px hsl(38, 45%, 22%);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<link rel="stylesheet" href="index.css" />
|
|
||||||
<main>
|
<main>
|
||||||
<header>
|
<header>
|
||||||
<div>com.mekanoe.art //</div>
|
<div>com.mekanoe.art //</div>
|
||||||
|
|
|
@ -28,4 +28,4 @@
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
></script>
|
></script>
|
||||||
<script src="/##name##.js" type="module"></script>
|
<script src="/##name##/main.js" type="module"></script>
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>com.mekanoe.art // 001-platform-provenance</title>
|
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<link rel="stylesheet" href="work.css" />
|
|
||||||
<main>
|
|
||||||
<canvas id="canvas" width="1280" height="720"></canvas>
|
|
||||||
<div id="telemetry">XX.X FPS (XX.X ms)</div>
|
|
||||||
</main>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.min.js"
|
|
||||||
integrity="sha512-cR3oS5mKRWD+38vYi1CNJk1DLpi104ovuQBuVv9p7nNxeqzSNiHzlboK2BZQybmpTi1QNnQ5unYajpURcMjeZQ=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script src="/001-platform-provenance.js" type="module"></script>
|
|
|
@ -1,31 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>com.mekanoe.art // 002-rainbow-trinity</title>
|
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<link rel="stylesheet" href="work.css" />
|
|
||||||
<main>
|
|
||||||
<canvas id="canvas" width="1280" height="720"></canvas>
|
|
||||||
<div id="telemetry">XX.X FPS (XX.X ms)</div>
|
|
||||||
</main>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.min.js"
|
|
||||||
integrity="sha512-cR3oS5mKRWD+38vYi1CNJk1DLpi104ovuQBuVv9p7nNxeqzSNiHzlboK2BZQybmpTi1QNnQ5unYajpURcMjeZQ=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script src="/002-rainbow-trinity.js" type="module"></script>
|
|
|
@ -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();
|
|
|
@ -1,37 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>com.mekanoe.art //</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible&display=swap"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
font-family: "Atkinson Hyperlegible", sans-serif;
|
|
||||||
background-color: hsl(32, 19%, 14%);
|
|
||||||
color: hsl(39, 75%, 51%);
|
|
||||||
line-height: 1.5;
|
|
||||||
text-shadow: 1px 1px 3px hsl(38, 45%, 22%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<link rel="stylesheet" href="index.css" />
|
|
||||||
<main>
|
|
||||||
<header>
|
|
||||||
<div>com.mekanoe.art //</div>
|
|
||||||
<div class="subtext">
|
|
||||||
<< noeidelon >>
|
|
||||||
<a href="https://github.com/mekanoe/noeidelon">[github]</a>
|
|
||||||
<a href="https://sapphic.engineer/noe" rel="me">[fedi]</a>
|
|
||||||
<br />a collection of 3D works
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<section id="works">
|
|
||||||
<ul>
|
|
||||||
<li><a href="/002-rainbow-trinity">./002-rainbow-trinity</a></li>
|
|
||||||
<li><a href="/001-platform-provenance">./001-platform-provenance</a></li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "noeidelon",
|
"name": "noeidelon",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./generators/generate.js",
|
"main": "./build.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun $BUNFLAGS ./generators/generate.ts",
|
"build": "bun $BUNFLAGS .",
|
||||||
"build:watch": "BUNFLAGS=--watch bun run build",
|
"build:watch": "BUNFLAGS=--watch bun run build",
|
||||||
"serve": "serve ./html",
|
"serve": "serve ./html",
|
||||||
"dev": "run-p serve build:watch"
|
"dev": "run-p serve build:watch"
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chalk": "^5.3.0",
|
||||||
"glob": "^10.3.10",
|
"glob": "^10.3.10",
|
||||||
"serve": "^14.2.1"
|
"serve": "^14.2.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Shader } from "./lib/shader.js";
|
import { Shader } from "../shader";
|
||||||
import { BasicPlane } from "./lib/basic-plane.js";
|
import { BasicPlane } from "../basic-plane";
|
||||||
import { App } from "./lib/app.js";
|
import { App } from "../app";
|
||||||
|
|
||||||
const app = new App({ fov: 20 });
|
const app = new App({ fov: 20 });
|
||||||
const gl = app.gl;
|
const gl = app.gl;
|
36
src/002-enter-the-third/main.ts
Normal file
36
src/002-enter-the-third/main.ts
Normal file
|
@ -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();
|
|
@ -111,9 +111,7 @@ export class App {
|
||||||
requestAnimationFrame(run);
|
requestAnimationFrame(run);
|
||||||
};
|
};
|
||||||
|
|
||||||
requestAnimationFrame((now) => {
|
requestAnimationFrame(run);
|
||||||
run(now);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
now() {
|
now() {
|
12
src/gltf-object.js
Normal file
12
src/gltf-object.js
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
23
src/network-object.js
Normal file
23
src/network-object.js
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
116
src/ply-object.js
Normal file
116
src/ply-object.js
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
src/webgpu-app.js
Normal file
40
src/webgpu-app.js
Normal file
|
@ -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 = `<div><i>your browser didn't let me set up webgpu. firefox nightly or enable <code>dom.webgpu.enable</code>.</i></div>`;
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue