003 fucking RENDERS models YAAAAAAAAA

This commit is contained in:
41666 2023-10-10 00:11:02 -04:00
parent 28faa03ac6
commit 23d7810ab6
30 changed files with 16955 additions and 221 deletions

View file

@ -24,19 +24,24 @@ https://art.mekanoe.com
### Infrastructure ### Infrastructure
- The "generator" does - **Output generation**
- Generates a work list from .js files within `html` - Bun renders TypeScript and GLSL to html/
- Creates this README.md file - src/public/ is copied to html/
- Creates the index.html file - This doubles as an "archival" system to preserve scuffedness
- Creates individual HTML files for each work - .ply files are converted to JS typed arrays and Mesh objects
- .html files (and README.md) are generated from rendered TypeScript main.ts files.
- The platform supplies - **Platform**
- Shader types
- Primitive objects - WebGLApp provides render loop, canvas, and other stuff
- A canvas to draw on - Behavior provides automatic registration of WebGLApp events (like Unity MonoBehaviors)
- WebGL hooks - Shader provides GLSL shader tooling
- Helpers, etc - Mesh is a DTO with vertex position, color, UVs, and faces
- MeshRenderer sends WebGL instructions for rendering a Mesh object with a Shader object.
- Renderable is a container of a Transform and Renderer (like a Unity GameObject)
- Transform is a tuplet of a position vec3, rotation quat, and scale vec3; to be used anywhere needed.
- Scene is a group of Renderables
## License ## License

BIN
bun.lockb

Binary file not shown.

View file

@ -11,6 +11,9 @@ mkdirSync("html");
const works = globSync("src/*/main.ts"); const works = globSync("src/*/main.ts");
console.log(chalk.green`>> Convert meshes ...`);
await convertMeshes();
console.log(chalk.green`>> Building ...`); console.log(chalk.green`>> Building ...`);
console.log(chalk.yellow(` Found ${works.length} works.`)); console.log(chalk.yellow(` Found ${works.length} works.`));
console.log(chalk.yellow(` Running Bun.build()`)); console.log(chalk.yellow(` Running Bun.build()`));
@ -44,6 +47,3 @@ const { stdout, stderr, exitCode } = Bun.spawnSync([
"-c", "-c",
"cp -r src/public/* html/", "cp -r src/public/* html/",
]); ]);
console.log(chalk.green`>> Convert meshes ...`);
await convertMeshes();

View file

