Naloga 3 WIP

This commit is contained in:
Gašper Dobrovoljc
2024-12-28 19:58:17 +01:00
parent 7ad330422b
commit a20a45ebd0
51 changed files with 3327 additions and 28 deletions

View File

@@ -0,0 +1,131 @@
import { quat, vec3, mat4 } from 'glm';
import { Transform } from '../core/Transform.js';
export class FirstPersonController {
constructor(node, domElement, {
pitch = 0,
yaw = 0,
velocity = [0, 0, 0],
acceleration = 50,
maxSpeed = 5,
decay = 0.99999,
pointerSensitivity = 0.002,
} = {}) {
this.node = node;
this.domElement = domElement;
this.keys = {};
this.pitch = pitch;
this.yaw = yaw;
this.velocity = velocity;
this.acceleration = acceleration;
this.maxSpeed = maxSpeed;
this.decay = decay;
this.pointerSensitivity = pointerSensitivity;
this.initHandlers();
}
initHandlers() {
this.pointermoveHandler = this.pointermoveHandler.bind(this);
this.keydownHandler = this.keydownHandler.bind(this);
this.keyupHandler = this.keyupHandler.bind(this);
const element = this.domElement;
const doc = element.ownerDocument;
doc.addEventListener('keydown', this.keydownHandler);
doc.addEventListener('keyup', this.keyupHandler);
element.addEventListener('click', e => element.requestPointerLock());
doc.addEventListener('pointerlockchange', e => {
if (doc.pointerLockElement === element) {
doc.addEventListener('pointermove', this.pointermoveHandler);
} else {
doc.removeEventListener('pointermove', this.pointermoveHandler);
}
});
}
update(t, dt) {
// Calculate forward and right vectors.
const cos = Math.cos(this.yaw);
const sin = Math.sin(this.yaw);
const forward = [-sin, 0, -cos];
const right = [cos, 0, -sin];
// Map user input to the acceleration vector.
const acc = vec3.create();
if (this.keys['KeyW']) {
vec3.add(acc, acc, forward);
}
if (this.keys['KeyS']) {
vec3.sub(acc, acc, forward);
}
if (this.keys['KeyD']) {
vec3.add(acc, acc, right);
}
if (this.keys['KeyA']) {
vec3.sub(acc, acc, right);
}
// Update velocity based on acceleration.
vec3.scaleAndAdd(this.velocity, this.velocity, acc, dt * this.acceleration);
// If there is no user input, apply decay.
if (!this.keys['KeyW'] &&
!this.keys['KeyS'] &&
!this.keys['KeyD'] &&
!this.keys['KeyA'])
{
const decay = Math.exp(dt * Math.log(1 - this.decay));
vec3.scale(this.velocity, this.velocity, decay);
}
// Limit speed to prevent accelerating to infinity and beyond.
const speed = vec3.length(this.velocity);
if (speed > this.maxSpeed) {
vec3.scale(this.velocity, this.velocity, this.maxSpeed / speed);
}
const transform = this.node.getComponentOfType(Transform);
if (transform) {
// Update translation based on velocity.
vec3.scaleAndAdd(transform.translation,
transform.translation, this.velocity, dt);
// Update rotation based on the Euler angles.
const rotation = quat.create();
quat.rotateY(rotation, rotation, this.yaw);
quat.rotateX(rotation, rotation, this.pitch);
transform.rotation = rotation;
}
}
pointermoveHandler(e) {
const dx = e.movementX;
const dy = e.movementY;
this.pitch -= dy * this.pointerSensitivity;
this.yaw -= dx * this.pointerSensitivity;
const twopi = Math.PI * 2;
const halfpi = Math.PI / 2;
this.pitch = Math.min(Math.max(this.pitch, -halfpi), halfpi);
this.yaw = ((this.yaw % twopi) + twopi) % twopi;
}
keydownHandler(e) {
this.keys[e.code] = true;
}
keyupHandler(e) {
this.keys[e.code] = false;
}
}

View File

