3D Projection Engine
const BACKGROUND = "black";
const FOREGROUND = "green";
const FPS = 60;
game.width = 600;
game.height = 800;
const ctx = game.getContext("2d");
console.log({ ctx });
function clear() {
ctx.fillStyle = BACKGROUND;
ctx.fillRect(0, 0, game.width, game.height);
}
function point(size = 20, { x, y }) {
ctx.fillStyle = FOREGROUND;
ctx.fillRect(x - size / 2, y - size / 2, size, size);
}
function screen(p) {
// -1..1 => 0.ww/h
return {
x: ((p.x + 1) / 2) * game.width,
y: (1 - (p.y + 1) / 2) * game.height,
};
}
function project({ x, y, z }) {
return {
x: x / z,
y: y / z,
};
}
const vs = [
{ x: 0.25, y: 0.25, z: 0.5 },
{ x: -0.25, y: 0.25, z: 0.5 },
{ x: -0.25, y: -0.25, z: 0.5 },
{ x: 0.25, y: -0.25, z: 0.5 },
{ x: 0.25, y: 0.25, z: -0.5 },
{ x: -0.25, y: 0.25, z: -0.5 },
{ x: -0.25, y: -0.25, z: -0.5 },
{ x: 0.25, y: -0.25, z: -0.5 },
];
const faces = [
[0, 1, 2, 3],
[4, 5, 6, 7],
[0, 4],
[1, 5],
[2, 6],
[3, 7],
];
function translateZ({ x, y, z }, dz) {
return { x: x, y: y, z: z + dz };
}
function rotate_xz({ x, y, z }, angle) {
let x_dash = x * Math.cos(angle) - z * Math.sin(angle);
let z_dash = z * Math.cos(angle) + x * Math.sin(angle);
return { x: x_dash, y, z: z_dash };
}
function line(p1, p2) {
ctx.strokeStyle = FOREGROUND;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
let dz = 1;
let angle = 0;
function frame() {
const dt = 1 / FPS;
//dz += 1 * dt;
angle += Math.PI * dt;
clear();
// for (const v of vs) {
// point(20, screen(project(translateZ(rotate_xz(v, angle), dz))));
//}
for (const face of faces) {
for (let i = 0; i < face.length; i++) {
const a = vs[face[i]];
const b = vs[face[(i + 1) % face.length]];
line(
screen(project(translateZ(rotate_xz(a, angle), dz))),
screen(project(translateZ(rotate_xz(b, angle), dz))),
);
}
}
setTimeout(frame, 1000 / FPS);
}
setTimeout(frame, 1000 / FPS);
ashish@1:~$