@ -1,6 +1,54 @@
import chalk from "chalk"; import chalk from "chalk";
import { globSync } from "glob"; import { globSync } from "glob";
const w = 1;
type Vertex = {
position: {
x: number;
y: number;
z: number;
};
color?: {
r: number;
g: number;
b: number;
a: number;
};
uv?: {
u: number;
v: number;
};
normal?: {
nx: number;
ny: number;
nz: number;
};
};
type Face = {
a: number;
b: number;
c: number;
};
type Triangle = {
a: Vertex;
b: Vertex;
c: Vertex;
};
// const vertToArray = ({ x, y, z, w, r, g, b, a, u, v }: Vertex): number[] => {
// if (!r) return [x, y, z, u, v];
// else return [x, y, z, r, g as number, b as number, a as number, u, v];
// };
// const triangleToArray = (t: Triangle): number[] => [
// ...vertToArray(t.a),
// ...vertToArray(t.b),
// ...vertToArray(t.c),
// ];
export const convertMeshes = async () => { export const convertMeshes = async () => {
const meshes = globSync("src/meshes/*.ply"); const meshes = globSync("src/meshes/*.ply");
@ -8,8 +56,47 @@ export const convertMeshes = async () => {
const ply = await Bun.file(file).text(); const ply = await Bun.file(file).text();
const [header, body] = ply.split("end_header"); const [header, body] = ply.split("end_header");
const colorSize = header.includes("red") ? 4 : 0;
const propertyList: [string, "float" | "uchar"][] = [];
const vertexConfig = {
colors: false,
uvs: false,
normals: false,
};
const headerLines = header.split("\n"); const headerLines = header.split("\n");
for (const property of headerLines.filter((header) =>
header.startsWith("property")
)) {
const [, propType, name] = property.split(" ");
if (propType === "list") {
continue;
}
propertyList.push([name, propType as any]);
switch (name) {
case "red":
case "green":
case "blue":
case "alpha":
vertexConfig.colors = true;
break;
case "nx":
case "ny":
case "nz":
vertexConfig.normals = true;
break;
case "s":
case "t":
vertexConfig.uvs = true;
break;
}
}
console.log({ propertyList });
const vertexCount = Number( const vertexCount = Number(
headerLines headerLines
.find((header) => header.startsWith("element vertex")) .find((header) => header.startsWith("element vertex"))
@ -20,49 +107,152 @@ export const convertMeshes = async () => {
throw new Error("couldn't get vertex count..."); throw new Error("couldn't get vertex count...");
} }
const values: number[] = []; const vertexes: Vertex[] = [];
const faces: Face[] = [];
for (const line of body.split("\n")) { for (const line of body.split("\n")) {
// line looks like const components = line.split(" ");
// x y z r g b a u v
// We need to convert it to
// x y z 1 r g b a u v
const [x, y, z, r, g, b, a, u, v] = line.split(" "); if (!components || !components[0]) {
if (!g) {
continue; continue;
} }
values.push( // do we only have 4 components?
parseFloat(x), if (components[0] === "3" || components.length === 4) {
parseFloat(y), const [, a, b, c] = components;
parseFloat(z),
1,
parseFloat(r),
parseFloat(g)
);
if (colorSize > 0) { // We do??!?! 🥺👉👈
values.push(parseFloat(b), parseFloat(a), parseFloat(u), parseFloat(v)); faces.push({ a: Number(a), b: Number(b), c: Number(c) });
continue;
} }
const vertex: Required<Vertex> = {
position: {
x: -1,
y: -1,
z: -1,
},
color: {
r: 0,
g: 0,
b: 0,
a: 255,
},
normal: {
nx: -1,
ny: -1,
nz: -1,
},
uv: {
u: -1,
v: -1,
},
};
for (const idx in components) {
const component = components[idx];
const [propName, propType] = propertyList[idx];
const p = parser[propType] ?? parseFloat;
switch (propName) {
case "x":
vertex.position.x = p(component);
break;
case "y":
vertex.position.y = p(component);
break;
case "z":
vertex.position.z = p(component);
case "s":
vertex.uv.u = p(component);
break;
case "t":
vertex.uv.v = p(component);
break;
case "red":
vertex.color.r = p(component);
break;
case "green":
vertex.color.g = p(component);
break;
case "blue":
vertex.color.b = p(component);
break;
case "nx":
vertex.normal.nx = p(component);
break;
case "ny":
vertex.normal.ny = p(component);
break;
case "nz":
vertex.normal.nz = p(component);
break;
}
}
vertexes.push(vertex);
} }
const positions: number[] = vertexes.flatMap((v) => [
v.position.x,
v.position.y,
v.position.z,
]);
const normals: number[] = vertexConfig.normals
? vertexes.flatMap((v: any) => [v.normal.nx, v.normal.ny, v.normal.nz])
: [];
const colors: number[] = vertexConfig.colors
? vertexes.flatMap((v: any) => [
v.color.r,
v.color.g,
v.color.b,
v.color.a,
])
: [];
const uvs: number[] = vertexConfig.uvs
? vertexes.flatMap((v: any) => [v.uv.u, v.uv.v])
: [];
const facesArray: number[] = faces.flatMap((f) => [f.a, f.b, f.c]);
const outFile = file.replace(".ply", ".ts"); const outFile = file.replace(".ply", ".ts");
const outString = ` const outString = `
import { Mesh } from "../renderer/mesh"; import { Mesh } from "../renderer/mesh";
// prettier-ignore // prettier-ignore
const mesh = new Float32Array(${JSON.stringify(values)}); const positions = new Float32Array(${JSON.stringify(positions)});
export default new Mesh({ // prettier-ignore
mesh, const colors = ${
positionSize: 4, vertexConfig.colors ? `new Uint8Array(${JSON.stringify(colors)})` : "null"
colorSize: ${colorSize}, };
uvSize: 2,
vertexCount: ${vertexCount}, // prettier-ignore
stride: ${4 + colorSize + 2}, const uvs = ${
name: ${JSON.stringify(file)} vertexConfig.uvs ? `new Float32Array(${JSON.stringify(uvs)})` : "null"
};
// prettier-ignore
const normals = ${
vertexConfig.normals
? `new Float32Array(${JSON.stringify(normals)})`
: "null"
};
// prettier-ignore
const faces = new Uint32Array(${JSON.stringify(facesArray)});
export default new Mesh({
colors,
faces,
name: ${JSON.stringify(file)},
normals,
positions,
uvs,
vertexCount: ${vertexCount}
}); });
`; `;
@ -70,3 +260,8 @@ export default new Mesh({
console.log(chalk.yellow(` -> ${file}...`)); console.log(chalk.yellow(` -> ${file}...`));
} }
}; };
const parser = {
float: (x: string) => parseFloat(x),
uchar: (x: string) => Number(x),
};

View file

@ -22,19 +22,24 @@ https://art.mekanoe.com
### Infrastructure ### Infrastructure
- The "generator" does - **Output generation**
- Generates a work list from .js files within `html` - Bun renders TypeScript and GLSL to html/
- Creates this README.md file - src/public/ is copied to html/
- Creates the index.html file - This doubles as an "archival" system to preserve scuffedness
- Creates individual HTML files for each work - .ply files are converted to JS typed arrays and Mesh objects
- .html files (and README.md) are generated from rendered TypeScript main.ts files.
- The platform supplies - **Platform**
- Shader types
- Primitive objects - WebGLApp provides render loop, canvas, and other stuff
- A canvas to draw on - Behavior provides automatic registration of WebGLApp events (like Unity MonoBehaviors)
- WebGL hooks - Shader provides GLSL shader tooling
- Helpers, etc - Mesh is a DTO with vertex position, color, UVs, and faces
- MeshRenderer sends WebGL instructions for rendering a Mesh object with a Shader object.
- Renderable is a container of a Transform and Renderer (like a Unity GameObject)
- Transform is a tuplet of a position vec3, rotation quat, and scale vec3; to be used anywhere needed.
- Scene is a group of Renderables
## License ## License

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,13 +4,14 @@
"main": "./hack/build.ts", "main": "./hack/build.ts",
"scripts": { "scripts": {
"build": "bun $BUNFLAGS .", "build": "bun $BUNFLAGS .",
"build:watch": "BUNFLAGS=--watch bun run build", "build:watch": "nodemon -e ts,js,frag,vert,glsl,ply",
"serve": "serve ./html", "serve": "serve ./html",
"dev": "run-p serve build:watch" "dev": "run-p serve build:watch"
}, },
"devDependencies": { "devDependencies": {
"bun-types": "latest", "bun-types": "latest",
"npm-run-all2": "^6.1.1", "npm-run-all2": "^6.1.1",
"nodemon": "^3.0.1",
"prettier": "^3.0.3" "prettier": "^3.0.3"
}, },
"peerDependencies": { "peerDependencies": {
@ -23,5 +24,14 @@
"glob": "^10.3.10", "glob": "^10.3.10",
"serve": "^14.2.1", "serve": "^14.2.1",
"typescript": "^5.2.2" "typescript": "^5.2.2"
},
"nodemonConfig": {
"ignore": [
"html/",
"README.md",
"src/meshes/*.ts"
],
"delay": 1000,
"exec": "bun run . --"
} }
} }