@@ -0,0 +1,74 @@
import { quat, vec3 } from 'glm';
import { Transform } from '../core/Transform.js';
export class OrbitController {
constructor(node, domElement, {
rotation = [0, 0, 0, 1],
distance = 2,
moveSensitivity = 0.004,
zoomSensitivity = 0.002,
} = {}) {
this.node = node;
this.domElement = domElement;
this.rotation = rotation;
this.distance = distance;
this.moveSensitivity = moveSensitivity;
this.zoomSensitivity = zoomSensitivity;
this.initHandlers();
}
initHandlers() {
this.pointerdownHandler = this.pointerdownHandler.bind(this);
this.pointerupHandler = this.pointerupHandler.bind(this);
this.pointermoveHandler = this.pointermoveHandler.bind(this);
this.wheelHandler = this.wheelHandler.bind(this);
this.domElement.addEventListener('pointerdown', this.pointerdownHandler);
this.domElement.addEventListener('wheel', this.wheelHandler);
}
pointerdownHandler(e) {
this.domElement.setPointerCapture(e.pointerId);
this.domElement.requestPointerLock();
this.domElement.removeEventListener('pointerdown', this.pointerdownHandler);
this.domElement.addEventListener('pointerup', this.pointerupHandler);
this.domElement.addEventListener('pointermove', this.pointermoveHandler);
}
pointerupHandler(e) {
this.domElement.releasePointerCapture(e.pointerId);
this.domElement.ownerDocument.exitPointerLock();
this.domElement.addEventListener('pointerdown', this.pointerdownHandler);
this.domElement.removeEventListener('pointerup', this.pointerupHandler);
this.domElement.removeEventListener('pointermove', this.pointermoveHandler);
}
pointermoveHandler(e) {
const dx = e.movementX;
const dy = e.movementY;
quat.rotateX(this.rotation, this.rotation, -dy * this.moveSensitivity);
quat.rotateY(this.rotation, this.rotation, -dx * this.moveSensitivity);
quat.normalize(this.rotation, this.rotation);
}
wheelHandler(e) {
this.distance *= Math.exp(this.zoomSensitivity * e.deltaY);
}
update() {
const transform = this.node.getComponentOfType(Transform);
if (!transform) {
return;
}
quat.copy(transform.rotation, this.rotation);
vec3.transformQuat(transform.translation, [0, 0, this.distance], this.rotation);
}
}

View File

@@ -0,0 +1,143 @@
import { quat, vec2, vec3 } from 'glm';
import { Transform } from 'engine/core/Transform.js';
export class TouchController {
constructor(node, domElement, {
translation = [0, 0, 0],
rotation = [0, 0, 0, 1],
distance = 2,
translateSensitivity = 0.001,
rotateSensitivity = 0.004,
wheelSensitivity = 0.002,
} = {}) {
this.node = node;
this.domElement = domElement;
this.translation = translation;
this.rotation = rotation;
this.distance = distance;
this.translateSensitivity = translateSensitivity;
this.rotateSensitivity = rotateSensitivity;
this.wheelSensitivity = wheelSensitivity;
this.pointers = new Map();
this.initHandlers();
}
initHandlers() {
this.pointerdownHandler = this.pointerdownHandler.bind(this);
this.pointerupHandler = this.pointerupHandler.bind(this);
this.pointermoveHandler = this.pointermoveHandler.bind(this);
this.wheelHandler = this.wheelHandler.bind(this);
this.domElement.addEventListener('pointerdown', this.pointerdownHandler);
this.domElement.addEventListener('pointerup', this.pointerupHandler);
this.domElement.addEventListener('pointercancel', this.pointerupHandler);
this.domElement.addEventListener('pointermove', this.pointermoveHandler);
this.domElement.addEventListener('wheel', this.wheelHandler);
}
pointerdownHandler(e) {
this.pointers.set(e.pointerId, e);
this.domElement.setPointerCapture(e.pointerId);
this.domElement.requestPointerLock();
}
pointerupHandler(e) {
this.pointers.delete(e.pointerId);
this.domElement.releasePointerCapture(e.pointerId);
this.domElement.ownerDocument.exitPointerLock();
}
pointermoveHandler(e) {
if (!this.pointers.has(e.pointerId)) {
return;
}
if (this.pointers.size === 1) {
if (e.shiftKey) {
this.translate(e.movementX, e.movementY);
} else {
this.rotate(e.movementX, e.movementY);
}
this.pointers.set(e.pointerId, e);
} else {
const N = this.pointers.size;
// Points before movement
const A = [...this.pointers.values()].map(e => [e.clientX, e.clientY]);
this.pointers.set(e.pointerId, e);
// Points after movement
const B = [...this.pointers.values()].map(e => [e.clientX, e.clientY]);
const centroidA = A.reduce((a, v) => vec2.scaleAndAdd(a, a, v, 1 / N), [0, 0]);
const centroidB = B.reduce((a, v) => vec2.scaleAndAdd(a, a, v, 1 / N), [0, 0]);
const translation = vec2.subtract(vec2.create(), centroidB, centroidA);
const centeredA = A.map(v => vec2.subtract(vec2.create(), v, centroidA));
const centeredB = B.map(v => vec2.subtract(vec2.create(), v, centroidB));
const scaleA = centeredA.reduce((a, v) => a + vec2.length(v) / N, 0);
const scaleB = centeredB.reduce((a, v) => a + vec2.length(v) / N, 0);
const scale = scaleA / scaleB;
const normalizedA = centeredA.map(v => vec2.normalize(vec2.create(), v));
const normalizedB = centeredB.map(v => vec2.normalize(vec2.create(), v));
let sin = 0;
for (let i = 0; i < A.length; i++) {
const a = normalizedA[i];
const b = normalizedB[i];
sin += (a[0] * b[1] - a[1] * b[0]) / N;
}
const angle = Math.asin(sin);
this.translate(...translation);
this.scale(scale);
this.screw(angle);
}
}
wheelHandler(e) {
this.scale(Math.exp(e.deltaY * this.wheelSensitivity));
}
screw(amount) {
quat.rotateZ(this.rotation, this.rotation, amount);
}
rotate(dx, dy) {
quat.rotateX(this.rotation, this.rotation, -dy * this.rotateSensitivity);
quat.rotateY(this.rotation, this.rotation, -dx * this.rotateSensitivity);
quat.normalize(this.rotation, this.rotation);
}
translate(dx, dy) {
const translation = [-dx, dy, 0];
vec3.transformQuat(translation, translation, this.rotation);
vec3.scaleAndAdd(this.translation, this.translation, translation, this.distance * this.translateSensitivity);
}
scale(amount) {
this.distance *= amount;
}
update() {
const transform = this.node.getComponentOfType(Transform);
if (!transform) {
return;
}
quat.copy(transform.rotation, this.rotation);
vec3.transformQuat(transform.translation, [0, 0, this.distance], this.rotation);
vec3.add(transform.translation, transform.translation, this.translation);
}
}

