UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

93 lines (86 loc) 3.36 kB
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; } } }