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
- The "generator" does
- **Output generation**
- Generates a work list from .js files within `html`
- Creates this README.md file
- Creates the index.html file
- Creates individual HTML files for each work
- Bun renders TypeScript and GLSL to html/
- src/public/ is copied to html/
- This doubles as an "archival" system to preserve scuffedness
- .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
- Shader types
- Primitive objects
- A canvas to draw on
- WebGL hooks
- Helpers, etc
- **Platform**
- WebGLApp provides render loop, canvas, and other stuff
- Behavior provides automatic registration of WebGLApp events (like Unity MonoBehaviors)
- Shader provides GLSL shader tooling
- 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

BIN
bun.lockb

Binary file not shown.

View file

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

View file

@ -1,6 +1,54 @@
import chalk from "chalk";
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 () => {
const meshes = globSync("src/meshes/*.ply");
@ -8,8 +56,47 @@ export const convertMeshes = async () => {
const ply = await Bun.file(file).text();
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");
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(
headerLines
.find((header) => header.startsWith("element vertex"))
@ -20,49 +107,152 @@ export const convertMeshes = async () => {
throw new Error("couldn't get vertex count...");
}
const values: number[] = [];
const vertexes: Vertex[] = [];
const faces: Face[] = [];
for (const line of body.split("\n")) {
// line looks like
// 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 components = line.split(" ");
const [x, y, z, r, g, b, a, u, v] = line.split(" ");
if (!g) {
if (!components || !components[0]) {
continue;
}
values.push(
parseFloat(x),
parseFloat(y),
parseFloat(z),
1,
parseFloat(r),
parseFloat(g)
);
// do we only have 4 components?
if (components[0] === "3" || components.length === 4) {
const [, a, b, c] = components;
if (colorSize > 0) {
values.push(parseFloat(b), parseFloat(a), parseFloat(u), parseFloat(v));
// We do??!?! 🥺👉👈
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 outString = `
import { Mesh } from "../renderer/mesh";
// prettier-ignore
const mesh = new Float32Array(${JSON.stringify(values)});
const positions = new Float32Array(${JSON.stringify(positions)});
export default new Mesh({
mesh,
positionSize: 4,
colorSize: ${colorSize},
uvSize: 2,
vertexCount: ${vertexCount},
stride: ${4 + colorSize + 2},
name: ${JSON.stringify(file)}
// prettier-ignore
const colors = ${
vertexConfig.colors ? `new Uint8Array(${JSON.stringify(colors)})` : "null"
};
// prettier-ignore
const uvs = ${
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}...`));
}
};
const parser = {
float: (x: string) => parseFloat(x),
uchar: (x: string) => Number(x),
};

View file

@ -22,19 +22,24 @@ https://art.mekanoe.com
### Infrastructure
- The "generator" does
- **Output generation**
- Generates a work list from .js files within `html`
- Creates this README.md file
- Creates the index.html file
- Creates individual HTML files for each work
- Bun renders TypeScript and GLSL to html/
- src/public/ is copied to html/
- This doubles as an "archival" system to preserve scuffedness
- .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
- Shader types
- Primitive objects
- A canvas to draw on
- WebGL hooks
- Helpers, etc
- **Platform**
- WebGLApp provides render loop, canvas, and other stuff
- Behavior provides automatic registration of WebGLApp events (like Unity MonoBehaviors)
- Shader provides GLSL shader tooling
- 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

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",
"scripts": {
"build": "bun $BUNFLAGS .",
"build:watch": "BUNFLAGS=--watch bun run build",
"build:watch": "nodemon -e ts,js,frag,vert,glsl,ply",
"serve": "serve ./html",
"dev": "run-p serve build:watch"
},
"devDependencies": {
"bun-types": "latest",
"npm-run-all2": "^6.1.1",
"nodemon": "^3.0.1",
"prettier": "^3.0.3"
},
"peerDependencies": {
@ -23,5 +24,14 @@
"glob": "^10.3.10",
"serve": "^14.2.1",
"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 { BasicPlane } from "./basic-plane";
import { App } from "./app";
import fragment from "./fragment.glsl";
import vertex from "./vertex.glsl";
// import { Shader } from "./shader";
// import { BasicPlane } from "./basic-plane";
// import { App } from "./app";
// import fragment from "./fragment.glsl";
// import vertex from "./vertex.glsl";
const app = new App({ fov: 20 });
const gl = app.gl;
// const app = new App({ fov: 20 });
// const gl = app.gl;
const shader = new Shader(app)
.attach(gl.VERTEX_SHADER, vertex)
.attach(gl.FRAGMENT_SHADER, fragment)
.link();
// const shader = new Shader(app)
// .attach(gl.VERTEX_SHADER, vertex)
// .attach(gl.FRAGMENT_SHADER, fragment)
// .link();
const plane = new BasicPlane(app);
plane.attachShader(shader);
// const plane = new BasicPlane(app);
// plane.attachShader(shader);
app.loop();
// app.loop();

View file

@ -1,42 +1,43 @@
// 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";
// // This one is archived.
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(
[0, 0, -6],
quat.fromEuler(quat.create(), 15, 0, 0)
);
// const app = new WebGLApp({ fov: 45 });
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 cameraStride = 3;
const x = Math.sin(time * 0.0001) * (cameraStride * 2 - cameraStride * 0.5);
// const modelTransform = new Transform([0, 0, 4]);
camera.rotation = quat.fromEuler(quat.create(), x, 0, 0);
// modelTransform.rotation = quat.fromEuler(quat.create(), x, 0, 0);
});
// app.onUpdate((time: number) => {
// const cameraStride = 3;
// const x = Math.sin(time * 0.0001) * (cameraStride * 2 - cameraStride * 0.5);
new Renderable(
app,
modelTransform,
new MeshRenderer(app, torus, uvRainbow(app), camera, {
drawMode: app.gl.TRIANGLE_FAN,
})
);
// camera.rotation = quat.fromEuler(quat.create(), x, 0, 0);
// // modelTransform.rotation = quat.fromEuler(quat.create(), x, 0, 0);
// });
// new Renderable(
// app,
// new Transform([1, 0, 0]),
// new MeshRenderer(app, plane, uvRainbow(app))
// modelTransform,
// 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 torus from "../meshes/torus";
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 camera = new Transform([0, -2, -6]);
new Renderable(
app,
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();

View file

@ -1,10 +1,12 @@
#version 300 es
precision highp float;
uniform float uTime;
uniform float uSinTime;
uniform float uCosTime;
varying highp vec2 vTextureCoord;
in vec2 vTextureCoord;
out vec4 fragColor;
vec3 rgb2hsv(vec3 c) {
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;
vec3 rgb = hsv2rgb(hsv);
gl_FragColor = vec4(rgb, 1.0);
gl_FragColor = clamp(gl_FragColor, 0.0, 1.0);
fragColor = vec4(rgb, 1.0);
fragColor = clamp(fragColor, 0.0, 1.0);
}

View file

@ -1,10 +1,11 @@
attribute vec4 aVertexPosition;
attribute vec2 aTextureCoord;
#version 300 es
layout(location = 0) in vec4 aVertexPosition;
layout(location = 1) in vec2 aTextureCoord;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying highp vec2 vTextureCoord;
out vec2 vTextureCoord;
void main() {
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";
// prettier-ignore
const mesh = new Float32Array([
// f4 position, f4 color, f2 uv
-1, -1, 0, 1, 1, 1, 1, 1, 0, 0,
1, -1, 0, 1, 1, 1, 1, 1, 1, 0,
-1, 1, 0, 1, 1, 1, 1, 1, 0, 1,
1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
]);
const positions = new Float32Array([1,-1,0,-1,1,0,-1,-1,0,1,1,0]);
// prettier-ignore
const colors = null;
// 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({
mesh,
positionSize: 4,
colorSize: 4,
uvSize: 2,
vertexCount: 4,
stride: 10,
name: "plane",
colors,
faces,
name: "src/meshes/plane.ply",
normals,
positions,
uvs,
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;
onUpdate?(...args: any[]): void;
onAfterUpdate?(...args: any[]): void;
onDraw?(...args: any[]): void;
constructor(public app: WebGLApp) {
this.onStart && app.onStart(this.onStart.bind(this));
this.onUpdate && app.onUpdate(this.onUpdate.bind(this));
this.onAfterUpdate && app.onAfterUpdate(this.onAfterUpdate.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 { WebGLApp } from "./webgl";
import { Transform } from "./transform";
import { errorShader } from "../common-shaders/error";
export type MeshRendererConfig = {
drawMode?: number;
cullMode?: number;
};
export class MeshRenderer extends Behavior {
private modelMatrix = mat4.create();
private projectionMatrix = mat4.create();
private _meshBuffer?: WebGLBuffer;
private buffers: {
position?: WebGLBuffer;
uv?: WebGLBuffer;
normal?: WebGLBuffer;
color?: WebGLBuffer;
faces?: WebGLBuffer;
} = {};
constructor(
public app: WebGLApp,
@ -25,106 +31,130 @@ export class MeshRenderer extends Behavior {
super(app);
}
get meshBuffer() {
if (this._meshBuffer) {
return this._meshBuffer;
}
throw new Error("mesh buffer not ready");
configure(config: MeshRendererConfig) {
this.config = config;
return this;
}
initializeMeshBuffer() {
makeBuffer(data: TypedArray, target: number = 34962) {
const gl = this.app.gl;
const buffer = gl.createBuffer();
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() {
const gl = this.app.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.meshBuffer);
initializeBuffers() {
this.buffers.faces = this.makeBuffer(
this.mesh.config.faces,
this.app.gl.ELEMENT_ARRAY_BUFFER
);
const positionLocation = this.shader.attrib("aVertexPosition");
if (positionLocation !== -1) {
this.buffers.position = this.makeBuffer(this.mesh.config.positions);
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(
positionLocation,
this.mesh.config.positionSize,
gl.FLOAT,
false,
4 * this.mesh.config.stride,
attributePosition,
elements,
glType,
normalized,
0,
0
);
gl.enableVertexAttribArray(positionLocation);
this.shader.bindAttrib(positionLocation, "aVertexPosition");
gl.enableVertexAttribArray(attributePosition);
}
if (this.mesh.config.colorSize !== 0) {
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");
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
onStart() {
mat4.perspective(
this.projectionMatrix,
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.zFar || 100
);
this.shader.compile();
this.initializeMeshBuffer();
this.initializeAttributes();
this.initializeBuffers();
this.shader.link();
}
onRenderableUpdate(time: number, transform: Transform) {
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();
gl.bindBuffer(gl.ARRAY_BUFFER, this.meshBuffer);
this.shader.setupUniforms(
time,
this.projectionMatrix,
transform,
this.camera
);
gl.drawArrays(
this.config.drawMode ?? gl.TRIANGLE_STRIP,
0,
this.mesh.config.vertexCount
gl.drawElements(
this.config.drawMode ?? gl.TRIANGLES,
this.mesh.config.faces.length,
gl.UNSIGNED_INT,
0
);
// gl.drawArrays(
// 0,
// this.mesh.config.vertexCount
// );
const err = gl.getError();
if (err !== 0) {
console.log({ err });

View file

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

View file

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

View file

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

View file

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

View file

@ -19,11 +19,13 @@ export class WebGLApp {
onAfterUpdate: RenderHandle[];
onUpdate: RenderHandle[];
onStart: RenderHandle[];
onDraw: RenderHandle[];
} = {
onBeforeUpdate: [],
onAfterUpdate: [],
onUpdate: [],
onStart: [],
onDraw: [],
};
constructor(public config: WebGPUAppConfig = {}) {
@ -36,7 +38,10 @@ export class WebGLApp {
if (!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")) {
this.telemetry = new Telemetry(this);
@ -53,6 +58,10 @@ export class WebGLApp {
}
}
get aspect() {
return this.canvas.clientWidth / this.canvas.clientHeight;
}
clear() {
const gl = this.gl;
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);
}
doUpdate(time: number) {
// this.jobsToSubmitThisFrame = [];
onDraw(handle: RenderHandle) {
this.registry.onDraw.push(handle);
}
doUpdate(time: number) {
this.registry.onBeforeUpdate.forEach((handle) => handle(time, this));
this.registry.onUpdate.forEach((handle) => handle(time, this));
this.registry.onAfterUpdate.forEach((handle) => handle(time, this));
// if (this.jobsToSubmitThisFrame.length !== 0) {
// this.device.queue.submit(this.jobsToSubmitThisFrame);
// }
this.registry.onDraw.forEach((handle) => handle(time, this));
}
doStart(time: number = 0) {
@ -96,15 +105,11 @@ export class WebGLApp {
}
async oneShot(time: number = 0) {
// await this.awaitRendererReady();
this.doStart(time);
this.doUpdate(time);
}
async start() {
// await this.awaitRendererReady();
this.doStart();
const run = (time: number) => {
@ -113,8 +118,4 @@ export class WebGLApp {
};
requestAnimationFrame(run);
}
// commit(commandEncoder: GPUCommandBuffer) {
// this.jobsToSubmitThisFrame.push(commandEncoder);
// }
}