I am following this tutorial on how to implement a first person camera. So far I was able to implement the entire (C++) class in TypeScript and it works as it is supposed to.
Problem
What I was not able to implement is a system which takes the camera direction only and initially orients the camera based on it. In the tutorial the author does set the camera direction to (0.0, 0.0, -1.0)
but it is instantly overwritten by calling _updateCameraVectors()
in the constructor. So as I see it, passing the camera direction to the class will always be ignored. Before looking at the code please note:
- In the tutorial the author referes to a variable
Front
, which in my case is calledcameraDirection
- In the tutorial only the view matrix is being calculated by the camera class. I am calculating the view-projection matrix.
Code
Here are the most important parts:
The constructor initializes all the important stuff, but most importantly calls _updateCameraVectors()
.
constructor(gl: WebGL2RenderingContext, program: WebGLProgram, position: vec3, cameraDirection = vec3.fromValues(0.0, 0.0, -1.0),
yaw = -90.0, pitch = 0.0,
aspect = gl.canvas.clientWidth / gl.canvas.clientHeight, zNear = 1, zFar = 2000) {
this.gl = gl;
this._program = program;
this._aspect = aspect;
this._zNear = zNear;
this._zFar = zFar;
this._yaw = yaw;
this._pitch = pitch;
this._cameraPosition = position;
this._cameraDirection = cameraDirection;
this._updateCameraVectors();
GLHelper.setUniformLocations(this.gl, this._unifroms, this._program);
}
In _updateCameraVectors()
the camera direction is being recalculated based on yaw and pitch values. Then the cameraUp
vector is calculated.
protected _updateCameraVectors(): void {
const front = vec3.create();
front[0] = Math.cos(MathHelper.degToRad(this._yaw)) * Math.cos(MathHelper.degToRad(this._pitch));
front[1] = Math.sin(MathHelper.degToRad(this._pitch));
front[2] = Math.sin(MathHelper.degToRad(this._yaw)) * Math.cos(MathHelper.degToRad(this._pitch));
vec3.normalize(this._cameraDirection, front);
const right = vec3.create();
vec3.cross(right, this._cameraDirection, this._worldUp); // World up is always (0,1,0)
vec3.normalize(this._cameraRight, right);
const up = vec3.create();
vec3.cross(up, this._cameraRight, this._cameraDirection);
vec3.normalize(this._cameraUp, up);
}
Based on a requestAnimationFrame
-loop the _computeviewProjectionMatrix
simply calculates a view-projection matrix which is later used for rendering.
private _computeviewProjectionMatrix(): mat4 {
this._aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight;
const cameraTarget = vec3.create();
vec3.add(cameraTarget, this._cameraPosition, this._cameraDirection); // Here the actual camera target is calculated.
const view = mat4.create();
mat4.lookAt(view, this._cameraPosition, cameraTarget, this._cameraUp);
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, this._fieldOfViewRadians, this._aspect, this._zNear, this._zFar);
const viewProjectionMatrix = mat4.create();
mat4.multiply(viewProjectionMatrix, projectionMatrix, view);
return viewProjectionMatrix;
}
The _handleMouseMovement
function calls _updateCameraVectors()
every time the pitch
and yaw
values are updated via mouse movement.
private _handleMouseMovement = (event: MouseEvent): void => {
if (this._firstMouseMove) {
this._firstMouseMove = false;
return;
}
let xOffset = event.movementX;
let yOffset = event.movementY;
xOffset *= this._sensitivity;
yOffset *= this._sensitivity;
this._yaw += xOffset;
if (!this._inverseY) {
this._pitch += yOffset;
} else {
this._pitch -= yOffset;
}
if (this._pitch > 89) {
this._pitch = 89;
}
if (this._pitch < -89) {
this._pitch = -89;
}
this._updateCameraVectors();
}
Last but not least there is a _moveCamera
function which updates the camera position based on WASD input. I will post only a excerpt because it is selfexplonatory.
private _moveCamera(): void {
// INFO: 'W' - Move forward
if (this._keyMap[87] === true) {
const factor = vec3.create();
vec3.scale(factor, this._cameraDirection, this._cameraSpeed * this._deltaTime);
vec3.add(this._cameraPosition, this._cameraPosition, factor);
}
// ...
}
Question
How would I modify this class to initially "lookAt" a vec3 point passed to the constructor? Note I am not really asking for code only. Suggestion, ideas and any advice would be really helpful.