View file

@ -1,20 +1,20 @@
// This one is archived. // // This one is archived.
import { Shader } from "./shader"; // import { Shader } from "./shader";
import { BasicPlane } from "./basic-plane"; // import { BasicPlane } from "./basic-plane";
import { App } from "./app"; // import { App } from "./app";
import fragment from "./fragment.glsl"; // import fragment from "./fragment.glsl";
import vertex from "./vertex.glsl"; // import vertex from "./vertex.glsl";
const app = new App({ fov: 20 }); // const app = new App({ fov: 20 });
const gl = app.gl; // const gl = app.gl;
const shader = new Shader(app) // const shader = new Shader(app)
.attach(gl.VERTEX_SHADER, vertex) // .attach(gl.VERTEX_SHADER, vertex)
.attach(gl.FRAGMENT_SHADER, fragment) // .attach(gl.FRAGMENT_SHADER, fragment)
.link(); // .link();
const plane = new BasicPlane(app); // const plane = new BasicPlane(app);
plane.attachShader(shader); // plane.attachShader(shader);
app.loop(); // app.loop();

View file

@ -1,42 +1,43 @@
// This one is archived. // // This one is archived.
import { MeshRenderer } from "../renderer/mesh-renderer";
import plane from "../meshes/plane";
import { WebGLApp } from "../renderer/webgl";
import { Renderable } from "../renderer/renderable";
import { Transform } from "../renderer/transform";
import { uvRainbow } from "../common-shaders/uv-rainbow";
import { quat } from "gl-matrix";
import torus from "../meshes/torus";
const app = new WebGLApp({ fov: 45 }); // import { MeshRenderer } from "../renderer/mesh-renderer";
// import plane from "../meshes/plane";
// import { WebGLApp } from "../renderer/webgl";
// import { Renderable } from "../renderer/renderable";
// import { Transform } from "../renderer/transform";
// import { uvRainbow } from "../common-shaders/uv-rainbow";
// import { quat } from "gl-matrix";
// import torus from "../meshes/torus";
const camera = new Transform( // const app = new WebGLApp({ fov: 45 });
[0, 0, -6],
quat.fromEuler(quat.create(), 15, 0, 0)
);
const modelTransform = new Transform([0, 0, 4]); // const camera = new Transform(
// [0, 0, -6],
// quat.fromEuler(quat.create(), 15, 0, 0)
// );
app.onUpdate((time: number) => { // const modelTransform = new Transform([0, 0, 4]);
const cameraStride = 3;
const x = Math.sin(time * 0.0001) * (cameraStride * 2 - cameraStride * 0.5);
camera.rotation = quat.fromEuler(quat.create(), x, 0, 0); // app.onUpdate((time: number) => {
// modelTransform.rotation = quat.fromEuler(quat.create(), x, 0, 0); // const cameraStride = 3;
}); // const x = Math.sin(time * 0.0001) * (cameraStride * 2 - cameraStride * 0.5);
new Renderable( // camera.rotation = quat.fromEuler(quat.create(), x, 0, 0);
app, // // modelTransform.rotation = quat.fromEuler(quat.create(), x, 0, 0);
modelTransform, // });
new MeshRenderer(app, torus, uvRainbow(app), camera, {
drawMode: app.gl.TRIANGLE_FAN,
})
);
// new Renderable( // new Renderable(
// app, // app,
// new Transform([1, 0, 0]), // modelTransform,
// new MeshRenderer(app, plane, uvRainbow(app)) // new MeshRenderer(app, torus, uvRainbow(app), camera, {
// drawMode: app.gl.TRIANGLE_FAN,
// })
// ); // );
app.start(); // // new Renderable(
// // app,
// // new Transform([1, 0, 0]),
// // new MeshRenderer(app, plane, uvRainbow(app))
// // );
// app.start();

View file

