From 371d0368fed695b3ac1500dd29e253e53eb9e32d Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Sat, 11 Apr 2026 15:16:41 +1000
Subject: [PATCH 01/18] Update main.js
---
main.js | 50 +++++++++++++++++---------------------------------
1 file changed, 17 insertions(+), 33 deletions(-)
diff --git a/main.js b/main.js
index 8f7f527..0a8ca6e 100644
--- a/main.js
+++ b/main.js
@@ -184,15 +184,6 @@ function getViewMatrix(camera) {
].flat();
return camToWorld;
}
-// function translate4(a, x, y, z) {
-// return [
-// ...a.slice(0, 12),
-// a[0] * x + a[4] * y + a[8] * z + a[12],
-// a[1] * x + a[5] * y + a[9] * z + a[13],
-// a[2] * x + a[6] * y + a[10] * z + a[14],
-// a[3] * x + a[7] * y + a[11] * z + a[15],
-// ];
-// }
function multiply4(a, b) {
return [
@@ -1186,32 +1177,25 @@ async function main() {
activeKeys.includes("ShiftLeft") ||
activeKeys.includes("ShiftRight");
- if (activeKeys.includes("ArrowUp")) {
- if (shiftKey) {
- inv = translate4(inv, 0, -0.03, 0);
- } else {
- inv = translate4(inv, 0, 0, 0.1);
- }
- }
- if (activeKeys.includes("ArrowDown")) {
- if (shiftKey) {
- inv = translate4(inv, 0, 0.03, 0);
- } else {
- inv = translate4(inv, 0, 0, -0.1);
- }
- }
- if (activeKeys.includes("ArrowLeft"))
- inv = translate4(inv, -0.03, 0, 0);
- //
- if (activeKeys.includes("ArrowRight"))
- inv = translate4(inv, 0.03, 0, 0);
- // inv = rotate4(inv, 0.01, 0, 1, 0);
- if (activeKeys.includes("KeyA")) inv = rotate4(inv, -0.01, 0, 1, 0);
- if (activeKeys.includes("KeyD")) inv = rotate4(inv, 0.01, 0, 1, 0);
+ // WASD - First person movement
+ if (activeKeys.includes("KeyW")) inv = translate4(inv, 0, 0, 0.1); // Forward
+ if (activeKeys.includes("KeyS")) inv = translate4(inv, 0, 0, -0.1); // Backward
+ if (activeKeys.includes("KeyA")) inv = translate4(inv, -0.03, 0, 0); // Strafe left
+ if (activeKeys.includes("KeyD")) inv = translate4(inv, 0.03, 0, 0); // Strafe right
+
+ // Space/Shift - Up/Down movement
+ if (activeKeys.includes("Space")) inv = translate4(inv, 0, -0.03, 0); // Up
+ if (shiftKey) inv = translate4(inv, 0, 0.03, 0); // Down
+
+ // Arrow keys - Camera rotation (look around)
+ if (activeKeys.includes("ArrowLeft")) inv = rotate4(inv, -0.01, 0, 1, 0); // Look left
+ if (activeKeys.includes("ArrowRight")) inv = rotate4(inv, 0.01, 0, 1, 0); // Look right
+ if (activeKeys.includes("ArrowUp")) inv = rotate4(inv, 0.01, 1, 0, 0); // Look up
+ if (activeKeys.includes("ArrowDown")) inv = rotate4(inv, -0.01, 1, 0, 0); // Look down
+
+ // Q/E - Roll camera (optional)
if (activeKeys.includes("KeyQ")) inv = rotate4(inv, 0.01, 0, 0, 1);
if (activeKeys.includes("KeyE")) inv = rotate4(inv, -0.01, 0, 0, 1);
- if (activeKeys.includes("KeyW")) inv = rotate4(inv, 0.005, 1, 0, 0);
- if (activeKeys.includes("KeyS")) inv = rotate4(inv, -0.005, 1, 0, 0);
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
let isJumping = activeKeys.includes("Space");
From f5f76f78299376e2b6d4fc92f52c4388ee29251a Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 15 Apr 2026 11:29:26 +1000
Subject: [PATCH 02/18] Update main.js
---
main.js | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/main.js b/main.js
index 0a8ca6e..3c2a276 100644
--- a/main.js
+++ b/main.js
@@ -1466,3 +1466,12 @@ main().catch((err) => {
document.getElementById("spinner").style.display = "none";
document.getElementById("message").innerText = err.toString();
});
+
+window.addEventListener('mousedown', () => {
+ window.focus();
+});
+
+// This helps when the mouse enters the iframe
+window.addEventListener('mouseenter', () => {
+ window.focus();
+});
From 6a45d8ae8dd2d674b638f77bbfd8b63522c048b8 Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 16 Apr 2026 11:39:18 +1000
Subject: [PATCH 03/18] Update index.html
---
index.html | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/index.html b/index.html
index c25c895..f7e641e 100644
--- a/index.html
+++ b/index.html
@@ -198,23 +198,25 @@
WebGL 3D Gaussian Splat Viewer
By Kevin Kwok.
Code on
GithubGithub.
- Use mouse or arrow keys to navigate.
+ Use arrow keys (recommneded) or mouse to navigate.
-movement (arrow keys)
-- left/right arrow keys to strafe side to side
-- up/down arrow keys to move forward/back
-- space to jump
+
movement (WASD)
+- A and D to strafe side to side
+- W and S arrow keys to move forward/back
+- space to jump / move up
+
+camera angle (arrow keys and QE)
+- left and right arrow to turn camera left/right
+- top and bottom arrow to tilt camera up/down
-camera angle (wasd)
-- a/d to turn camera left/right
-- w/s to tilt camera up/down
-- q/e to roll camera counterclockwise/clockwise
-- i/k and j/l to orbit
trackpad
- scroll up/down/left/right to orbit
@@ -226,6 +228,10 @@
WebGL 3D Gaussian Splat Viewer
- click and drag to orbit
- right click (or ctrl/cmd key) and drag up/down to move
+advanced:
+- q/e to roll camera counterclockwise/clockwise
+- i/k and j/l to orbit
+
touch (mobile)
- one finger to orbit
- two finger pinch to move forward/back
From 744ff513fec294cad7fd731c5ba99f634c8e484d Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 16 Apr 2026 11:41:51 +1000
Subject: [PATCH 04/18] Update index.html
---
index.html | 22 +++++++++-------------
1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/index.html b/index.html
index f7e641e..350f9d1 100644
--- a/index.html
+++ b/index.html
@@ -191,20 +191,16 @@
-
-
WebGL 3D Gaussian Splat Viewer
-
-
- By Kevin Kwok.
- Code on
- GithubGithub.
-
+
WebGL 3D Gaussian Splat Viewer
+
+
+ By Kevin Kwok.
+ Code on Github.
+
+ Forked by Jeremy Jiao. Code on
+ Github.
+
-
Use arrow keys (recommneded) or mouse to navigate.
From 003c7f6d2a183389ad6c64cdde7ca92dff5192dc Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 7 May 2026 10:10:00 +1000
Subject: [PATCH 05/18] Update main.js
---
main.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/main.js b/main.js
index 3c2a276..077a877 100644
--- a/main.js
+++ b/main.js
@@ -1193,9 +1193,9 @@ async function main() {
if (activeKeys.includes("ArrowUp")) inv = rotate4(inv, 0.01, 1, 0, 0); // Look up
if (activeKeys.includes("ArrowDown")) inv = rotate4(inv, -0.01, 1, 0, 0); // Look down
- // Q/E - Roll camera (optional)
- if (activeKeys.includes("KeyQ")) inv = rotate4(inv, 0.01, 0, 0, 1);
- if (activeKeys.includes("KeyE")) inv = rotate4(inv, -0.01, 0, 0, 1);
+ // Q/E - Roll camera (optional) modified from original inverted roll.
+ if (activeKeys.includes("KeyE")) inv = rotate4(inv, 0.01, 0, 0, 1);
+ if (activeKeys.includes("KeyQ")) inv = rotate4(inv, -0.01, 0, 0, 1);
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
let isJumping = activeKeys.includes("Space");
From 28258abf4e3e121e99b883c56dfa50a9f42dee9f Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 15:46:53 +1000
Subject: [PATCH 06/18] Update main.js
---
main.js | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/main.js b/main.js
index 077a877..060fe01 100644
--- a/main.js
+++ b/main.js
@@ -1,3 +1,13 @@
+
+
let cameras = [
{
id: 0,
From d8caee4f071731b57b82342e6d85d023302d0554 Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 15:49:49 +1000
Subject: [PATCH 07/18] Update main.js
---
main.js | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/main.js b/main.js
index 060fe01..8098c4c 100644
--- a/main.js
+++ b/main.js
@@ -1,12 +1,4 @@
-
+
let cameras = [
{
From ea01adb646e3d1c5c6ef3246b565c0302e4693ba Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 15:53:56 +1000
Subject: [PATCH 08/18] Update main.js
---
main.js | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/main.js b/main.js
index 8098c4c..7de77d8 100644
--- a/main.js
+++ b/main.js
@@ -752,7 +752,28 @@ async function main() {
const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
const reader = req.body.getReader();
- let splatData = new Uint8Array(req.headers.get("content-length"));
+
+ // Fallback if content-length is missing or altered by Gzip compression
+ let contentLength = parseInt(req.headers.get("content-length"));
+ let splatData = new Uint8Array(contentLength || 0);
+
+ // Stream the data chunks smoothly into a dynamic array builder
+ let bytesRead = 0;
+ let chunks = [];
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ chunks.push(value);
+ bytesRead += value.length;
+ }
+
+ // Combine all chunks into the final array accurately matching unpacked size
+ splatData = new Uint8Array(bytesRead);
+ let offset = 0;
+ for (let chunk of chunks) {
+ splatData.set(chunk, offset);
+ offset += chunk.length;
+ }
const downsample =
splatData.length / rowLength > 500000 ? 1 : 1 / devicePixelRatio;
From f1b600be277e900cfa2cbe99d23b22b6a41cded5 Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 16:04:49 +1000
Subject: [PATCH 09/18] Update main.js
---
main.js | 55 +++++++++++++++++++++++++++----------------------------
1 file changed, 27 insertions(+), 28 deletions(-)
diff --git a/main.js b/main.js
index 7de77d8..c06513d 100644
--- a/main.js
+++ b/main.js
@@ -743,37 +743,36 @@ async function main() {
"https://huggingface.co/cakewalk/splat-data/resolve/main/",
);
const req = await fetch(url, {
- mode: "cors", // no-cors, *cors, same-origin
- credentials: "omit", // include, *same-origin, omit
- });
- console.log(req);
- if (req.status != 200)
- throw new Error(req.status + " Unable to load " + req.url);
+ mode: "cors", // no-cors, *cors, same-origin
+ credentials: "omit", // include, *same-origin, omit
+ });
+ console.log(req);
+ if (req.status != 200)
+ throw new Error(req.status + " Unable to load " + req.url);
- const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
- const reader = req.body.getReader();
-
- // Fallback if content-length is missing or altered by Gzip compression
- let contentLength = parseInt(req.headers.get("content-length"));
- let splatData = new Uint8Array(contentLength || 0);
+ const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
+ const reader = req.body.getReader();
+
+ // HIDE ERROR: Allocate a massive flat placeholder array size (e.g. 150MB)
+ // This gives the compressed gzip stream chunks plenty of room to expand
+ // so it never overflows or throws an out-of-bounds error banner.
+ let splatData = new Uint8Array(150 * 1024 * 1024);
+
+ let bytesRead = 0;
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+
+ // Put the incoming data chunk directly into our oversized array
+ splatData.set(value, bytesRead);
+ bytesRead += value.length;
+ }
+
+ // Shrink the array down to exactly how many bytes were actually read
+ // so WebGL doesn't try to render empty space
+ splatData = splatData.subarray(0, bytesRead);
- // Stream the data chunks smoothly into a dynamic array builder
- let bytesRead = 0;
- let chunks = [];
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- chunks.push(value);
- bytesRead += value.length;
- }
- // Combine all chunks into the final array accurately matching unpacked size
- splatData = new Uint8Array(bytesRead);
- let offset = 0;
- for (let chunk of chunks) {
- splatData.set(chunk, offset);
- offset += chunk.length;
- }
const downsample =
splatData.length / rowLength > 500000 ? 1 : 1 / devicePixelRatio;
From 927a681af60c113eec96aff3d03d3e84f0b57fbf Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 16:07:04 +1000
Subject: [PATCH 10/18] Update main.js
---
main.js | 50 ++++++++++++++++++++++++++------------------------
1 file changed, 26 insertions(+), 24 deletions(-)
diff --git a/main.js b/main.js
index c06513d..4e83612 100644
--- a/main.js
+++ b/main.js
@@ -729,6 +729,7 @@ let defaultViewMatrix = [
0.03, 6.55, 1,
];
let viewMatrix = defaultViewMatrix;
+
async function main() {
let carousel = true;
const params = new URLSearchParams(location.search);
@@ -736,44 +737,45 @@ async function main() {
viewMatrix = JSON.parse(decodeURIComponent(location.hash.slice(1)));
carousel = false;
} catch (err) {}
+
const url = new URL(
// "nike.splat",
// location.href,
params.get("url") || "train.splat",
"https://huggingface.co/cakewalk/splat-data/resolve/main/",
);
+
const req = await fetch(url, {
- mode: "cors", // no-cors, *cors, same-origin
- credentials: "omit", // include, *same-origin, omit
- });
- console.log(req);
- if (req.status != 200)
- throw new Error(req.status + " Unable to load " + req.url);
+ mode: "cors", // no-cors, *cors, same-origin
+ credentials: "omit", // include, *same-origin, omit
+ });
+ console.log(req);
+ if (req.status != 200)
+ throw new Error(req.status + " Unable to load " + req.url);
- const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
- const reader = req.body.getReader();
+ const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
+ const reader = req.body.getReader();
+
+ // HIDE ERROR: Allocate a massive flat placeholder array size (e.g. 150MB)
+ // This gives the compressed gzip stream chunks plenty of room to expand
+ // so it never overflows or throws an out-of-bounds error banner.
+ let splatData = new Uint8Array(150 * 1024 * 1024);
+
+ // FIX: Removed 'let' from bytesRead here to fix the "already been declared" SyntaxError
+ bytesRead = 0;
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
- // HIDE ERROR: Allocate a massive flat placeholder array size (e.g. 150MB)
- // This gives the compressed gzip stream chunks plenty of room to expand
- // so it never overflows or throws an out-of-bounds error banner.
- let splatData = new Uint8Array(150 * 1024 * 1024);
-
- let bytesRead = 0;
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
-
- // Put the incoming data chunk directly into our oversized array
- splatData.set(value, bytesRead);
- bytesRead += value.length;
- }
+ // Put the incoming data chunk directly into our oversized array
+ splatData.set(value, bytesRead);
+ bytesRead += value.length;
+ }
// Shrink the array down to exactly how many bytes were actually read
// so WebGL doesn't try to render empty space
splatData = splatData.subarray(0, bytesRead);
-
-
const downsample =
splatData.length / rowLength > 500000 ? 1 : 1 / devicePixelRatio;
console.log(splatData.length / rowLength, downsample);
From 1f863ac61a9889abd33b66e89356c3b94cda46be Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 16:09:22 +1000
Subject: [PATCH 11/18] Update main.js
---
main.js | 26 +-------------------------
1 file changed, 1 insertion(+), 25 deletions(-)
diff --git a/main.js b/main.js
index 4e83612..077a877 100644
--- a/main.js
+++ b/main.js
@@ -1,5 +1,3 @@
-
-
let cameras = [
{
id: 0,
@@ -729,7 +727,6 @@ let defaultViewMatrix = [
0.03, 6.55, 1,
];
let viewMatrix = defaultViewMatrix;
-
async function main() {
let carousel = true;
const params = new URLSearchParams(location.search);
@@ -737,14 +734,12 @@ async function main() {
viewMatrix = JSON.parse(decodeURIComponent(location.hash.slice(1)));
carousel = false;
} catch (err) {}
-
const url = new URL(
// "nike.splat",
// location.href,
params.get("url") || "train.splat",
"https://huggingface.co/cakewalk/splat-data/resolve/main/",
);
-
const req = await fetch(url, {
mode: "cors", // no-cors, *cors, same-origin
credentials: "omit", // include, *same-origin, omit
@@ -755,26 +750,7 @@ async function main() {
const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
const reader = req.body.getReader();
-
- // HIDE ERROR: Allocate a massive flat placeholder array size (e.g. 150MB)
- // This gives the compressed gzip stream chunks plenty of room to expand
- // so it never overflows or throws an out-of-bounds error banner.
- let splatData = new Uint8Array(150 * 1024 * 1024);
-
- // FIX: Removed 'let' from bytesRead here to fix the "already been declared" SyntaxError
- bytesRead = 0;
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
-
- // Put the incoming data chunk directly into our oversized array
- splatData.set(value, bytesRead);
- bytesRead += value.length;
- }
-
- // Shrink the array down to exactly how many bytes were actually read
- // so WebGL doesn't try to render empty space
- splatData = splatData.subarray(0, bytesRead);
+ let splatData = new Uint8Array(req.headers.get("content-length"));
const downsample =
splatData.length / rowLength > 500000 ? 1 : 1 / devicePixelRatio;
From 21c329de14cb8a87e793254c8948852075c59e5d Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 16:14:35 +1000
Subject: [PATCH 12/18] Update main.js
---
main.js | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/main.js b/main.js
index 077a877..7de77d8 100644
--- a/main.js
+++ b/main.js
@@ -1,3 +1,5 @@
+
+
let cameras = [
{
id: 0,
@@ -750,7 +752,28 @@ async function main() {
const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
const reader = req.body.getReader();
- let splatData = new Uint8Array(req.headers.get("content-length"));
+
+ // Fallback if content-length is missing or altered by Gzip compression
+ let contentLength = parseInt(req.headers.get("content-length"));
+ let splatData = new Uint8Array(contentLength || 0);
+
+ // Stream the data chunks smoothly into a dynamic array builder
+ let bytesRead = 0;
+ let chunks = [];
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ chunks.push(value);
+ bytesRead += value.length;
+ }
+
+ // Combine all chunks into the final array accurately matching unpacked size
+ splatData = new Uint8Array(bytesRead);
+ let offset = 0;
+ for (let chunk of chunks) {
+ splatData.set(chunk, offset);
+ offset += chunk.length;
+ }
const downsample =
splatData.length / rowLength > 500000 ? 1 : 1 / devicePixelRatio;
From 3bb73449a88c4e15bed6d2889e1c71698187b8f8 Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Wed, 10 Jun 2026 16:25:25 +1000
Subject: [PATCH 13/18] Update main.js
---
main.js | 25 +------------------------
1 file changed, 1 insertion(+), 24 deletions(-)
diff --git a/main.js b/main.js
index 7de77d8..077a877 100644
--- a/main.js
+++ b/main.js
@@ -1,5 +1,3 @@
-
-
let cameras = [
{
id: 0,
@@ -752,28 +750,7 @@ async function main() {
const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
const reader = req.body.getReader();
-
- // Fallback if content-length is missing or altered by Gzip compression
- let contentLength = parseInt(req.headers.get("content-length"));
- let splatData = new Uint8Array(contentLength || 0);
-
- // Stream the data chunks smoothly into a dynamic array builder
- let bytesRead = 0;
- let chunks = [];
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- chunks.push(value);
- bytesRead += value.length;
- }
-
- // Combine all chunks into the final array accurately matching unpacked size
- splatData = new Uint8Array(bytesRead);
- let offset = 0;
- for (let chunk of chunks) {
- splatData.set(chunk, offset);
- offset += chunk.length;
- }
+ let splatData = new Uint8Array(req.headers.get("content-length"));
const downsample =
splatData.length / rowLength > 500000 ? 1 : 1 / devicePixelRatio;
From 5fc713121f9f7553e68d7ff6a95b1b6c3ffc573f Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 18 Jun 2026 21:20:11 +1000
Subject: [PATCH 14/18] Update README.md
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f9b391f..62b7cc0 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,9 @@ https://github.com/antimatter15/splat/assets/30054/878d5d34-e0a7-4336-85df-111ff
-## controls
+## controls
+This was the main feature of this fork, just game-like controls.
movement (arrow keys)
- left/right arrow keys to strafe side to side
From 4bb32b605c3b43ed1d21e4afb4119e8668cb9219 Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 9 Jul 2026 21:30:58 +1000
Subject: [PATCH 15/18] Implement roll lock for camera in touch controls
Added roll lock functionality to prevent camera rolling during touch interactions.
---
main.js | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/main.js b/main.js
index 077a877..1213e68 100644
--- a/main.js
+++ b/main.js
@@ -1083,20 +1083,31 @@ async function main() {
let inv = invert4(viewMatrix);
let dx = (4 * (e.touches[0].clientX - startX)) / innerWidth;
let dy = (4 * (e.touches[0].clientY - startY)) / innerHeight;
-
let d = 4;
inv = translate4(inv, 0, 0, d);
- // inv = translate4(inv, -x, -y, -z);
- // inv = translate4(inv, x, y, z);
+ // inv = translate4(inv, -x, -y, -z);
+ // inv = translate4(inv, x, y, z);
inv = rotate4(inv, dx, 0, 1, 0);
inv = rotate4(inv, -dy, 1, 0, 0);
inv = translate4(inv, 0, 0, -d);
viewMatrix = invert4(inv);
+ // === ADD THIS ROLL LOCK (prevents camera rolling) ===
+ inv = invert4(viewMatrix);
+ inv[2] = 0; // forward X
+ inv[6] = 0; // forward Y
+ inv[10] = 1; // forward Z
+ inv[1] = 0; // up X
+ inv[5] = 1; // up Y
+ inv[9] = 0; // up Z
+ viewMatrix = invert4(inv);
+ // ===================================================
+
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
- } else if (e.touches.length === 2) {
+ }
+ else if (e.touches.length === 2) {
// alert('beep')
const dtheta =
Math.atan2(startY - altY, startX - altX) -
From 3c9242adabc375d09022ba249addae086aa43e37 Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 9 Jul 2026 21:33:49 +1000
Subject: [PATCH 16/18] Implement camera roll lock to maintain level view
Added logic to keep the camera level by recalculating the right and up vectors based on the forward direction.
---
main.js | 39 +++++++++++++++++++++++++++++++--------
1 file changed, 31 insertions(+), 8 deletions(-)
diff --git a/main.js b/main.js
index 1213e68..eaaa733 100644
--- a/main.js
+++ b/main.js
@@ -1093,15 +1093,38 @@ async function main() {
viewMatrix = invert4(inv);
- // === ADD THIS ROLL LOCK (prevents camera rolling) ===
- inv = invert4(viewMatrix);
- inv[2] = 0; // forward X
- inv[6] = 0; // forward Y
- inv[10] = 1; // forward Z
- inv[1] = 0; // up X
- inv[5] = 1; // up Y
- inv[9] = 0; // up Z
+
+ // Force the camera to stay level
+ let forward = [inv[2], inv[6], inv[10]];
+ let up = [0, 1, 0]; // world up
+
+ // Rebuild right vector
+ let right = [
+ forward[1] * up[2] - forward[2] * up[1],
+ forward[2] * up[0] - forward[0] * up[2],
+ forward[0] * up[1] - forward[1] * up[0]
+ ];
+
+ // Normalize
+ let len = Math.hypot(right[0], right[1], right[2]);
+ right[0] /= len;
+ right[1] /= len;
+ right[2] /= len;
+
+ // Rebuild up vector properly
+ up = [
+ right[2] * forward[1] - right[1] * forward[2],
+ right[0] * forward[2] - right[2] * forward[0],
+ right[1] * forward[0] - right[0] * forward[1]
+ ];
+
+ // Write back to matrix
+ inv[0] = right[0]; inv[4] = right[1]; inv[8] = right[2];
+ inv[1] = up[0]; inv[5] = up[1]; inv[9] = up[2];
+ inv[2] = forward[0]; inv[6] = forward[1]; inv[10] = forward[2];
+
viewMatrix = invert4(inv);
+ // =============================
// ===================================================
startX = e.touches[0].clientX;
From b85a501fa7be0e17d4c356504674915dd23cb829 Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 9 Jul 2026 21:38:29 +1000
Subject: [PATCH 17/18] Update main.js
---
main.js | 56 +++++++++++++++++++++++---------------------------------
1 file changed, 23 insertions(+), 33 deletions(-)
diff --git a/main.js b/main.js
index eaaa733..7b34114 100644
--- a/main.js
+++ b/main.js
@@ -1080,56 +1080,46 @@ async function main() {
(e) => {
e.preventDefault();
if (e.touches.length === 1 && down) {
- let inv = invert4(viewMatrix);
let dx = (4 * (e.touches[0].clientX - startX)) / innerWidth;
let dy = (4 * (e.touches[0].clientY - startY)) / innerHeight;
+
+ let inv = invert4(viewMatrix);
+
+ // Simpler orbit with better roll control
let d = 4;
inv = translate4(inv, 0, 0, d);
- // inv = translate4(inv, -x, -y, -z);
- // inv = translate4(inv, x, y, z);
- inv = rotate4(inv, dx, 0, 1, 0);
- inv = rotate4(inv, -dy, 1, 0, 0);
+
+ inv = rotate4(inv, dx, 0, 1, 0); // Yaw
+ inv = rotate4(inv, -dy, 1, 0, 0); // Pitch
+
inv = translate4(inv, 0, 0, -d);
viewMatrix = invert4(inv);
+ // Strong roll correction
+ inv = invert4(viewMatrix);
+ const forward = [inv[2], inv[6], inv[10]];
+ const right = [
+ forward[1],
+ -forward[0],
+ 0
+ ].map((v, i, a) => v / Math.hypot(a[0], a[1]));
- // Force the camera to stay level
- let forward = [inv[2], inv[6], inv[10]];
- let up = [0, 1, 0]; // world up
-
- // Rebuild right vector
- let right = [
- forward[1] * up[2] - forward[2] * up[1],
- forward[2] * up[0] - forward[0] * up[2],
- forward[0] * up[1] - forward[1] * up[0]
+ const up = [
+ right[1] * forward[2] - right[2] * forward[1],
+ right[2] * forward[0] - right[0] * forward[2],
+ right[0] * forward[1] - right[1] * forward[0]
];
-
- // Normalize
- let len = Math.hypot(right[0], right[1], right[2]);
- right[0] /= len;
- right[1] /= len;
- right[2] /= len;
-
- // Rebuild up vector properly
- up = [
- right[2] * forward[1] - right[1] * forward[2],
- right[0] * forward[2] - right[2] * forward[0],
- right[1] * forward[0] - right[0] * forward[1]
- ];
-
- // Write back to matrix
+
inv[0] = right[0]; inv[4] = right[1]; inv[8] = right[2];
inv[1] = up[0]; inv[5] = up[1]; inv[9] = up[2];
inv[2] = forward[0]; inv[6] = forward[1]; inv[10] = forward[2];
-
+
viewMatrix = invert4(inv);
- // =============================
- // ===================================================
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
- }
+ }
else if (e.touches.length === 2) {
// alert('beep')
const dtheta =
From de8c69ea81af1d42434ac35012b1bc93d8f1924d Mon Sep 17 00:00:00 2001
From: Jeremy Jiao <144148@caulfieldgs.vic.edu.au>
Date: Thu, 9 Jul 2026 21:57:05 +1000
Subject: [PATCH 18/18] Updated WASD controls
---
main.js | 36 ++++++------------------------------
1 file changed, 6 insertions(+), 30 deletions(-)
diff --git a/main.js b/main.js
index 7b34114..077a877 100644
--- a/main.js
+++ b/main.js
@@ -1080,47 +1080,23 @@ async function main() {
(e) => {
e.preventDefault();
if (e.touches.length === 1 && down) {
+ let inv = invert4(viewMatrix);
let dx = (4 * (e.touches[0].clientX - startX)) / innerWidth;
let dy = (4 * (e.touches[0].clientY - startY)) / innerHeight;
- let inv = invert4(viewMatrix);
-
- // Simpler orbit with better roll control
let d = 4;
inv = translate4(inv, 0, 0, d);
-
- inv = rotate4(inv, dx, 0, 1, 0); // Yaw
- inv = rotate4(inv, -dy, 1, 0, 0); // Pitch
-
+ // inv = translate4(inv, -x, -y, -z);
+ // inv = translate4(inv, x, y, z);
+ inv = rotate4(inv, dx, 0, 1, 0);
+ inv = rotate4(inv, -dy, 1, 0, 0);
inv = translate4(inv, 0, 0, -d);
viewMatrix = invert4(inv);
- // Strong roll correction
- inv = invert4(viewMatrix);
- const forward = [inv[2], inv[6], inv[10]];
- const right = [
- forward[1],
- -forward[0],
- 0
- ].map((v, i, a) => v / Math.hypot(a[0], a[1]));
-
- const up = [
- right[1] * forward[2] - right[2] * forward[1],
- right[2] * forward[0] - right[0] * forward[2],
- right[0] * forward[1] - right[1] * forward[0]
- ];
-
- inv[0] = right[0]; inv[4] = right[1]; inv[8] = right[2];
- inv[1] = up[0]; inv[5] = up[1]; inv[9] = up[2];
- inv[2] = forward[0]; inv[6] = forward[1]; inv[10] = forward[2];
-
- viewMatrix = invert4(inv);
-
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
- }
- else if (e.touches.length === 2) {
+ } else if (e.touches.length === 2) {
// alert('beep')
const dtheta =
Math.atan2(startY - altY, startX - altX) -