View File

@@ -0,0 +1,88 @@
import { quat, vec3 } from 'glm';
import { Transform } from '../core/Transform.js';
export class TurntableController {
constructor(node, domElement, {
pitch = 0,
yaw = 0,
distance = 1,
moveSensitivity = 0.004,
zoomSensitivity = 0.002,
} = {}) {
this.node = node;
this.domElement = domElement;
this.pitch = pitch;
this.yaw = yaw;
this.distance = distance;
this.moveSensitivity = moveSensitivity;
this.zoomSensitivity = zoomSensitivity;
this.initHandlers();
}
initHandlers() {
this.pointerdownHandler = this.pointerdownHandler.bind(this);
this.pointerupHandler = this.pointerupHandler.bind(this);
this.pointermoveHandler = this.pointermoveHandler.bind(this);
this.wheelHandler = this.wheelHandler.bind(this);
this.domElement.addEventListener('pointerdown', this.pointerdownHandler);
this.domElement.addEventListener('wheel', this.wheelHandler);
}
pointerdownHandler(e) {
this.domElement.setPointerCapture(e.pointerId);
this.domElement.requestPointerLock();
this.domElement.removeEventListener('pointerdown', this.pointerdownHandler);
this.domElement.addEventListener('pointerup', this.pointerupHandler);
this.domElement.addEventListener('pointermove', this.pointermoveHandler);
}
pointerupHandler(e) {
this.domElement.releasePointerCapture(e.pointerId);
this.domElement.ownerDocument.exitPointerLock();
this.domElement.addEventListener('pointerdown', this.pointerdownHandler);
this.domElement.removeEventListener('pointerup', this.pointerupHandler);
this.domElement.removeEventListener('pointermove', this.pointermoveHandler);
}
pointermoveHandler(e) {
const dx = e.movementX;
const dy = e.movementY;
this.pitch -= dy * this.moveSensitivity;
this.yaw -= dx * this.moveSensitivity;
const twopi = Math.PI * 2;
const halfpi = Math.PI / 2;
this.pitch = Math.min(Math.max(this.pitch, -halfpi), halfpi);
this.yaw = ((this.yaw % twopi) + twopi) % twopi;
}
wheelHandler(e) {
this.distance *= Math.exp(this.zoomSensitivity * e.deltaY);
}
update() {
const transform = this.node.getComponentOfType(Transform);
if (!transform) {
return;
}
const rotation = quat.create();
quat.rotateY(rotation, rotation, this.yaw);
quat.rotateX(rotation, rotation, this.pitch);
transform.rotation = rotation;
const translation = [0, 0, this.distance];
vec3.rotateX(translation, translation, [0, 0, 0], this.pitch);
vec3.rotateY(translation, translation, [0, 0, 0], this.yaw);
transform.translation = translation;
}
}