Spaces:
Running
Running
import { Camera } from './Camera.js'; | |
import { Object3D } from '../core/Object3D.js'; | |
import { _Math } from '../math/Math.js'; | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author greggman / http://games.greggman.com/ | |
* @author zz85 / http://www.lab4games.net/zz85/blog | |
* @author tschw | |
*/ | |
function PerspectiveCamera( fov, aspect, near, far ) { | |
Camera.call( this ); | |
this.type = 'PerspectiveCamera'; | |
this.fov = fov !== undefined ? fov : 50; | |
this.zoom = 1; | |
this.near = near !== undefined ? near : 0.1; | |
this.far = far !== undefined ? far : 2000; | |
this.focus = 10; | |
this.aspect = aspect !== undefined ? aspect : 1; | |
this.view = null; | |
this.filmGauge = 35; // width of the film (default in millimeters) | |
this.filmOffset = 0; // horizontal film offset (same unit as gauge) | |
this.updateProjectionMatrix(); | |
} | |
PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { | |
constructor: PerspectiveCamera, | |
isPerspectiveCamera: true, | |
copy: function ( source, recursive ) { | |
Camera.prototype.copy.call( this, source, recursive ); | |
this.fov = source.fov; | |
this.zoom = source.zoom; | |
this.near = source.near; | |
this.far = source.far; | |
this.focus = source.focus; | |
this.aspect = source.aspect; | |
this.view = source.view === null ? null : Object.assign( {}, source.view ); | |
this.filmGauge = source.filmGauge; | |
this.filmOffset = source.filmOffset; | |
return this; | |
}, | |
/** | |
* Sets the FOV by focal length in respect to the current .filmGauge. | |
* | |
* The default film gauge is 35, so that the focal length can be specified for | |
* a 35mm (full frame) camera. | |
* | |
* Values for focal length and film gauge must have the same unit. | |
*/ | |
setFocalLength: function ( focalLength ) { | |
// see http://www.bobatkins.com/photography/technical/field_of_view.html | |
var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; | |
this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); | |
this.updateProjectionMatrix(); | |
}, | |
/** | |
* Calculates the focal length from the current .fov and .filmGauge. | |
*/ | |
getFocalLength: function () { | |
var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); | |
return 0.5 * this.getFilmHeight() / vExtentSlope; | |
}, | |
getEffectiveFOV: function () { | |
return _Math.RAD2DEG * 2 * Math.atan( | |
Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); | |
}, | |
getFilmWidth: function () { | |
// film not completely covered in portrait format (aspect < 1) | |
return this.filmGauge * Math.min( this.aspect, 1 ); | |
}, | |
getFilmHeight: function () { | |
// film not completely covered in landscape format (aspect > 1) | |
return this.filmGauge / Math.max( this.aspect, 1 ); | |
}, | |
/** | |
* Sets an offset in a larger frustum. This is useful for multi-window or | |
* multi-monitor/multi-machine setups. | |
* | |
* For example, if you have 3x2 monitors and each monitor is 1920x1080 and | |
* the monitors are in grid like this | |
* | |
* +---+---+---+ | |
* | A | B | C | | |
* +---+---+---+ | |
* | D | E | F | | |
* +---+---+---+ | |
* | |
* then for each monitor you would call it like this | |
* | |
* var w = 1920; | |
* var h = 1080; | |
* var fullWidth = w * 3; | |
* var fullHeight = h * 2; | |
* | |
* --A-- | |
* camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); | |
* --B-- | |
* camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); | |
* --C-- | |
* camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); | |
* --D-- | |
* camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); | |
* --E-- | |
* camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); | |
* --F-- | |
* camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); | |
* | |
* Note there is no reason monitors have to be the same size or in a grid. | |
*/ | |
setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { | |
this.aspect = fullWidth / fullHeight; | |
if ( this.view === null ) { | |
this.view = { | |
enabled: true, | |
fullWidth: 1, | |
fullHeight: 1, | |
offsetX: 0, | |
offsetY: 0, | |
width: 1, | |
height: 1 | |
}; | |
} | |
this.view.enabled = true; | |
this.view.fullWidth = fullWidth; | |
this.view.fullHeight = fullHeight; | |
this.view.offsetX = x; | |
this.view.offsetY = y; | |
this.view.width = width; | |
this.view.height = height; | |
this.updateProjectionMatrix(); | |
}, | |
clearViewOffset: function () { | |
if ( this.view !== null ) { | |
this.view.enabled = false; | |
} | |
this.updateProjectionMatrix(); | |
}, | |
updateProjectionMatrix: function () { | |
var near = this.near, | |
top = near * Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, | |
height = 2 * top, | |
width = this.aspect * height, | |
left = - 0.5 * width, | |
view = this.view; | |
if ( this.view !== null && this.view.enabled ) { | |
var fullWidth = view.fullWidth, | |
fullHeight = view.fullHeight; | |
left += view.offsetX * width / fullWidth; | |
top -= view.offsetY * height / fullHeight; | |
width *= view.width / fullWidth; | |
height *= view.height / fullHeight; | |
} | |
var skew = this.filmOffset; | |
if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); | |
this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); | |
this.projectionMatrixInverse.getInverse( this.projectionMatrix ); | |
}, | |
toJSON: function ( meta ) { | |
var data = Object3D.prototype.toJSON.call( this, meta ); | |
data.object.fov = this.fov; | |
data.object.zoom = this.zoom; | |
data.object.near = this.near; | |
data.object.far = this.far; | |
data.object.focus = this.focus; | |
data.object.aspect = this.aspect; | |
if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); | |
data.object.filmGauge = this.filmGauge; | |
data.object.filmOffset = this.filmOffset; | |
return data; | |
} | |
} ); | |
export { PerspectiveCamera }; | |