194 lines
4.5 KiB
TypeScript
194 lines
4.5 KiB
TypeScript
/*!
|
|
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
import { Matrix4x4, Node } from ".";
|
|
|
|
export type Camera = OrthographicCamera | PerspectiveCamera;
|
|
|
|
export interface OrthographicCameraProps {
|
|
readonly name?: string;
|
|
|
|
readonly verticalSize: number;
|
|
readonly nearPlane: number;
|
|
readonly farPlane: number;
|
|
}
|
|
|
|
export interface PerspectiveCameraProps {
|
|
readonly name?: string;
|
|
|
|
readonly verticalFovRad: number;
|
|
readonly nearPlane: number;
|
|
readonly farPlane: number;
|
|
}
|
|
|
|
export class OrthographicCamera {
|
|
|
|
readonly type!: "OrthographicCamera";
|
|
|
|
_name: string;
|
|
|
|
_halfVerticalSize: number;
|
|
_nearPlane: number;
|
|
_farPlane: number;
|
|
|
|
/** backreference */
|
|
_node: Node | null;
|
|
|
|
constructor({
|
|
name = "",
|
|
verticalSize,
|
|
nearPlane,
|
|
farPlane,
|
|
}: OrthographicCameraProps) {
|
|
this._name = name;
|
|
|
|
this._halfVerticalSize = verticalSize;
|
|
this._nearPlane = nearPlane;
|
|
this._farPlane = farPlane;
|
|
|
|
this._node = null;
|
|
}
|
|
|
|
set name(value: string) { this._name = value; }
|
|
get name(): string { return this._name; }
|
|
|
|
set halfVerticalSize(value: number) { this._halfVerticalSize = value; }
|
|
get halfVerticalSize(): number { return this._halfVerticalSize; }
|
|
|
|
set nearPlane(value: number) { this._nearPlane = value; }
|
|
get nearPlane(): number { return this._nearPlane; }
|
|
|
|
set farPlane(value: number) { this._farPlane = value; }
|
|
get farPlane(): number { return this._farPlane; }
|
|
|
|
attach(node: Node): OrthographicCamera {
|
|
if (this._node !== null) {
|
|
this._node._camera = null;
|
|
}
|
|
|
|
if (node._camera !== null) {
|
|
node._camera._node = null;
|
|
}
|
|
|
|
node._camera = this;
|
|
this._node = node;
|
|
return this;
|
|
}
|
|
|
|
detach(): OrthographicCamera {
|
|
if (this._node === null) {
|
|
return this;
|
|
}
|
|
|
|
this._node._camera = null;
|
|
this._node = null;
|
|
return this;
|
|
}
|
|
|
|
computeProjectionMatrix(aspectRatio: number, res: Matrix4x4): Matrix4x4 {
|
|
const halfHorizontalSize = this._halfVerticalSize / aspectRatio;
|
|
return res.set(
|
|
1 / halfHorizontalSize, 0, 0, 0,
|
|
0, 1 / this._halfVerticalSize, 0, 0,
|
|
0, 0, 1 / (this._nearPlane - this._farPlane), 0,
|
|
0, 0, this._farPlane / (this._farPlane - this._nearPlane), 1,
|
|
);
|
|
}
|
|
}
|
|
|
|
export class PerspectiveCamera {
|
|
|
|
readonly type!: "PerspectiveCamera";
|
|
|
|
_name: string;
|
|
|
|
_verticalFovRad: number;
|
|
_nearPlane: number;
|
|
_farPlane: number;
|
|
|
|
/** backreference */
|
|
_node: Node | null;
|
|
|
|
constructor({
|
|
name = "",
|
|
verticalFovRad,
|
|
nearPlane,
|
|
farPlane,
|
|
}: PerspectiveCameraProps) {
|
|
this._name = name;
|
|
|
|
this._verticalFovRad = verticalFovRad;
|
|
this._nearPlane = nearPlane;
|
|
this._farPlane = farPlane;
|
|
|
|
this._node = null;
|
|
}
|
|
|
|
set name(value: string) { this._name = value; }
|
|
get name(): string { return this._name; }
|
|
|
|
set nearPlane(value: number) { this._nearPlane = value; }
|
|
get nearPlane(): number { return this._nearPlane; }
|
|
|
|
set farPlane(value: number) { this._farPlane = value; }
|
|
get farPlane(): number { return this._farPlane; }
|
|
|
|
attach(node: Node): PerspectiveCamera {
|
|
if (this._node !== null) {
|
|
this._node._camera = null;
|
|
}
|
|
|
|
if (node._camera !== null) {
|
|
node._camera._node = null;
|
|
}
|
|
|
|
node._camera = this;
|
|
this._node = node;
|
|
return this;
|
|
}
|
|
|
|
detach(): PerspectiveCamera {
|
|
if (this._node === null) {
|
|
return this;
|
|
}
|
|
|
|
this._node._camera = null;
|
|
this._node = null;
|
|
return this;
|
|
}
|
|
|
|
computeProjectionMatrix(aspectRatio: number, res: Matrix4x4): Matrix4x4 {
|
|
const halfVerticalCotangent = 1 / Math.tan(0.5 * this._verticalFovRad);
|
|
if (this._farPlane === Infinity) {
|
|
return res.set(
|
|
halfVerticalCotangent / aspectRatio, 0, 0, 0,
|
|
0, halfVerticalCotangent, 0, 0,
|
|
0, 0, 0, 1,
|
|
0, 0, this._nearPlane, 0,
|
|
);
|
|
} else {
|
|
return res.set(
|
|
halfVerticalCotangent / aspectRatio, 0, 0, 0,
|
|
0, halfVerticalCotangent, 0, 0,
|
|
0, 0, this._nearPlane / (this._nearPlane - this._farPlane), 1,
|
|
0, 0, this._nearPlane * this._farPlane / (this._farPlane - this._nearPlane), 0,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Object.defineProperty(OrthographicCamera.prototype, "type", { value: "OrthographicCamera" });
|
|
|
|
Object.defineProperty(PerspectiveCamera.prototype, "type", { value: "PerspectiveCamera" });
|
|
|
|
export function isOrthographicCamera(value: unknown): value is OrthographicCamera {
|
|
return Boolean(value) && (value as OrthographicCamera).type === "OrthographicCamera";
|
|
}
|
|
|
|
export function isPerspectiveCamera(value: unknown): value is PerspectiveCamera {
|
|
return Boolean(value) && (value as PerspectiveCamera).type === "PerspectiveCamera";
|
|
}
|