UNPKG

6.62 kBJavaScriptView Raw
1'use strict';
2
3import * as THREE from 'three';
4
5/**
6 * Class for managing multiple cameras used in a viewport.
7 * @class FluxCameras
8 * @param {Number} width Width of the viewport
9 * @param {Number} height Height of the viewport
10 */
11export default function FluxCameras(width, height) {
12 // Initialize default cameras and frustums.
13 this._perspCamera = new THREE.PerspectiveCamera(30, width/height, 0.1, 100000);
14 // Flux is Z up
15 this._perspCamera.up = new THREE.Vector3( 0, 0, 1 );
16
17 this._orthoCamera = new THREE.OrthographicCamera(100, -100, 100, -100, -1000, 1000);
18
19 this.setView(FluxCameras.VIEWS.perspective);
20 this.updateCamera(width, height);
21}
22
23/**
24 * Enumeration of all possible views for the camera.
25 * Values are perspective, top, bottom, front, back, right, left.
26 * @type {Object}
27 */
28FluxCameras.VIEWS = {
29 perspective: 0,
30 top: 1,
31 bottom: 2,
32 front: 3,
33 back: 4,
34 right: 5,
35 left: 6,
36 END: 7
37};
38
39/**
40 * Get the current camera object
41 * @return {THREE.Camera} The current camera
42 */
43FluxCameras.prototype.getCamera = function () {
44 if (this._view === FluxCameras.VIEWS.perspective) {
45 return this._perspCamera;
46 }
47 return this._orthoCamera;
48};
49
50FluxCameras.DEFAULT_POSITIONS = [
51 [25, 10, 13], // perspective
52 [0, 0, -100], // top
53 [0, 0, 100], // bottom
54 [0, 0, 0], // front
55 [0, 0, 0], // back
56 [0, 0, 0], // right
57 [0, 0, 0] // left
58];
59
60FluxCameras.DEFAULT_ROTATIONS = [
61 [0, 0, 0], // perspective
62 [0, 0, 0], // top
63 [0, Math.PI, 0], // bottom
64 [Math.PI/2, 0, 0], // front
65 [Math.PI/2, Math.PI, 0], // back
66 [Math.PI/2, Math.PI/2, 0], // right
67 [Math.PI/2, -Math.PI/2, 0] // left
68];
69
70FluxCameras.isValidView = function (view) {
71 return view != null && view.constructor === Number && view > -1 && view < FluxCameras.VIEWS.END;
72};
73
74/**
75 * Set which camera view to use (ex perspective, top etc.).
76 * @param {FluxCameras.VIEWS} view The new view mode
77 */
78FluxCameras.prototype.setView = function (view) {
79 if (!FluxCameras.isValidView(view)) return;
80 this._view = view;
81
82 var camera = this.getCamera();
83 camera.position.set(FluxCameras.DEFAULT_POSITIONS[view][0],
84 FluxCameras.DEFAULT_POSITIONS[view][1],
85 FluxCameras.DEFAULT_POSITIONS[view][2]);
86
87 camera.rotation.set(FluxCameras.DEFAULT_ROTATIONS[view][0],
88 FluxCameras.DEFAULT_ROTATIONS[view][1],
89 FluxCameras.DEFAULT_ROTATIONS[view][2]);
90};
91
92/**
93 * Recompute derived state when the camera is changed.
94 * @param {Number} width Width of the viewport (used to calculate aspect ratio)
95 * @param {Number} height Height of the viewport (used to calculate aspect ratio)
96 */
97FluxCameras.prototype.updateCamera = function(width, height) {
98 this._perspCamera.aspect = width / height;
99 this._perspCamera.updateProjectionMatrix();
100
101 var a = width / height;
102 var h = this._orthoCamera.top - this._orthoCamera.bottom;
103 this._orthoCamera.top = h / 2;
104 this._orthoCamera.bottom = - h / 2;
105 this._orthoCamera.right = h / 2 * a;
106 this._orthoCamera.left = - h / 2 * a;
107 this._orthoCamera.updateProjectionMatrix();
108};
109
110/**
111 * Extract only relevant properties from a camera
112 * @param {THREE.Camera} camera The camera source
113 * @return {Object} The camera data
114 */
115FluxCameras.cameraToJSON = function(camera) {
116 var serializableCamera = {
117 px: camera.position.x,
118 py: camera.position.y,
119 pz: camera.position.z,
120 rx: camera.rotation.x,
121 ry: camera.rotation.y,
122 rz: camera.rotation.z,
123 near: camera.near,
124 far: camera.far
125 };
126 // Handle extra OrthographicCamera properties
127 if (camera instanceof THREE.OrthographicCamera) {
128 serializableCamera.top = camera.top;
129 serializableCamera.right = camera.right;
130 serializableCamera.bottom = camera.bottom;
131 serializableCamera.left = camera.left;
132 serializableCamera.type = 'orthographic';
133 } else {
134 serializableCamera.type = 'perspective';
135 }
136 return serializableCamera;
137};
138
139/**
140 * Check if something is anumber
141 * @param {Number} num The value
142 * @returns {boolean} True for numbers
143 * @private
144 */
145function _isNumber(num) {
146 return num != null && num.constructor === Number;
147}
148
149/**
150 * Check whether a set of properties are valid numbers
151 * @param {Array.<string>} schema The list of properties
152 * @param {Object} data The object with properties
153 * @returns {boolean} True if all numbers
154 * @private
155 */
156function _checkNumbers(schema, data) {
157 // Make sure all the properties are valid and exist
158 for (var i=0;i<schema.length;i++) {
159 if (!_isNumber(data[schema[i]])) {
160 return false;
161 }
162 }
163 return true;
164}
165
166/**
167 * Rehydrate camera instance from an object property tree.
168 * @param {THREE.camera} camera The camera to receive data
169 * @param {Object} data The data to parse and apply
170 */
171FluxCameras.cameraFromJSON = function(camera, data) {
172 var schema = ['px', 'py', 'pz', 'rx', 'ry', 'rz', 'near', 'far'];
173 if (!_checkNumbers(schema, data)) return;
174 camera.position.x = data.px;
175 camera.position.y = data.py;
176 camera.position.z = data.pz;
177 camera.rotation.x = data.rx;
178 camera.rotation.y = data.ry;
179 camera.rotation.z = data.rz;
180 camera.near = data.near;
181 camera.far = data.far;
182
183 // Handle extra OrthographicCamera properties
184 if (camera.constructor === THREE.OrthographicCamera) {
185 schema = ['top', 'right', 'bottom', 'left'];
186 if (!_checkNumbers(schema, data)) return;
187 camera.top = data.top;
188 camera.right = data.right;
189 camera.bottom = data.bottom;
190 camera.left = data.left;
191 }
192};
193
194/**
195 * Make serializable by pruning all references and building an object property tree
196 * @return {Object} The simplified model
197 */
198FluxCameras.prototype.toJSON = function() {
199 var serializableCameras = {
200 perspective: FluxCameras.cameraToJSON(this._perspCamera),
201 orthographic: FluxCameras.cameraToJSON(this._orthoCamera),
202 view: this._view
203 };
204 return serializableCameras;
205};
206
207/**
208* Update the corresponding cameras in this object from a serialized object.
209* @param {Object} serializableCameras The camera data to use.
210*/
211FluxCameras.prototype.fromJSON = function(serializableCameras) {
212 this.setView(serializableCameras.view);
213 FluxCameras.cameraFromJSON(this._perspCamera, serializableCameras.perspective);
214 FluxCameras.cameraFromJSON(this._orthoCamera, serializableCameras.orthographic);
215};