oktaeder/example/script.ts

139 lines
3.1 KiB
TypeScript

/// <reference types="../node_modules/@webgpu/types" />
import { Color, Mesh, Node, PerspectiveCamera, PointLight, Quaternion, Scene, Submesh, Vector3 } from "../src/data/index";
import { Renderer, degToRad } from "../src/oktaeder";
import "./style.css";
new EventSource("/esbuild").addEventListener("change", () => location.reload());
const canvas = document.createElement("canvas");
window.addEventListener("resize", onResize);
onResize.call(window);
const renderer = await Renderer.init(canvas);
const camera = new PerspectiveCamera({
verticalFovRad: degToRad(50),
nearPlane: 0.001,
farPlane: Infinity,
});
const vertexBuffer = renderer.createVertexBuffer({ vertexCount: 12, texCoord: true });
vertexBuffer.writeTypedArray(0, {
position: new Float32Array([
0, 0, 1,
1, 0, 0,
0, 1, 0,
-1, 0, 0,
0, 0, -1,
0, 0, -1,
0, 0, -1,
1, 0, 0,
0, -1, 0,
-1, 0, 0,
0, 0, -1,
0, 0, 1,
]),
texCoord: new Float32Array([
0.5, 0.7113,
0.333333, 1,
0.166666, 0.7113,
0.333333, 0.4226,
0, 0.4226,
0, 1,
1, 1,
0.666666, 1,
0.833333, 0.7113,
0.666666, 0.4226,
1, 0.4226,
0.5, 0.7113,
]),
});
const indexBuffer = renderer.createIndexBuffer({ indexCount: 24, indexFormat: "uint16" });
indexBuffer.writeArray(0, [
0, 2, 1,
3, 4, 2,
5, 1, 2,
2, 0, 3,
6, 8, 7,
9, 8, 10,
7, 8, 11,
11, 8, 9,
]);
const submesh: Submesh = { start: 0, length: 24 };
const mesh = new Mesh({ vertexBuffer, indexBuffer, submeshes: [submesh] });
const imageBitmap = await loadImageBitmap("/uvmap.png");
const texture = renderer.createTexture({
format: "srgb",
width: imageBitmap.width,
height: imageBitmap.height,
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
});
renderer._device.queue.copyExternalImageToTexture(
{ source: imageBitmap, flipY: false },
{ texture: texture._texture },
{ width: imageBitmap.width, height: imageBitmap.height },
);
const material = renderer.createMaterial({
baseColor: Color.white(),
baseColorPartialCoverageTexture: texture,
roughness: 0.5,
metallic: 0,
});
const node = new Node({ mesh, materials: [material] });
const scene = new Scene({
nodes: [
node,
new Node({
translation: new Vector3(0, 1, -1),
light: new PointLight({ color: new Color(1, 1, 1) }),
}),
new Node({
translation: new Vector3(0, -1, -1),
light: new PointLight({ color: new Color(1, 1, 1) }),
}),
new Node({
translation: new Vector3(0, 0.8, -3),
rotation: Quaternion.fromRotationYZ(degToRad(15)),
camera,
}),
],
ambientLight: new Color(0.01, 0.01, 0.01),
});
function onResize(this: Window) {
canvas.width = this.innerWidth;
canvas.height = this.innerHeight;
}
const _quaternion = Quaternion.identity();
async function loadImageBitmap(url: string) {
const res = await fetch(url);
const blob = await res.blob();
const imageBitmap = await createImageBitmap(blob, { colorSpaceConversion: "none" });
return imageBitmap;
}
function draw(timeMs: number) {
const time = 0.001 * timeMs;
node.setRotation(_quaternion.setRotationZX(-0.5 * time));
renderer.render(scene, camera);
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
document.body.appendChild(canvas);