Naloga 3 WIP
This commit is contained in:
131
naloga_3/engine/controllers/FirstPersonController.js
Normal file
131
naloga_3/engine/controllers/FirstPersonController.js
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
74
naloga_3/engine/controllers/OrbitController.js
Normal file
74
naloga_3/engine/controllers/OrbitController.js
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
143
naloga_3/engine/controllers/TouchController.js
Normal file
143
naloga_3/engine/controllers/TouchController.js
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
88
naloga_3/engine/controllers/TurntableController.js
Normal file
88
naloga_3/engine/controllers/TurntableController.js
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user