]> nyquistdev.net Git - basic-3d-example.git/commitdiff
first commit basic-projection
authorA-Larsen <austin.larsen@mailfence.com>
Thu, 15 Oct 2026 01:20:15 +0000 (21:20 -0400)
committerA-Larsen <austin.larsen@mailfence.com>
Thu, 15 Oct 2026 01:20:15 +0000 (21:20 -0400)
index.js

index 17f28704b3424b7c205831d93befbaa10666f85c..c1018fda54062019181114e82c3b72e04df6064b 100644 (file)
--- a/index.js
+++ b/index.js
-
-/* ===================== SETUP ===================== */
-const game = document.getElementById("game");
-game.width = 800;
-game.height = 800;
-const ctx = game.getContext("2d");
-
-const BACKGROUND = "#101010";
-const FOREGROUND = "#50FF50";
-const TEXT = "#FFFFFF";
-
-const FPS = 60;
-const NEAR = 0.1;
-
-/* ===================== GEOMETRY ===================== */
-const vertices = [
-    {x:  0.25, y:  0.25, z:  0.25},
-    {x: -0.25, y:  0.25, z:  0.25},
-    {x: -0.25, y: -0.25, z:  0.25},
-    {x:  0.25, y: -0.25, z:  0.25},
-    {x:  0.25, y:  0.25, z: -0.25},
-    {x: -0.25, y:  0.25, z: -0.25},
-    {x: -0.25, y: -0.25, z: -0.25},
-    {x:  0.25, y: -0.25, z: -0.25},
-];
-
-const edges = [
-    [0,1,2,3],
-    [4,5,6,7],
-    [0,4],[1,5],[2,6],[3,7]
-];
-
-/* ===================== OBJECT ===================== */
-let objectPos = { x: 0, y: 0, z: 2 };
-let angle = 0;
-
-const AXIS_MODES = [
-    ["x"], ["y"], ["z"],
-    ["x","y"], ["y","z"], ["x","z"],
-    ["x","y","z"]
-];
-let axisModeIndex = AXIS_MODES.length - 1;
-let activeAxes = AXIS_MODES[axisModeIndex];
-
-/* ===================== CAMERA ===================== */
-let camera = {
-    position: { x: 0, y: 0, z: 0 },
-    rotation: [1,0,0,0],
-    speed: 0.05,
-    sensitivity: 0.002
-};
-
-/* ===================== INPUT ===================== */
-const keys = {};
-window.addEventListener("keydown", e => keys[e.key.toLowerCase()] = true);
-window.addEventListener("keyup",   e => keys[e.key.toLowerCase()] = false);
-
-/* ===================== QUATERNIONS ===================== */
-function quatFromAxisAngle(axis, angle) {
-    const h = angle / 2;
-    const s = Math.sin(h);
-    return [Math.cos(h), axis.x*s, axis.y*s, axis.z*s];
-}
-
-function quatMultiply(a, b) {
-    const [w1,x1,y1,z1] = a;
-    const [w2,x2,y2,z2] = b;
-    return [
-        w1*w2 - x1*x2 - y1*y2 - z1*z2,
-        w1*x2 + x1*w2 + y1*z2 - z1*y2,
-        w1*y2 - x1*z2 + y1*w2 + z1*x2,
-        w1*z2 + x1*y2 - y1*x2 + z1*w2
-    ];
+const canvas = document.getElementById("game");
+canvas.width = 800;
+canvas.height = 800;
+const ctx = canvas.getContext("2d");
+
+let objects = {
+    cube: {
+        vertices: [
+            // front
+            {x: -0.5 , y: -0.5, z: 0.5}, // top left
+            {x:  0.5 , y: -0.5, z: 0.5}, // top right
+            {x:  0.5 , y: 0.5, z: 0.5},  // bottom right
+            {x: -0.5 , y: 0.5, z: 0.5},  // bottom left
+
+            // back
+            {x: -0.5 , y: -0.5, z: -0.5}, // top left
+            {x:  0.5 , y: -0.5, z: -0.5}, // top right
+            {x:  0.5 , y: 0.5, z: -0.5},  // bottom right
+            {x: -0.5 , y: 0.5, z: -0.5},  // bottom left
+        ],
+        index_buffers: [
+            [0, 1, 2, 3],
+            [4, 5, 6, 7],
+            [0, 4],
+            [1, 5],
+            [2, 6],
+            [3, 7],
+        ]
+    }
 }
 
-function quatConjugate(q) {
-    return [q[0], -q[1], -q[2], -q[3]];
+function project({x, y, z}) {
+    return {
+        x: x/z,
+        y: y/z
+    }
 }
 
-function quatRotateVector(q, v) {
-    const qv = [0, v.x, v.y, v.z];
-    const r = quatMultiply(quatMultiply(q, qv), quatConjugate(q));
-    return { x: r[1], y: r[2], z: r[3] };
-}
+function screen({x, y}) {
+    //-1..1 -> 0..2 -> 0..1 -> 0..w/h
+    // p.y *= -1
+    return {
+        x: (x + 1)/2 * canvas.width,
+        y: (1 -(y + 1)/2) * canvas.height
 
-/* ===================== CAMERA VECTORS ===================== */
-function cameraForward() {
-    return quatRotateVector(camera.rotation, {x:0,y:0,z:1});
-}
-function cameraRight() {
-    return quatRotateVector(camera.rotation, {x:1,y:0,z:0});
-}
-function cameraUp() {
-    return quatRotateVector(camera.rotation, {x:0,y:1,z:0});
+    }
 }
 
-function applyCamera(p) {
-    const v = {
-        x: p.x - camera.position.x,
-        y: p.y - camera.position.y,
-        z: p.z - camera.position.z
-    };
-    return quatRotateVector(quatConjugate(camera.rotation), v);
+function clear() {
+    ctx.fillStyle = "#000000"
+    ctx.fillRect(0, 0, canvas.width, canvas.height);
 }
 
-/* ===================== PROJECTION ===================== */
-function project(p) {
-    return { x: p.x / p.z, y: p.y / p.z };
-}
-function screen(p) {
-    return {
-        x: (p.x + 1) * 0.5 * game.width,
-        y: (1 - (p.y + 1) * 0.5) * game.height
-    };
+function translateZ({x, y, z}, dz) {
+    return {x: x, y: y, z: z + dz};
 }
 
-/* ===================== DRAW ===================== */
-function clear() {
-    ctx.fillStyle = BACKGROUND;
-    ctx.fillRect(0,0,game.width,game.height);
-
-    ctx.fillStyle = TEXT;
-    ctx.font = "14px monospace";
-    ctx.fillText("WASD: move | QE: up/down", 10, 18);
-    ctx.fillText("Mouse: look | Click canvas to lock", 10, 36);
-    ctx.fillText("Click canvas again: change cube axes", 10, 54);
-    ctx.fillText("Cube axes: " + activeAxes.join(","), 10, 72);
-}
-
-function line(a,b) {
-    ctx.strokeStyle = FOREGROUND;
-    ctx.lineWidth = 2;
+function line(p1, p2) {
+    ctx.lineWidth = 3;
+    ctx.strokeStyle = "#FF0000";
     ctx.beginPath();
-    ctx.moveTo(a.x,a.y);
-    ctx.lineTo(b.x,b.y);
+    ctx.moveTo(p1.x, p1.y);
+    ctx.lineTo(p2.x, p2.y);
     ctx.stroke();
 }
 
-/* ===================== LOOP ===================== */
-function frame() {
-    angle += Math.PI / FPS;
-
-    // --- cube rotation quaternion ---
-    let q = [1,0,0,0];
-    for (const ax of activeAxes) {
-        if (ax==="x") q = quatMultiply(quatFromAxisAngle({x:1,y:0,z:0}, angle), q);
-        if (ax==="y") q = quatMultiply(quatFromAxisAngle({x:0,y:1,z:0}, angle), q);
-        if (ax==="z") q = quatMultiply(quatFromAxisAngle({x:0,y:0,z:1}, angle), q);
-    }
-
-    // --- camera movement ---
-    if (keys["w"]) {
-        const f = cameraForward();
-        camera.position.x += f.x * camera.speed;
-        camera.position.y += f.y * camera.speed;
-        camera.position.z += f.z * camera.speed;
-    }
-    if (keys["s"]) {
-        const f = cameraForward();
-        camera.position.x -= f.x * camera.speed;
-        camera.position.y -= f.y * camera.speed;
-        camera.position.z -= f.z * camera.speed;
-    }
-    if (keys["a"]) {
-        const r = cameraRight();
-        camera.position.x -= r.x * camera.speed;
-        camera.position.y -= r.y * camera.speed;
-        camera.position.z -= r.z * camera.speed;
-    }
-    if (keys["d"]) {
-        const r = cameraRight();
-        camera.position.x += r.x * camera.speed;
-        camera.position.y += r.y * camera.speed;
-        camera.position.z += r.z * camera.speed;
-    }
-    if (keys["q"]) {
-        const u = cameraUp();
-        camera.position.x -= u.x * camera.speed;
-        camera.position.y -= u.y * camera.speed;
-        camera.position.z -= u.z * camera.speed;
-    }
-    if (keys["e"]) {
-        const u = cameraUp();
-        camera.position.x += u.x * camera.speed;
-        camera.position.y += u.y * camera.speed;
-        camera.position.z += u.z * camera.speed;
-    }
 
+function frame() {
     clear();
+    let dz = 1.5
 
-    for (const e of edges) {
-        for (let i=0;i<e.length;i++) {
-            let a = vertices[e[i]];
-            let b = vertices[e[(i+1)%e.length]];
+    let index_buffers = objects.cube.index_buffers;
+    let vertices = objects.cube.vertices;
 
-            a = quatRotateVector(q, a);
-            b = quatRotateVector(q, b);
+    for (const face of index_buffers) {
+        for (let j = 0; j < face.length; j++) {
+            let idA = face[j];
+            let idB = face[(j + 1) % face.length];
 
-            a = { x:a.x+objectPos.x, y:a.y+objectPos.y, z:a.z+objectPos.z };
-            b = { x:b.x+objectPos.x, y:b.y+objectPos.y, z:b.z+objectPos.z };
+            const a = vertices[idA];
+            const b = vertices[idB];
 
-            a = applyCamera(a);
-            b = applyCamera(b);
+            let ta = screen(project(translateZ(a, dz)));
+            let tb = screen(project(translateZ(b, dz)));
 
-            if (a.z<=NEAR || b.z<=NEAR) continue;
-            line(screen(project(a)), screen(project(b)));
+            line(ta, tb);
         }
     }
-
-    setTimeout(frame, 1000/FPS);
 }
-frame();
-
-/* ===================== INTERACTION ===================== */
-game.onclick = () => {
-    game.requestPointerLock();
-    axisModeIndex = (axisModeIndex + 1) % AXIS_MODES.length;
-    activeAxes = AXIS_MODES[axisModeIndex];
-};
-
-window.addEventListener("mousemove", e => {
-    if (document.pointerLockElement !== game) return;
-
-    const yaw   = quatFromAxisAngle({x:0,y:1,z:0}, -e.movementX * camera.sensitivity);
-    const pitch = quatFromAxisAngle({x:1,y:0,z:0}, -e.movementY * camera.sensitivity);
 
-    camera.rotation = quatMultiply(yaw, camera.rotation);
-    camera.rotation = quatMultiply(camera.rotation, pitch);
-});
+frame()