UNPKG

shaku

Version:

A simple and effective JavaScript game development framework that knows its place!

167 lines (148 loc) 5.48 kB
/** * Camera class. * * |-- copyright and license --| * @module Shaku * @file shaku\src\gfx\camera3d.js * @author Ronen Ness (ronenness@gmail.com | http://ronenness.com) * @copyright (c) 2021 Ronen Ness * @license MIT * |-- end copyright and license --| * */ 'use strict'; const Matrix = require('../utils/matrix.js'); const Vector3 = require("../utils/vector3"); const Frustum = require("../utils/frustum"); const Camera = require('./camera.js'); const Vector2 = require('../utils/vector2.js'); /** * Implements a 3d Camera object. */ class Camera3D extends Camera { /** * Create the camera. * @param {Gfx} gfx The gfx manager instance. */ constructor(gfx) { super(gfx); /** * Camera projection matrix. * You can set it manually, or use 'orthographicOffset' / 'orthographic' / 'perspective' helper functions. */ this.projection = null; /** * Camera view matrix. * You can set it manually, or use 'setViewLookat' helper function. */ this.view = null; // build perspective camera by default this.perspective(); this.setViewLookat(new Vector3(0, 5, -10), Vector3.zero()); } /** * Calc and return the currently-visible view frustum, based on active camera. * @returns {Frustum} Visible frustum. */ calcVisibleFrustum() { if (!this.projection || !this.view) { throw new Error("You must set both projection and view matrices to calculate visible frustum!"); } const frustum = new Frustum(); frustum.setFromProjectionMatrix(Matrix.multiply(this.projection, this.view)); return frustum; } /** * Set camera view matrix from source position and lookat. * @param {Vector3=} eyePosition Camera source position. * @param {Vector3=} lookAt Camera look-at target. */ setViewLookat(eyePosition, lookAt) { this.view = Matrix.createLookAt(eyePosition || new Vector3(0, 0, -500), lookAt || Vector3.zeroReadonly, Vector3.upReadonly); } /** * Get 3d direction vector of this camera. * @returns {Vector3} 3D direction vector. */ getDirection() { const e = this.view.values; return new Vector3( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalizeSelf(); } /** * Get view projection matrix. * @returns {Matrix} View-projection matrix. */ getViewProjection() { return Matrix.multiply(this.view, this.projection); } /** * Get projection view matrix. * @returns {Matrix} Projection-view matrix. */ getProjectionView() { return Matrix.multiply(this.projection, this.view); } /** * Make this camera a perspective camera. * @param {*} fieldOfView Field of view angle in radians. * @param {*} aspectRatio Aspect ratio. * @param {*} near Near clipping plane. * @param {*} far Far clipping plane. */ perspective(fieldOfView, aspectRatio, near, far) { this.projection = Matrix.createPerspective(fieldOfView || (Math.PI / 2), aspectRatio || 1, near || 0.1, far || 1000); } /** * Unproject a 2d vector into 3D space. * You can use this method to get the 3D direction the user points on with the mouse. * @param {Vector2} point Vector to unproject. * @param {Number} zDistance Distance from camera to locate the 3D point at (0 = near plane, 1 = far plane). * @returns {Vector3} Unprojected point in 3D space. */ unproject(point, zDistance = 0) { function project(out, vec, m) { var x = vec[0], y = vec[1], z = vec[2], a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3], a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7], a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11], a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15] var lw = 1 / (x * a03 + y * a13 + z * a23 + a33) out[0] = (x * a00 + y * a10 + z * a20 + a30) * lw out[1] = (x * a01 + y * a11 + z * a21 + a31) * lw out[2] = (x * a02 + y * a12 + z * a22 + a32) * lw return out } function unproject(out, vec, viewport, invProjectionView) { var viewX = viewport[0], viewY = viewport[1], viewWidth = viewport[2], viewHeight = viewport[3] var x = vec[0], y = vec[1], z = vec[2] x = x - viewX y = viewHeight - y - 1 y = y - viewY out[0] = (2 * x) / viewWidth - 1 out[1] = (2 * y) / viewHeight - 1 out[2] = 2 * z - 1 return project(out, out, invProjectionView.values) } const out = []; const projInverted = this.getProjectionView().inverted(); const viewport = this.viewport || Shaku.gfx.getRenderingRegion(); unproject(out, [point.x, point.y, zDistance], [viewport.x, viewport.y, viewport.width, viewport.height], projInverted); return new Vector3(out[0], out[1], out[2]); } } // export the camera object module.exports = Camera3D;