Summary Files Commits Branches @commit @diff @stats
path: root / Year 2 / Computer Graphics with WebGL / Project / camera-movement.js
diff options
context:
space:
mode:
Commit53c352887fdb3e0345e97c2760d8f7985a1bfc48 (patch)
Author
 Syndamia <kamen@syndamia.com>
2023-08-28 13:01:49 +0300
Committer
 Syndamia <kamen@syndamia.com>
2023-08-28 13:01:49 +0300
Files4ca4700916a35a216a96ac7c347a5ad1b33a4ef2 /Year 2/Computer Graphics with WebGL/Project/camera-movement.js
uni-projects-53c352887fdb3e0345e97c2760d8f7985a1bfc48.tar
uni-projects-53c352887fdb3e0345e97c2760d8f7985a1bfc48.tar.gz
uni-projects-53c352887fdb3e0345e97c2760d8f7985a1bfc48.zip
Parent8861a3e72f28f1757954c7f20ebeb469980f573a (diff)
[WGL/P] Added a lot of comments and reworked formatting of certain code
1 files changed, 164 insertions, 0 deletions
-rw-r--r--Year 2/Computer Graphics with WebGL/Project/camera-movement.js164
diff --git a/Year 2/Computer Graphics with WebGL/Project/camera-movement.js b/Year 2/Computer Graphics with WebGL/Project/camera-movement.js
new file mode 100644
index 0000000..f06db00
--- /dev/null
+++ b/Year 2/Computer Graphics with WebGL/Project/camera-movement.js
@@ -0,0 +1,164 @@
+/* Logic for moving around with mouse and keyboard */
+
+/* Helpers */
+
+// Since with the mouse cursor we change the looking direction vertically or horizontally
+// it's significantly simpler to just use spherical coordinates and change the angles upon
+// mouse movement.
+// Since these are looking directions, the sphere radius is one (meaning the spherical coordinates
+// will always be converted into a cartesian unit vector).
+// The downside is that we have additional calculations to make, but it's worth it.
+SphericalCoord = function(theta, phi) {
+ this.fulltheta = this.theta = theta;
+ this.phi = phi;
+ this.recalculateCartesian();
+}
+SphericalCoord.prototype.recalculateCartesian = function() {
+ return this.cartesian = [Math.sin(this.theta) * Math.sin(this.phi),
+ Math.cos(this.theta),
+ Math.sin(this.theta) * Math.cos(this.phi)];
+}
+SphericalCoord.prototype.addAngles = function(theta, phi) {
+ // I figured this out on my own :)
+ // Theta is in the range [0, pi), however in a sphere we have 2pi degrees.
+ // We keep a "full" theta, which is in the range [0, 2pi), and then adjust normal theta
+ // accordingly. With just normal theta, after a positive value is passed (for example)
+ // we wouldn't know in which direction to rotate (or more accurately, we would always
+ // increase theta so it goes the the "south" pole, which is bad, since increasing the
+ // value should just continue rotating the point, like with phi).
+ // When you "overextend" past pi, so when you need to reach the other hemisphere, between
+ // pi and 2pi, you need to rotate phi by 180 degrees (2pi), so theta is acting on the other
+ // hemisphere, and then we need to remove from 2pi the "leftover" amount, since our theta
+ // would signify the "reverse" angle than the one we want.
+
+ var before = this.fulltheta <= Math.PI;
+ this.fulltheta = (this.fulltheta + theta) % (2 * Math.PI);
+ this.theta = (this.fulltheta <= Math.PI) ? this.fulltheta : -this.fulltheta;
+
+ // We check if fulltheta passed through the 180 degree/pi border
+ if (before && this.fulltheta > Math.PI) {
+ this.phi = (this.phi + Math.PI) % (2 * Math.PI);
+ }
+ else if (!before && this.fulltheta <= Math.PI) {
+ this.phi = (this.phi - Math.PI) % (2 * Math.PI);
+ }
+
+ this.phi = (this.phi + phi) % (2 * Math.PI);
+
+ this.recalculateCartesian();
+ return this;
+}
+SphericalCoord.prototype.toCartesian = function() {
+ return this.cartesian;
+}
+
+function vecSum(a, b) {
+ return [a[0]+b[0], a[1]+b[1], a[2]+b[2]];
+}
+function vecSub(a, b) {
+ return [a[0]-b[0], a[1]-b[1], a[2]-b[2]];
+}
+
+/* Variables */
+
+const MOVE_SPEED = 1.5;
+const ROTATION_SPEED = 3;
+
+var pos = [1000, 0, 650];
+
+var forward = new SphericalCoord(Math.PI / 2, 0);
+var up = new SphericalCoord(0, 0);
+ // this vector can be calculated from forward and up, but this way we improve speed
+var left = new SphericalCoord(Math.PI / 2, Math.PI / 2);
+
+var isPaused = true;
+
+/* Navigation keys */
+
+// Since I'm lazy, there is a different function, changing pos, for every key
+// so when a key is pressed, we'll execute the action on the same index
+var pressedKeys = [ false, false, false, false, false, false ];
+var keyAction = [
+ () => { pos = vecSum(pos, up.toCartesian()); }, // ' '
+ () => { pos = vecSub(pos, up.toCartesian()); }, // 'Shift'
+ () => { pos = vecSum(pos, forward.toCartesian()); }, // 'w'
+ () => { pos = vecSub(pos, forward.toCartesian()); }, // 's'
+ () => { pos = vecSum(pos, left.toCartesian()); }, // 'a'
+ () => { pos = vecSub(pos, left.toCartesian()); }, // 'd'
+];
+document.addEventListener('keydown', (event) => {
+ if (isPaused) return;
+
+ switch (event.key) {
+ case ' ' : pressedKeys[0] = true; break;
+ case 'Shift': pressedKeys[1] = true; break;
+ case 'w' : pressedKeys[2] = true; break;
+ case 's' : pressedKeys[3] = true; break;
+ case 'a' : pressedKeys[4] = true; break;
+ case 'd' : pressedKeys[5] = true; break;
+ }
+});
+document.addEventListener('keyup', (event) => {
+ if (isPaused) return;
+
+ switch (event.key) {
+ case ' ' : pressedKeys[0] = false; break;
+ case 'Shift': pressedKeys[1] = false; break;
+ case 'w' : pressedKeys[2] = false; break;
+ case 's' : pressedKeys[3] = false; break;
+ case 'a' : pressedKeys[4] = false; break;
+ case 'd' : pressedKeys[5] = false; break;
+ }
+});
+
+/* Mouse movement */
+
+var onlyHorizontal = false;
+var onlyVertical = false;
+function updateLookingDirection(e) {
+ if (isPaused) return;
+
+ // Get mouse movement
+ var vertical = (1-onlyHorizontal) * (e.movementY / canvas.offsetHeight) * ROTATION_SPEED;
+ var horizontal = (1-onlyVertical) * (-e.movementX / canvas.offsetWidth) * ROTATION_SPEED;
+
+ forward.addAngles(vertical, horizontal);
+ up.addAngles(vertical, horizontal);
+ left.addAngles(vertical, horizontal);
+}
+document.addEventListener('mousemove', updateLookingDirection);
+document.addEventListener('pointerlockchange', () => {
+ isPaused = document.pointerLockElement !== canvas;
+ menu.classList.toggle('hide');
+});
+
+function mouseInit() {
+ // Capture mouse pointer when appropriate
+ paused = document.getElementById('paused');
+
+ canvas.addEventListener('click', async () => {
+ if (isPaused)
+ await canvas.requestPointerLock();
+ });
+ paused.addEventListener('click', async () => {
+ if (isPaused)
+ await canvas.requestPointerLock();
+ });
+ document.addEventListener('keyup', (e) => {
+ if (e.key === 'Escape') paused.click();
+ });
+}
+
+/* On every drawFrame */
+coordsElem = undefined;
+
+function updateCamera() {
+ // Move camera position
+ for (var i = 0; i < pressedKeys.length; i++)
+ if (pressedKeys[i]) keyAction[i]();
+ lookAt(pos, vecSum(pos, forward.toCartesian()), up.toCartesian());
+
+ // Update "external" variables
+ if (coordsElem) coordsElem.innerText = 'X: '+pos[0]+' Y: '+pos[1]+' Z: '+pos[2];
+ gl.uniform3fv(uEye, pos);
+}