@ -4,13 +4,27 @@ import { Renderable } from "../renderer/renderable";
import { Transform } from "../renderer/transform"; import { Transform } from "../renderer/transform";
import torus from "../meshes/torus"; import torus from "../meshes/torus";
import { errorShader } from "../common-shaders/error"; import { errorShader } from "../common-shaders/error";
import plane from "../meshes/plane";
import { uvRainbow } from "../common-shaders/uv-rainbow";
import uvsphere from "../meshes/uvsphere";
import { quat } from "gl-matrix";
import trianglething from "../meshes/trianglething";
import teapot from "../meshes/teapot";
const app = new WebGLApp({ fov: 45 }); const app = new WebGLApp({ fov: 45 });
const camera = new Transform([0, -2, -6]);
new Renderable( new Renderable(
app, app,
new Transform(), new Transform(),
new MeshRenderer(app, torus, errorShader(app)) new MeshRenderer(app, trianglething, uvRainbow(app), camera).configure({})
); );
// new Renderable(
// app,
// new Transform(),
// new MeshRenderer(app, torus, uvRainbow(app), camera).configure({})
// );
app.start(); app.start();

View file

@ -1,10 +1,12 @@
#version 300 es
precision highp float; precision highp float;
uniform float uTime; uniform float uTime;
uniform float uSinTime; uniform float uSinTime;
uniform float uCosTime; uniform float uCosTime;
varying highp vec2 vTextureCoord; in vec2 vTextureCoord;
out vec4 fragColor;
vec3 rgb2hsv(vec3 c) { vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
@ -30,6 +32,6 @@ void main() {
hsv.z = 1.0; hsv.z = 1.0;
vec3 rgb = hsv2rgb(hsv); vec3 rgb = hsv2rgb(hsv);
gl_FragColor = vec4(rgb, 1.0); fragColor = vec4(rgb, 1.0);
gl_FragColor = clamp(gl_FragColor, 0.0, 1.0); fragColor = clamp(fragColor, 0.0, 1.0);
} }

View file

