mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
230 lines • 8.97 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const gl_matrix_1 = require("gl-matrix");
const gl_matrix_addon_1 = require("../common/gl-matrix-addon");
const vectorHeap = gl_matrix_1.vec3.create();
const vectorHeap2 = gl_matrix_1.vec3.create();
const vectorHeap3 = gl_matrix_1.vec3.create();
const quatHeap = gl_matrix_1.quat.create();
const facingCorrection = gl_matrix_1.quat.setAxisAngle(gl_matrix_1.quat.create(), gl_matrix_addon_1.VEC3_UNIT_X, Math.PI / 2);
/**
* A camera.
*/
class Camera {
constructor() {
this.isPerspective = true;
this.fov = 0;
this.aspect = 0;
this.isOrtho = false;
this.leftClipPlane = 0;
this.rightClipPlane = 0;
this.bottomClipPlane = 0;
this.topClipPlane = 0;
this.nearClipPlane = 0;
this.farClipPlane = 0;
this.location = gl_matrix_1.vec3.create();
this.rotation = gl_matrix_1.quat.create();
this.inverseRotation = gl_matrix_1.quat.create();
/**
* World -> View.
*/
this.viewMatrix = gl_matrix_1.mat4.create();
/**
* View -> Clip.
*/
this.projectionMatrix = gl_matrix_1.mat4.create();
/**
* World -> Clip.
*/
this.viewProjectionMatrix = gl_matrix_1.mat4.create();
/**
* View -> World.
*/
this.inverseViewMatrix = gl_matrix_1.mat4.create();
/**
* Clip -> World.
*/
this.inverseViewProjectionMatrix = gl_matrix_1.mat4.create();
/**
* The X axis in camera space.
*/
this.directionX = gl_matrix_1.vec3.create();
/**
* The Y axis in camera space.
*/
this.directionY = gl_matrix_1.vec3.create();
/**
* The Z axis in camera space.
*/
this.directionZ = gl_matrix_1.vec3.create();
/**
* The four corners of a 2x2 rectangle.
*/
this.vectors = [gl_matrix_1.vec3.fromValues(-1, -1, 0), gl_matrix_1.vec3.fromValues(-1, 1, 0), gl_matrix_1.vec3.fromValues(1, 1, 0), gl_matrix_1.vec3.fromValues(1, -1, 0), gl_matrix_1.vec3.fromValues(1, 0, 0), gl_matrix_1.vec3.fromValues(0, 1, 0), gl_matrix_1.vec3.fromValues(0, 0, 1)];
/**
* Same as vectors, however these are all billboarded to the camera.
*/
this.billboardedVectors = [gl_matrix_1.vec3.create(), gl_matrix_1.vec3.create(), gl_matrix_1.vec3.create(), gl_matrix_1.vec3.create(), gl_matrix_1.vec3.create(), gl_matrix_1.vec3.create(), gl_matrix_1.vec3.create()];
/**
* The camera frustum planes in this order: left, right, top, bottom, near, far.
*/
this.planes = [gl_matrix_1.vec4.create(), gl_matrix_1.vec4.create(), gl_matrix_1.vec4.create(), gl_matrix_1.vec4.create(), gl_matrix_1.vec4.create(), gl_matrix_1.vec4.create()];
}
/**
* Set the camera to perspective projection mode.
*/
perspective(fov, aspect, near, far) {
this.isPerspective = true;
this.isOrtho = false;
this.fov = fov;
this.aspect = aspect;
this.nearClipPlane = near;
this.farClipPlane = far;
this.update();
}
/**
* Set the camera to orthogonal projection mode.
*/
ortho(left, right, bottom, top, near, far) {
this.isPerspective = false;
this.isOrtho = true;
this.leftClipPlane = left;
this.rightClipPlane = right;
this.bottomClipPlane = bottom;
this.topClipPlane = top;
this.nearClipPlane = near;
this.farClipPlane = far;
this.update();
}
/**
* Set the camera location in world coordinates.
*/
setLocation(location) {
gl_matrix_1.vec3.copy(this.location, location);
this.update();
}
/**
* Move the camera by the given offset in world coordinates.
*/
move(offset) {
gl_matrix_1.vec3.add(this.location, this.location, offset);
this.update();
}
/**
* Set the camera rotation.
*/
setRotation(rotation) {
gl_matrix_1.quat.copy(this.rotation, rotation);
this.update();
}
/**
* Rotate the camera by the given rotation.
*/
rotate(rotation) {
gl_matrix_1.quat.mul(this.rotation, this.rotation, rotation);
this.update();
}
/**
* Look at `to`.
*/
face(to, worldUp) {
gl_matrix_1.quat.mul(this.rotation, facingCorrection, (0, gl_matrix_addon_1.quatLookAt)(quatHeap, to, this.location, worldUp));
this.update();
}
/**
* Move to `from` and look at `to`.
*/
moveToAndFace(from, to, worldUp) {
gl_matrix_1.vec3.copy(this.location, from);
this.face(to, worldUp);
}
/**
* Reset the location and angles.
*/
reset() {
gl_matrix_1.vec3.set(this.location, 0, 0, 0);
gl_matrix_1.quat.identity(this.rotation);
this.update();
}
/**
* Recalculate the camera's transformation.
*/
update() {
const location = this.location;
const rotation = this.rotation;
const inverseRotation = this.inverseRotation;
const viewMatrix = this.viewMatrix;
const projectionMatrix = this.projectionMatrix;
const viewProjectionMatrix = this.viewProjectionMatrix;
const vectors = this.vectors;
const billboardedVectors = this.billboardedVectors;
// View -> Clip.
if (this.isPerspective) {
gl_matrix_1.mat4.perspective(projectionMatrix, this.fov, this.aspect, this.nearClipPlane, this.farClipPlane);
}
else {
gl_matrix_1.mat4.ortho(projectionMatrix, this.leftClipPlane, this.rightClipPlane, this.bottomClipPlane, this.topClipPlane, this.nearClipPlane, this.farClipPlane);
}
// World -> View.
gl_matrix_1.mat4.fromQuat(viewMatrix, rotation);
gl_matrix_1.mat4.translate(viewMatrix, viewMatrix, gl_matrix_1.vec3.negate(vectorHeap, location));
// World -> Clip.
gl_matrix_1.mat4.mul(viewProjectionMatrix, projectionMatrix, viewMatrix);
// View -> World.
gl_matrix_1.mat4.invert(this.inverseViewMatrix, viewMatrix);
// Clip -> World.
gl_matrix_1.mat4.invert(this.inverseViewProjectionMatrix, viewProjectionMatrix);
// Recaculate the camera's frusum planes
(0, gl_matrix_addon_1.unpackPlanes)(this.planes, viewProjectionMatrix);
gl_matrix_1.quat.conjugate(inverseRotation, rotation);
// View-space axes.
gl_matrix_1.vec3.transformQuat(this.directionX, gl_matrix_addon_1.VEC3_UNIT_X, inverseRotation);
gl_matrix_1.vec3.transformQuat(this.directionY, gl_matrix_addon_1.VEC3_UNIT_Y, inverseRotation);
gl_matrix_1.vec3.transformQuat(this.directionZ, gl_matrix_addon_1.VEC3_UNIT_Z, inverseRotation);
// View-space rectangle, aka billboarded.
for (let i = 0; i < 7; i++) {
gl_matrix_1.vec3.transformQuat(billboardedVectors[i], vectors[i], inverseRotation);
}
}
/**
* Given a vector in camera space, return the vector transformed to world space.
*/
cameraToWorld(out, v) {
return gl_matrix_1.vec3.transformMat4(out, v, this.inverseViewMatrix);
}
/**
* Given a vector in world space, return the vector transformed to camera space.
*/
worldToCamera(out, v) {
return gl_matrix_1.vec3.transformMat4(out, v, this.viewMatrix);
}
/**
* Given a vector in world space, return the vector transformed to screen space.
*/
worldToScreen(out, v, viewport) {
gl_matrix_1.vec3.transformMat4(vectorHeap, v, this.viewProjectionMatrix);
out[0] = Math.round(((vectorHeap[0] + 1) / 2) * viewport[2]);
out[1] = Math.round(((vectorHeap[1] + 1) / 2) * viewport[3]);
return out;
}
/**
* Given a vector in screen space, return a ray from the near plane to the far plane.
*/
screenToWorldRay(out, v, viewport) {
const a = vectorHeap;
const b = vectorHeap2;
const c = vectorHeap3;
const x = v[0];
const y = v[1];
const inverseViewProjectionMatrix = this.inverseViewProjectionMatrix;
// Intersection on the near-plane
(0, gl_matrix_addon_1.unproject)(a, gl_matrix_1.vec3.set(c, x, y, 0), inverseViewProjectionMatrix, viewport);
// Intersection on the far-plane
(0, gl_matrix_addon_1.unproject)(b, gl_matrix_1.vec3.set(c, x, y, 1), inverseViewProjectionMatrix, viewport);
out.set(a, 0);
out.set(b, 3);
return out;
}
}
exports.default = Camera;
//# sourceMappingURL=camera.js.map