Spaces:
Sleeping
Sleeping
File size: 5,227 Bytes
2b7aae2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | import { Camera } from './Camera.js';
import * as MathUtils from '../math/MathUtils.js';
class PerspectiveCamera extends Camera {
constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) {
super();
this.type = 'PerspectiveCamera';
this.fov = fov;
this.zoom = 1;
this.near = near;
this.far = far;
this.focus = 10;
this.aspect = aspect;
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();
}
copy(source, recursive) {
super.copy(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(focalLength) {
/** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */
const vExtentSlope = (0.5 * this.getFilmHeight()) / focalLength;
this.fov = MathUtils.RAD2DEG * 2 * Math.atan(vExtentSlope);
this.updateProjectionMatrix();
}
/**
* Calculates the focal length from the current .fov and .filmGauge.
*/
getFocalLength() {
const vExtentSlope = Math.tan(MathUtils.DEG2RAD * 0.5 * this.fov);
return (0.5 * this.getFilmHeight()) / vExtentSlope;
}
getEffectiveFOV() {
return MathUtils.RAD2DEG * 2 * Math.atan(Math.tan(MathUtils.DEG2RAD * 0.5 * this.fov) / this.zoom);
}
getFilmWidth() {
// film not completely covered in portrait format (aspect < 1)
return this.filmGauge * Math.min(this.aspect, 1);
}
getFilmHeight() {
// 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
*
* const w = 1920;
* const h = 1080;
* const fullWidth = w * 3;
* const 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(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() {
if (this.view !== null) {
this.view.enabled = false;
}
this.updateProjectionMatrix();
}
updateProjectionMatrix() {
const near = this.near;
let top = (near * Math.tan(MathUtils.DEG2RAD * 0.5 * this.fov)) / this.zoom;
let height = 2 * top;
let width = this.aspect * height;
let left = -0.5 * width;
const view = this.view;
if (this.view !== null && this.view.enabled) {
const fullWidth = view.fullWidth,
fullHeight = view.fullHeight;
left += (view.offsetX * width) / fullWidth;
top -= (view.offsetY * height) / fullHeight;
width *= view.width / fullWidth;
height *= view.height / fullHeight;
}
const 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.copy(this.projectionMatrix).invert();
}
toJSON(meta) {
const data = super.toJSON(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;
}
}
PerspectiveCamera.prototype.isPerspectiveCamera = true;
export { PerspectiveCamera };
|