@ -1,10 +1,11 @@
attribute vec4 aVertexPosition; #version 300 es
attribute vec2 aTextureCoord; layout(location = 0) in vec4 aVertexPosition;
layout(location = 1) in vec2 aTextureCoord;
uniform mat4 uModelViewMatrix; uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix; uniform mat4 uProjectionMatrix;
varying highp vec2 vTextureCoord; out vec2 vTextureCoord;
void main() { void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;

18
src/meshes/plane.ply Normal file
View file

@ -0,0 +1,18 @@
ply
format ascii 1.0
comment Created in Blender version 3.6.4
element vertex 4
property float x
property float y
property float z
property float s
property float t
element face 2
property list uchar uint vertex_indices
end_header
1 -1 0 1 0
-1 1 0 0 1
-1 -1 0 0 0
1 1 0 1 1
3 0 1 2
3 0 3 1

View file

@ -1,20 +1,28 @@
import { Mesh } from "../renderer/mesh"; import { Mesh } from "../renderer/mesh";
// prettier-ignore // prettier-ignore
const mesh = new Float32Array([ const positions = new Float32Array([1,-1,0,-1,1,0,-1,-1,0,1,1,0]);
// f4 position, f4 color, f2 uv
-1, -1, 0, 1, 1, 1, 1, 1, 0, 0, // prettier-ignore
1, -1, 0, 1, 1, 1, 1, 1, 1, 0, const colors = null;
-1, 1, 0, 1, 1, 1, 1, 1, 0, 1,
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, // prettier-ignore
]); const uvs = new Float32Array([1,0,0,1,0,0,1,1]);
// prettier-ignore
const normals = null;
// prettier-ignore
const faces = new Uint32Array([0,1,2,0,3,1]);
export default new Mesh({ export default new Mesh({
mesh, colors,
positionSize: 4, faces,
colorSize: 4, name: "src/meshes/plane.ply",
uvSize: 2, normals,
vertexCount: 4, positions,
stride: 10, uvs,
name: "plane", vertexCount: 4
}); });

2028
src/meshes/teapot.ply Normal file

File diff suppressed because it is too large Load diff

28
src/meshes/teapot.ts Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,76 @@
ply
format ascii 1.0
comment Created in Blender version 3.6.4
element vertex 31
property float x
property float y
property float z
property float nx
property float ny
property float nz
property uchar red
property uchar green
property uchar blue
property uchar alpha
property float s
property float t
element face 26
property list uchar uint vertex_indices
end_header
0 2.609 0 0 -1 8.3803394e-07 0 1 255 255 0.00013659838 0.8180941
-0.368004 2.9959495 0.50652 -0.5257306 -0.44721383 0.723607 0 1 255 255 0.15755458 0.7272099
-0.595448 2.9959495 -0.19346951 -0.8506533 -0.44721213 -0.2763879 0 1 255 255 0.1575546 0.9089782
0 2.609 0 0 -1 8.3803394e-07 0 1 255 255 0.00013659043 0.6363257
0.368004 2.9959495 0.50652 0.5257306 -0.4472139 0.723607 0 1 255 255 0.15755458 0.54544157
0 2.609 0 0 -1 8.3803394e-07 0 1 255 255 0.0001365666 0.09102075
-0.595448 2.9959495 -0.19346951 -0.8506533 -0.44721213 -0.2763879 0 1 255 255 0.15755455 0.00013656964
0 2.9959495 -0.6260975 0 -0.44721153 -0.89442825 0 1 255 255 0.15755457 0.18190491
0 2.609 0 0 -1 8.3803394e-07 0 1 255 255 0.00013657454 0.2727891
0.595448 2.9959495 -0.19346951 0.8506533 -0.4472122 -0.27638793 0 1 255 255 0.15755457 0.36367324
0 2.609 0 0 -1 8.3803394e-07 0 1 255 255 0.00013658249 0.45455742
0 3.6220505 0.6260975 0 0.44721153 0.89442825 0 1 255 255 0.31497157 0.6363257
-0.595448 3.6220505 0.19346951 -0.8506533 0.44721216 0.27638793 0 1 255 255 0.31497157 0.8180941
-0.368004 3.6220505 -0.50652 -0.5257306 0.4472139 -0.72360694 0 1 255 255 0.31497154 0.09102073
0.368004 3.6220505 -0.50652 0.5257306 0.4472139 -0.72360694 0 1 255 255 0.31497154 0.27278906
0.595448 3.6220505 0.19346951 0.85065323 0.4472122 0.2763879 0 1 255 255 0.31497154 0.45455742
-0.368004 3.6220505 -0.50652 -0.5257306 0.4472139 -0.72360694 0 1 255 255 0.31497157 0.9998634
0 4.0090003 0 0 1 -8.165459e-07 0 1 255 255 0.47238955 0.72720987
0 4.0090003 0 0 1 -8.165459e-07 0 1 255 255 0.47238958 0.9089782
0 4.0090003 0 0 1 -8.165459e-07 0 1 255 255 0.47238955 0.1819049
0 4.0090003 0 0 1 -8.165459e-07 0 1 255 255 0.47238955 0.36367324
0 4.0090003 0 0 1 -8.165459e-07 0 1 255 255 0.47238955 0.54544157
1 0 0 0.8999471 -0.43599886 2.4163015e-07 0 1 255 255 0.8119813 0.33945516
0 2.5 0 -5.150958e-08 1 0 0 1 255 255 0.642322 0.16979587
-4.3711392e-08 0 1 -3.0203772e-08 -0.4359989 0.8999472 0 1 255 255 0.8119813 0.00013659486
-1 0 -8.7422784e-08 -0.8999471 -0.4359989 -2.4291313e-07 0 1 255 255 0.47266275 0.00013660162
-4.3711392e-08 0 1 -3.0203772e-08 -0.4359989 0.8999472 0 1 255 255 0.81198126 0.6790469
1.19248815e-08 0 -1 0 -0.43599886 -0.8999471 0 1 255 255 0.47266275 0.33972836
1 0 0 0.8999471 -0.43599886 2.4163015e-07 0 1 255 255 0.47266272 0.6790469
1.19248815e-08 0 -1 0 -0.43599886 -0.8999471 0 1 255 255 0.47266275 0.33945513
-1 0 -8.7422784e-08 -0.8999471 -0.4359989 -2.4291313e-07 0 1 255 255 0.81198126 0.3397284
3 0 1 2
3 1 3 4
3 5 6 7
3 8 7 9
3 10 9 4
3 1 4 11
3 2 1 12
3 7 6 13
3 9 7 14
3 4 9 15
3 1 11 12
3 2 12 16
3 7 13 14
3 9 14 15
3 4 15 11
3 12 11 17
3 16 12 18
3 14 13 19
3 15 14 20
3 11 15 21
3 22 23 24
3 24 23 25
3 26 27 28
3 25 23 29
3 29 23 22
3 26 30 27

View file

@ -0,0 +1,28 @@
import { Mesh } from "../renderer/mesh";
// prettier-ignore
const positions = new Float32Array([0,2.609,0,-0.368004,2.9959495,0.50652,-0.595448,2.9959495,-0.19346951,0,2.609,0,0.368004,2.9959495,0.50652,0,2.609,0,-0.595448,2.9959495,-0.19346951,0,2.9959495,-0.6260975,0,2.609,0,0.595448,2.9959495,-0.19346951,0,2.609,0,0,3.6220505,0.6260975,-0.595448,3.6220505,0.19346951,-0.368004,3.6220505,-0.50652,0.368004,3.6220505,-0.50652,0.595448,3.6220505,0.19346951,-0.368004,3.6220505,-0.50652,0,4.0090003,0,0,4.0090003,0,0,4.0090003,0,0,4.0090003,0,0,4.0090003,0,1,0,0,0,2.5,0,-4.3711392e-8,0,1,-1,0,-8.7422784e-8,-4.3711392e-8,0,1,1.19248815e-8,0,-1,1,0,0,1.19248815e-8,0,-1,-1,0,-8.7422784e-8]);
// prettier-ignore
const colors = new Uint8Array([0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255,0,1,255,255]);
// prettier-ignore
const uvs = new Float32Array([0.00013659838,0.8180941,0.15755458,0.7272099,0.1575546,0.9089782,0.00013659043,0.6363257,0.15755458,0.54544157,0.0001365666,0.09102075,0.15755455,0.00013656964,0.15755457,0.18190491,0.00013657454,0.2727891,0.15755457,0.36367324,0.00013658249,0.45455742,0.31497157,0.6363257,0.31497157,0.8180941,0.31497154,0.09102073,0.31497154,0.27278906,0.31497154,0.45455742,0.31497157,0.9998634,0.47238955,0.72720987,0.47238958,0.9089782,0.47238955,0.1819049,0.47238955,0.36367324,0.47238955,0.54544157,0.8119813,0.33945516,0.642322,0.16979587,0.8119813,0.00013659486,0.47266275,0.00013660162,0.81198126,0.6790469,0.47266275,0.33972836,0.47266272,0.6790469,0.47266275,0.33945513,0.81198126,0.3397284]);
// prettier-ignore
const normals = new Float32Array([0,-1,8.3803394e-7,-0.5257306,-0.44721383,0.723607,-0.8506533,-0.44721213,-0.2763879,0,-1,8.3803394e-7,0.5257306,-0.4472139,0.723607,0,-1,8.3803394e-7,-0.8506533,-0.44721213,-0.2763879,0,-0.44721153,-0.89442825,0,-1,8.3803394e-7,0.8506533,-0.4472122,-0.27638793,0,-1,8.3803394e-7,0,0.44721153,0.89442825,-0.8506533,0.44721216,0.27638793,-0.5257306,0.4472139,-0.72360694,0.5257306,0.4472139,-0.72360694,0.85065323,0.4472122,0.2763879,-0.5257306,0.4472139,-0.72360694,0,1,-8.165459e-7,0,1,-8.165459e-7,0,1,-8.165459e-7,0,1,-8.165459e-7,0,1,-8.165459e-7,0.8999471,-0.43599886,2.4163015e-7,-5.150958e-8,1,0,-3.0203772e-8,-0.4359989,0.8999472,-0.8999471,-0.4359989,-2.4291313e-7,-3.0203772e-8,-0.4359989,0.8999472,0,-0.43599886,-0.8999471,0.8999471,-0.43599886,2.4163015e-7,0,-0.43599886,-0.8999471,-0.8999471,-0.4359989,-2.4291313e-7]);
// prettier-ignore
const faces = new Uint32Array([0,1,2,1,3,4,5,6,7,8,7,9,10,9,4,1,4,11,2,1,12,7,6,13,9,7,14,4,9,15,1,11,12,2,12,16,7,13,14,9,14,15,4,15,11,12,11,17,16,12,18,14,13,19,15,14,20,11,15,21,22,23,24,24,23,25,26,27,28,25,23,29,29,23,22,26,30,27]);
export default new Mesh({
colors,
faces,
name: "src/meshes/trianglething.ply",
normals,
positions,
uvs,
vertexCount: 31
});

14234
src/meshes/uvsphere.ply Normal file

File diff suppressed because it is too large Load diff

28
src/meshes/uvsphere.ts Normal file

File diff suppressed because one or more lines are too long

View file

@ -5,10 +5,12 @@ export abstract class Behavior {
onBeforeUpdate?(...args: any[]): void; onBeforeUpdate?(...args: any[]): void;
onUpdate?(...args: any[]): void; onUpdate?(...args: any[]): void;
onAfterUpdate?(...args: any[]): void; onAfterUpdate?(...args: any[]): void;
onDraw?(...args: any[]): void;
constructor(public app: WebGLApp) { constructor(public app: WebGLApp) {
this.onStart && app.onStart(this.onStart.bind(this)); this.onStart && app.onStart(this.onStart.bind(this));
this.onUpdate && app.onUpdate(this.onUpdate.bind(this)); this.onUpdate && app.onUpdate(this.onUpdate.bind(this));
this.onAfterUpdate && app.onAfterUpdate(this.onAfterUpdate.bind(this)); this.onAfterUpdate && app.onAfterUpdate(this.onAfterUpdate.bind(this));
this.onBeforeUpdate && app.onBeforeUpdate(this.onBeforeUpdate.bind(this)); this.onBeforeUpdate && app.onBeforeUpdate(this.onBeforeUpdate.bind(this));
this.onDraw && app.onDraw(this.onDraw.bind(this));
} }
} }

