@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
93 lines (86 loc) • 3.36 kB
text/typescript
import {MathUtils, PerspectiveCamera} from 'three';
import {TypeAssert} from '../../../engine/poly/Assert';
import {PerspectiveCameraAttribute, UpdateProjectionOptions} from '../CoreCamera';
import {CameraFrameMode} from '../CoreCameraFrameMode';
import {BaseCoreCameraFrameMode} from './_BaseCoreCameraFrameMode';
import {cameraSetViewOffset} from '../CoreCameraViewOffset';
import {coreObjectClassFactory} from '../../geometry/CoreObjectFactory';
interface CoreCameraPerspectiveFrameModeOptions {
camera: PerspectiveCamera;
mode: CameraFrameMode;
fov: number;
expectedAspectRatio: number;
}
interface UpdateOptions extends UpdateProjectionOptions {
cameraWithAttributes?: PerspectiveCamera;
}
export class CoreCameraPerspectiveFrameMode {
static updateCameraAspect(camera: PerspectiveCamera, aspect: number, options?: UpdateOptions) {
camera.aspect = aspect;
const cameraWithAttributes = options?.cameraWithAttributes || camera;
const frameMode = BaseCoreCameraFrameMode.frameMode(cameraWithAttributes);
const expectedAspectRatio = BaseCoreCameraFrameMode.expectedAspectRatio(cameraWithAttributes) as
| number
| undefined;
const fov = coreObjectClassFactory(cameraWithAttributes).attribValue(
cameraWithAttributes,
PerspectiveCameraAttribute.FOV
) as number | undefined;
if (fov != null && expectedAspectRatio != null) {
this._update({
mode: frameMode,
camera: camera,
fov: fov,
expectedAspectRatio: expectedAspectRatio,
});
}
if (options && options.resolution) {
cameraSetViewOffset(camera, options.resolution);
}
camera.updateProjectionMatrix();
}
private static _update(options: CoreCameraPerspectiveFrameModeOptions) {
const mode = options.mode;
switch (mode) {
case CameraFrameMode.DEFAULT: {
return this._adjustFOVFromModeDefault(options);
}
case CameraFrameMode.COVER: {
return this._adjustFOVFromModeCover(options);
}
case CameraFrameMode.CONTAIN: {
return this._adjustFOVFromModeContain(options);
}
}
TypeAssert.unreachable(mode);
}
private static _adjustFOVFromModeDefault(options: CoreCameraPerspectiveFrameModeOptions) {
options.camera.fov = options.fov;
}
private static _adjustFOVFromModeCover(options: CoreCameraPerspectiveFrameModeOptions) {
// from
// https://discourse.threejs.org/t/keeping-an-object-scaled-based-on-the-bounds-of-the-canvas-really-battling-to-explain-this-one/17574/10
//
if (options.camera.aspect > options.expectedAspectRatio) {
// window too large
const cameraHeight = Math.tan(MathUtils.degToRad(options.fov / 2));
const ratio = options.camera.aspect / options.expectedAspectRatio;
const newCameraHeight = cameraHeight / ratio;
options.camera.fov = MathUtils.radToDeg(Math.atan(newCameraHeight)) * 2;
} else {
options.camera.fov = options.fov;
}
}
private static _adjustFOVFromModeContain(options: CoreCameraPerspectiveFrameModeOptions) {
if (options.camera.aspect > options.expectedAspectRatio) {
// window too large
options.camera.fov = options.fov;
} else {
// window too narrow
const cameraHeight = Math.tan(MathUtils.degToRad(options.fov / 2));
const ratio = options.camera.aspect / options.expectedAspectRatio;
const newCameraHeight = cameraHeight / ratio;
options.camera.fov = MathUtils.radToDeg(Math.atan(newCameraHeight)) * 2;
}
}
}