3dexperiments/src/engine/camera.js

127 lines
3.6 KiB
JavaScript

import { Node } from './components'
import { glMatrix, mat4, vec2, vec3 } from 'gl-matrix'
const SPEED = 10.0
const SENSITIVTY = 100.0
const FOV = 45.0
const ZNEAR = 0.1
const ZFAR = 1000.0
class Camera extends Node {
constructor (pos, rotation) {
super(pos, rotation)
this.fov = FOV
this.speed = SPEED
this.sensitivity = SENSITIVTY
// Create an empty projection matrix
this.projection = mat4.create()
// Helping vectors for calculating the view matrix
this.up = vec3.create()
this.front = vec3.fromValues(0.0, 0.0, -1.0)
this.right = vec3.create()
this.worldUp = vec3.fromValues(0.0, 1.0, 0.0)
this.updateTransform()
}
processKeyboard (direction, delta) {
let newSpeed = this.speed * delta
let velocity = vec3.fromValues(newSpeed, newSpeed, newSpeed)
let vec = vec3.create()
if (direction === 0) {
vec3.multiply(vec, this.front, velocity)
vec3.add(this.pos, this.pos, vec)
}
if (direction === 1) {
vec3.multiply(vec, this.front, velocity)
vec3.sub(this.pos, this.pos, vec)
}
if (direction === 2) {
vec3.multiply(vec, this.right, velocity)
vec3.sub(this.pos, this.pos, vec)
}
if (direction === 3) {
vec3.multiply(vec, this.right, velocity)
vec3.add(this.pos, this.pos, vec)
}
}
processMouseMove (offset, constrain = true) {
let fst = vec2.fromValues(offset.x * this.sensitivity, offset.y * this.sensitivity)
this.rotation[0] += glMatrix.toRadian(fst[0])
this.rotation[1] += glMatrix.toRadian(fst[1])
// Make sure that when pitch is out of bounds, screen doesn't get flipped
if (constrain) {
if (this.rotation[1] > glMatrix.toRadian(89.0)) {
this.rotation[1] = glMatrix.toRadian(89.0)
}
if (this.rotation[1] < -glMatrix.toRadian(89.0)) {
this.rotation[1] = -glMatrix.toRadian(89.0)
}
}
this.updateTransform()
}
// Calculate the vertices required for the view matrix
updateTransform () {
// Prevent premature call (from super class)
if (!this.front || !this.worldUp) return
// Calculate the new Front vector
let front = vec3.create()
front[0] = Math.cos(this.rotation[0]) * Math.cos(this.rotation[1])
front[1] = Math.sin(this.rotation[1])
front[2] = Math.sin(this.rotation[0]) * Math.cos(this.rotation[1])
vec3.normalize(this.front, front)
// Also re-calculate the Right and Up vector
// Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
let rightCross = vec3.create()
let upCross = vec3.create()
vec3.cross(rightCross, this.front, this.worldUp)
vec3.normalize(this.right, rightCross)
vec3.cross(upCross, this.right, this.front)
vec3.normalize(this.up, upCross)
}
updateProjection (gl) {
let aspect = gl.canvas.width / gl.canvas.height
mat4.perspective(this.projection, this.fov, aspect, ZNEAR, ZFAR)
}
// Calculate the view matrix on-the-go
// Really no advantage in storing this
get view () {
let mat = mat4.create()
let center = vec3.create()
vec3.add(center, this.pos, this.front)
mat4.lookAt(mat, this.pos, center, this.up)
return mat
}
// Override the default draw method because we don't need to draw the camera,
// instead set the projection and view matrices
draw (gl, shader) {
const projloc = shader.getUniformLocation(gl, 'uProjectionMatrix')
const viewloc = shader.getUniformLocation(gl, 'uViewMatrix')
gl.uniformMatrix4fv(projloc, false, this.projection)
gl.uniformMatrix4fv(viewloc, false, this.view)
}
}
export default Camera