View file

@ -4,16 +4,22 @@ import { Mesh } from "./mesh";
import { Shader } from "./shader"; import { Shader } from "./shader";
import { WebGLApp } from "./webgl"; import { WebGLApp } from "./webgl";
import { Transform } from "./transform"; import { Transform } from "./transform";
import { errorShader } from "../common-shaders/error";
export type MeshRendererConfig = { export type MeshRendererConfig = {
drawMode?: number; drawMode?: number;
cullMode?: number;
}; };
export class MeshRenderer extends Behavior { export class MeshRenderer extends Behavior {
private modelMatrix = mat4.create(); private modelMatrix = mat4.create();
private projectionMatrix = mat4.create(); private projectionMatrix = mat4.create();
private _meshBuffer?: WebGLBuffer; private buffers: {
position?: WebGLBuffer;
uv?: WebGLBuffer;
normal?: WebGLBuffer;
color?: WebGLBuffer;
faces?: WebGLBuffer;
} = {};
constructor( constructor(
public app: WebGLApp, public app: WebGLApp,
@ -25,106 +31,130 @@ export class MeshRenderer extends Behavior {
super(app); super(app);
} }
get meshBuffer() { configure(config: MeshRendererConfig) {
if (this._meshBuffer) { this.config = config;
return this._meshBuffer; return this;
}
throw new Error("mesh buffer not ready");
} }
initializeMeshBuffer() { makeBuffer(data: TypedArray, target: number = 34962) {
const gl = this.app.gl; const gl = this.app.gl;
const buffer = gl.createBuffer(); const buffer = gl.createBuffer();
if (!buffer) { if (!buffer) {
throw new Error("failed to create mesh buffer"); throw new Error("failed to create a buffer");
} }
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, this.mesh.config.mesh, gl.STATIC_DRAW);
this._meshBuffer = buffer; gl.bindBuffer(target, buffer);
gl.bufferData(target, data, gl.STATIC_DRAW);
gl.bindBuffer(target, null);
return buffer;
} }
initializeAttributes() { initializeBuffers() {
const gl = this.app.gl; this.buffers.faces = this.makeBuffer(
gl.bindBuffer(gl.ARRAY_BUFFER, this.meshBuffer); this.mesh.config.faces,
this.app.gl.ELEMENT_ARRAY_BUFFER
);
const positionLocation = this.shader.attrib("aVertexPosition"); this.buffers.position = this.makeBuffer(this.mesh.config.positions);
if (positionLocation !== -1) { this.bindAttrib(this.buffers.position, 0, 3, this.app.gl.FLOAT);
if (this.mesh.config.normals) {
this.buffers.normal = this.makeBuffer(this.mesh.config.normals);
this.bindAttrib(
this.buffers.normal,
"aVertexNormals",
3,
this.app.gl.FLOAT,
true
);
}
if (this.mesh.config.colors) {
this.buffers.color = this.makeBuffer(this.mesh.config.colors);
this.bindAttrib(
this.buffers.color,
"aVertexColors",
4,
this.app.gl.UNSIGNED_BYTE
);
}
if (this.mesh.config.uvs) {
this.buffers.uv = this.makeBuffer(this.mesh.config.uvs);
this.bindAttrib(this.buffers.uv, 1, 2, this.app.gl.FLOAT);
}
}
bindAttrib(
buffer: WebGLBuffer,
attribute: string | number,
elements: number,
glType: number,
normalized: boolean = false
) {
const gl = this.app.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
const attributePosition = Number.isSafeInteger(attribute)
? (attribute as number)
: this.shader.attrib(attribute as string);
if (attributePosition !== -1) {
gl.vertexAttribPointer( gl.vertexAttribPointer(
positionLocation, attributePosition,
this.mesh.config.positionSize, elements,
gl.FLOAT, glType,
false, normalized,
4 * this.mesh.config.stride, 0,
0 0
); );
gl.enableVertexAttribArray(positionLocation); gl.enableVertexAttribArray(attributePosition);
this.shader.bindAttrib(positionLocation, "aVertexPosition");
} }
if (this.mesh.config.colorSize !== 0) { gl.bindBuffer(gl.ARRAY_BUFFER, null);
const colorLocation = this.shader.attrib("aVertexColor");
if (colorLocation !== -1) {
gl.vertexAttribPointer(
colorLocation,
this.mesh.config.colorSize,
gl.FLOAT,
false,
4 * this.mesh.config.stride,
4 * this.mesh.config.positionSize
);
gl.enableVertexAttribArray(colorLocation);
this.shader.bindAttrib(colorLocation, "aVertexColor");
}
}
const uvLocation = this.shader.attrib("aTextureCoord");
if (uvLocation !== -1) {
gl.vertexAttribPointer(
uvLocation,
this.mesh.config.uvSize,
gl.FLOAT,
false,
4 * this.mesh.config.stride,
4 * (this.mesh.config.positionSize + this.mesh.config.colorSize)
);
gl.enableVertexAttribArray(uvLocation);
this.shader.bindAttrib(uvLocation, "aTextureCoord");
}
} }
onStart() { onStart() {
mat4.perspective( mat4.perspective(
this.projectionMatrix, this.projectionMatrix,
this.app.config.fov || 45, this.app.config.fov || 45,
this.app.canvas.width / this.app.canvas.height, this.app.aspect,
this.app.config.zNear || 0.1, this.app.config.zNear || 0.1,
this.app.config.zFar || 100 this.app.config.zFar || 100
); );
this.shader.compile(); this.shader.compile();
this.initializeMeshBuffer(); this.initializeBuffers();
this.initializeAttributes();
this.shader.link(); this.shader.link();
} }
onRenderableUpdate(time: number, transform: Transform) { onRenderableUpdate(time: number, transform: Transform) {
const gl = this.app.gl; const gl = this.app.gl;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers.faces || null);
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.position || null);
this.shader.use(); this.shader.use();
gl.bindBuffer(gl.ARRAY_BUFFER, this.meshBuffer);
this.shader.setupUniforms( this.shader.setupUniforms(
time, time,
this.projectionMatrix, this.projectionMatrix,
transform, transform,
this.camera this.camera
); );
gl.drawArrays(
this.config.drawMode ?? gl.TRIANGLE_STRIP, gl.drawElements(
0, this.config.drawMode ?? gl.TRIANGLES,
this.mesh.config.vertexCount this.mesh.config.faces.length,
gl.UNSIGNED_INT,
0
); );
// gl.drawArrays(
// 0,
// this.mesh.config.vertexCount
// );
const err = gl.getError(); const err = gl.getError();
if (err !== 0) { if (err !== 0) {
console.log({ err }); console.log({ err });

View file

@ -1,11 +1,11 @@
export type MeshConfig = { export type MeshConfig = {
mesh: Float32Array; colors: Uint8Array | null;
positionSize: number; faces: Uint32Array;
colorSize: number;
uvSize: number;
vertexCount: number;
stride: number;
name: string; name: string;
normals: Float32Array | null;
positions: Float32Array;
uvs: Float32Array | null;
vertexCount: number;
}; };
export class Mesh { export class Mesh {

View file

@ -12,7 +12,7 @@ export class Renderable extends Behavior {
super(app); super(app);
} }
onUpdate(time: number) { onDraw(time: number) {
this.renderer.onRenderableUpdate(time, this.transform); this.renderer.onRenderableUpdate(time, this.transform);
} }
} }

View file

@ -68,7 +68,6 @@ export class Shader {
const gl = this.gl; const gl = this.gl;
this.attach(gl.FRAGMENT_SHADER, this.fragmentCode); this.attach(gl.FRAGMENT_SHADER, this.fragmentCode);
this.attach(gl.VERTEX_SHADER, this.vertexCode); this.attach(gl.VERTEX_SHADER, this.vertexCode);
this.link();
} }
link() { link() {
@ -100,6 +99,9 @@ export class Shader {
model: Transform, model: Transform,
view: Transform view: Transform
) { ) {
const viewMatrix = view.toMat4();
mat4.invert(viewMatrix, viewMatrix);
const { gl } = this._app as WebGLApp; const { gl } = this._app as WebGLApp;
gl.useProgram(this._program as WebGLProgram); gl.useProgram(this._program as WebGLProgram);
gl.uniformMatrix4fv(this.uniform("uProjectionMatrix"), false, projection); gl.uniformMatrix4fv(this.uniform("uProjectionMatrix"), false, projection);

View file

@ -7,12 +7,19 @@ export class Transform {
public scale = vec3.fromValues(1, 1, 1) public scale = vec3.fromValues(1, 1, 1)
) {} ) {}
rotateEuler(x: number, y: number, z: number) {
quat.fromEuler(this.rotation, x, y, z);
return this;
}
toMat4() { toMat4() {
return mat4.fromRotationTranslationScale( const mat = mat4.create();
mat4.create(), mat4.fromRotationTranslationScale(
mat,
this.rotation, this.rotation,
this.position, this.position,
this.scale this.scale
); );
return mat;
} }
} }

View file

@ -19,11 +19,13 @@ export class WebGLApp {
onAfterUpdate: RenderHandle[]; onAfterUpdate: RenderHandle[];
onUpdate: RenderHandle[]; onUpdate: RenderHandle[];
onStart: RenderHandle[]; onStart: RenderHandle[];
onDraw: RenderHandle[];
} = { } = {
onBeforeUpdate: [], onBeforeUpdate: [],
onAfterUpdate: [], onAfterUpdate: [],
onUpdate: [], onUpdate: [],
onStart: [], onStart: [],
onDraw: [],
}; };
constructor(public config: WebGPUAppConfig = {}) { constructor(public config: WebGPUAppConfig = {}) {
@ -36,7 +38,10 @@ export class WebGLApp {
if (!context) { if (!context) {
throw new Error("Canvas was unable to get a webgl2 context"); throw new Error("Canvas was unable to get a webgl2 context");
} }
this.gl = context; const gl = (this.gl = context);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
// this.gl.getExtension("OES_element_index_uint"); // default
if (location.search.includes("telemetry")) { if (location.search.includes("telemetry")) {
this.telemetry = new Telemetry(this); this.telemetry = new Telemetry(this);
@ -53,6 +58,10 @@ export class WebGLApp {
} }
} }
get aspect() {
return this.canvas.clientWidth / this.canvas.clientHeight;
}
clear() { clear() {
const gl = this.gl; const gl = this.gl;
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
@ -78,16 +87,16 @@ export class WebGLApp {
this.registry.onStart.push(handle); this.registry.onStart.push(handle);
} }
doUpdate(time: number) { onDraw(handle: RenderHandle) {
// this.jobsToSubmitThisFrame = []; this.registry.onDraw.push(handle);
}
doUpdate(time: number) {
this.registry.onBeforeUpdate.forEach((handle) => handle(time, this)); this.registry.onBeforeUpdate.forEach((handle) => handle(time, this));
this.registry.onUpdate.forEach((handle) => handle(time, this)); this.registry.onUpdate.forEach((handle) => handle(time, this));
this.registry.onAfterUpdate.forEach((handle) => handle(time, this)); this.registry.onAfterUpdate.forEach((handle) => handle(time, this));
// if (this.jobsToSubmitThisFrame.length !== 0) { this.registry.onDraw.forEach((handle) => handle(time, this));
// this.device.queue.submit(this.jobsToSubmitThisFrame);
// }
} }
doStart(time: number = 0) { doStart(time: number = 0) {
@ -96,15 +105,11 @@ export class WebGLApp {
} }
async oneShot(time: number = 0) { async oneShot(time: number = 0) {
// await this.awaitRendererReady();
this.doStart(time); this.doStart(time);
this.doUpdate(time); this.doUpdate(time);
} }
async start() { async start() {
// await this.awaitRendererReady();
this.doStart(); this.doStart();
const run = (time: number) => { const run = (time: number) => {
@ -113,8 +118,4 @@ export class WebGLApp {
}; };
requestAnimationFrame(run); requestAnimationFrame(run);
} }
// commit(commandEncoder: GPUCommandBuffer) {
// this.jobsToSubmitThisFrame.push(commandEncoder);
// }
} }