├── .gitignore
├── mockups-on-cylinders
├── images
│ ├── background.png
│ └── mockup.png
├── index.html
└── js
│ ├── VRControls.js
│ ├── VREffect.js
│ └── three.min.js
├── readme.md
└── threejs-vr-boilerplate
├── index.html
└── js
├── VRControls.js
├── VREffect.js
└── three.min.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/mockups-on-cylinders/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MozillaReality/vr-web-examples/96333f53953535d44bb5b34e6f062377d9407a8e/mockups-on-cylinders/images/background.png
--------------------------------------------------------------------------------
/mockups-on-cylinders/images/mockup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MozillaReality/vr-web-examples/96333f53953535d44bb5b34e6f062377d9407a8e/mockups-on-cylinders/images/mockup.png
--------------------------------------------------------------------------------
/mockups-on-cylinders/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | VR Mockups on Cylinders Boilerplate
6 |
7 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
31 |
32 |
33 |
36 |
37 |
38 |
218 |
219 |
--------------------------------------------------------------------------------
/mockups-on-cylinders/js/VRControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | */
5 |
6 | THREE.VRControls = function ( object, onError ) {
7 |
8 | var scope = this;
9 |
10 | var vrInputs = [];
11 |
12 | function filterInvalidDevices( devices ) {
13 |
14 | // Exclude Cardboard position sensor if Oculus exists.
15 |
16 | var oculusDevices = devices.filter( function ( device ) {
17 |
18 | return device.deviceName.toLowerCase().indexOf('oculus') !== -1;
19 |
20 | } );
21 |
22 | if ( oculusDevices.length >= 1 ) {
23 |
24 | return devices.filter( function ( device ) {
25 |
26 | return device.deviceName.toLowerCase().indexOf('cardboard') === -1;
27 |
28 | } );
29 |
30 | } else {
31 |
32 | return devices;
33 |
34 | }
35 |
36 | }
37 |
38 | function gotVRDevices( devices ) {
39 |
40 | devices = filterInvalidDevices( devices );
41 |
42 | for ( var i = 0; i < devices.length; i ++ ) {
43 |
44 | if ( devices[ i ] instanceof PositionSensorVRDevice ) {
45 |
46 | vrInputs.push( devices[ i ] );
47 |
48 | }
49 |
50 | }
51 |
52 | if ( onError ) onError( 'HMD not available' );
53 |
54 | }
55 |
56 | if ( navigator.getVRDevices ) {
57 |
58 | navigator.getVRDevices().then( gotVRDevices );
59 |
60 | }
61 |
62 | // the Rift SDK returns the position in meters
63 | // this scale factor allows the user to define how meters
64 | // are converted to scene units.
65 |
66 | this.scale = 1;
67 |
68 | this.update = function () {
69 |
70 | for ( var i = 0; i < vrInputs.length; i ++ ) {
71 |
72 | var vrInput = vrInputs[ i ];
73 |
74 | var state = vrInput.getState();
75 |
76 | if ( state.orientation !== null ) {
77 |
78 | object.quaternion.copy( state.orientation );
79 |
80 | }
81 |
82 | if ( state.position !== null ) {
83 |
84 | object.position.copy( state.position ).multiplyScalar( scope.scale );
85 |
86 | }
87 |
88 | }
89 |
90 | };
91 |
92 | this.resetSensor = function () {
93 |
94 | for ( var i = 0; i < vrInputs.length; i ++ ) {
95 |
96 | var vrInput = vrInputs[ i ];
97 |
98 | if ( vrInput.resetSensor !== undefined ) {
99 |
100 | vrInput.resetSensor();
101 |
102 | } else if ( vrInput.zeroSensor !== undefined ) {
103 |
104 | vrInput.zeroSensor();
105 |
106 | }
107 |
108 | }
109 |
110 | };
111 |
112 | this.zeroSensor = function () {
113 |
114 | THREE.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' );
115 | this.resetSensor();
116 |
117 | };
118 |
119 | };
120 |
--------------------------------------------------------------------------------
/mockups-on-cylinders/js/VREffect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | *
5 | * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
6 | *
7 | * Firefox: http://mozvr.com/downloads/
8 | * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
9 | *
10 | */
11 |
12 | THREE.VREffect = function ( renderer, onError ) {
13 |
14 | var vrHMD;
15 | var eyeTranslationL, eyeFOVL;
16 | var eyeTranslationR, eyeFOVR;
17 |
18 | function gotVRDevices( devices ) {
19 |
20 | for ( var i = 0; i < devices.length; i ++ ) {
21 |
22 | if ( devices[ i ] instanceof HMDVRDevice ) {
23 |
24 | vrHMD = devices[ i ];
25 |
26 | if ( vrHMD.getEyeParameters !== undefined ) {
27 |
28 | var eyeParamsL = vrHMD.getEyeParameters( 'left' );
29 | var eyeParamsR = vrHMD.getEyeParameters( 'right' );
30 |
31 | eyeTranslationL = eyeParamsL.eyeTranslation;
32 | eyeTranslationR = eyeParamsR.eyeTranslation;
33 | eyeFOVL = eyeParamsL.recommendedFieldOfView;
34 | eyeFOVR = eyeParamsR.recommendedFieldOfView;
35 |
36 | } else {
37 |
38 | // TODO: This is an older code path and not spec compliant.
39 | // It should be removed at some point in the near future.
40 | eyeTranslationL = vrHMD.getEyeTranslation( 'left' );
41 | eyeTranslationR = vrHMD.getEyeTranslation( 'right' );
42 | eyeFOVL = vrHMD.getRecommendedEyeFieldOfView( 'left' );
43 | eyeFOVR = vrHMD.getRecommendedEyeFieldOfView( 'right' );
44 |
45 | }
46 |
47 | break; // We keep the first we encounter
48 |
49 | }
50 |
51 | }
52 |
53 | if ( vrHMD === undefined ) {
54 |
55 | if ( onError ) onError( 'HMD not available' );
56 |
57 | }
58 |
59 | }
60 |
61 | if ( navigator.getVRDevices ) {
62 |
63 | navigator.getVRDevices().then( gotVRDevices );
64 |
65 | }
66 |
67 | //
68 |
69 | this.scale = 1;
70 |
71 | this.setSize = function( width, height ) {
72 |
73 | renderer.setSize( width, height );
74 |
75 | };
76 |
77 | // fullscreen
78 |
79 | var isFullscreen = false;
80 |
81 | var canvas = renderer.domElement;
82 | var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
83 |
84 | document.addEventListener( fullscreenchange, function ( event ) {
85 |
86 | isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement;
87 |
88 | }, false );
89 |
90 | this.setFullScreen = function ( boolean ) {
91 |
92 | if ( vrHMD === undefined ) return;
93 | if ( isFullscreen === boolean ) return;
94 |
95 | if ( canvas.mozRequestFullScreen ) {
96 |
97 | canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
98 |
99 | } else if ( canvas.webkitRequestFullscreen ) {
100 |
101 | canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
102 |
103 | }
104 |
105 | };
106 |
107 | // render
108 |
109 | var cameraL = new THREE.PerspectiveCamera();
110 | var cameraR = new THREE.PerspectiveCamera();
111 |
112 | this.render = function ( scene, camera ) {
113 |
114 | if ( vrHMD ) {
115 |
116 | var sceneL, sceneR;
117 |
118 | if ( scene instanceof Array ) {
119 |
120 | sceneL = scene[ 0 ];
121 | sceneR = scene[ 1 ];
122 |
123 | } else {
124 |
125 | sceneL = scene;
126 | sceneR = scene;
127 |
128 | }
129 |
130 | var size = renderer.getSize();
131 | size.width /= 2;
132 |
133 | renderer.enableScissorTest( true );
134 | renderer.clear();
135 |
136 | if ( camera.parent === undefined ) camera.updateMatrixWorld();
137 |
138 | cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
139 | cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );
140 |
141 | camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
142 | camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
143 |
144 | cameraL.translateX( eyeTranslationL.x * this.scale );
145 | cameraR.translateX( eyeTranslationR.x * this.scale );
146 |
147 | // render left eye
148 | renderer.setViewport( 0, 0, size.width, size.height );
149 | renderer.setScissor( 0, 0, size.width, size.height );
150 | renderer.render( sceneL, cameraL );
151 |
152 | // render right eye
153 | renderer.setViewport( size.width, 0, size.width, size.height );
154 | renderer.setScissor( size.width, 0, size.width, size.height );
155 | renderer.render( sceneR, cameraR );
156 |
157 | renderer.enableScissorTest( false );
158 |
159 | return;
160 |
161 | }
162 |
163 | // Regular render mode if not HMD
164 |
165 | if ( scene instanceof Array ) scene = scene[ 0 ];
166 |
167 | renderer.render( scene, camera );
168 |
169 | };
170 |
171 | //
172 |
173 | function fovToNDCScaleOffset( fov ) {
174 |
175 | var pxscale = 2.0 / (fov.leftTan + fov.rightTan);
176 | var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5;
177 | var pyscale = 2.0 / (fov.upTan + fov.downTan);
178 | var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5;
179 | return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
180 |
181 | }
182 |
183 | function fovPortToProjection( fov, rightHanded, zNear, zFar ) {
184 |
185 | rightHanded = rightHanded === undefined ? true : rightHanded;
186 | zNear = zNear === undefined ? 0.01 : zNear;
187 | zFar = zFar === undefined ? 10000.0 : zFar;
188 |
189 | var handednessScale = rightHanded ? -1.0 : 1.0;
190 |
191 | // start with an identity matrix
192 | var mobj = new THREE.Matrix4();
193 | var m = mobj.elements;
194 |
195 | // and with scale/offset info for normalized device coords
196 | var scaleAndOffset = fovToNDCScaleOffset(fov);
197 |
198 | // X result, map clip edges to [-w,+w]
199 | m[0 * 4 + 0] = scaleAndOffset.scale[0];
200 | m[0 * 4 + 1] = 0.0;
201 | m[0 * 4 + 2] = scaleAndOffset.offset[0] * handednessScale;
202 | m[0 * 4 + 3] = 0.0;
203 |
204 | // Y result, map clip edges to [-w,+w]
205 | // Y offset is negated because this proj matrix transforms from world coords with Y=up,
206 | // but the NDC scaling has Y=down (thanks D3D?)
207 | m[1 * 4 + 0] = 0.0;
208 | m[1 * 4 + 1] = scaleAndOffset.scale[1];
209 | m[1 * 4 + 2] = -scaleAndOffset.offset[1] * handednessScale;
210 | m[1 * 4 + 3] = 0.0;
211 |
212 | // Z result (up to the app)
213 | m[2 * 4 + 0] = 0.0;
214 | m[2 * 4 + 1] = 0.0;
215 | m[2 * 4 + 2] = zFar / (zNear - zFar) * -handednessScale;
216 | m[2 * 4 + 3] = (zFar * zNear) / (zNear - zFar);
217 |
218 | // W result (= Z in)
219 | m[3 * 4 + 0] = 0.0;
220 | m[3 * 4 + 1] = 0.0;
221 | m[3 * 4 + 2] = handednessScale;
222 | m[3 * 4 + 3] = 0.0;
223 |
224 | mobj.transpose();
225 |
226 | return mobj;
227 | }
228 |
229 | function fovToProjection( fov, rightHanded, zNear, zFar ) {
230 |
231 | var DEG2RAD = Math.PI / 180.0;
232 |
233 | var fovPort = {
234 | upTan: Math.tan( fov.upDegrees * DEG2RAD ),
235 | downTan: Math.tan( fov.downDegrees * DEG2RAD ),
236 | leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
237 | rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
238 | };
239 |
240 | return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
241 |
242 | }
243 |
244 | };
245 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## VR Web Examples
2 |
3 | A collection of boilerplate web VR scenes from the Mozilla Research VR team. For additional demos, tutorials and resources, visit [mozvr.com](http://mozvr.com)
4 |
5 | * [Basic Three.js VR boilerplate](http://mozvr.github.io/vr-web-examples/threejs-vr-boilerplate/)
6 | * [Mockups on cylinders](http://mozvr.github.io/vr-web-examples/mockups-on-cylinders/)
7 |
8 | To view these examples in virtual reality mode requires a VR-enabled browser and a compatible headset. Currently the Oculus Rift DK1 and DK2 are supported, with support for additional devices coming soon.
9 |
10 | Download experimental builds of Firefox with VR support for Mac and Windows:
11 |
12 | [http://mozvr.com/downloads](http://mozvr.com/downloads)
13 |
14 | If using web VR for the first time, make sure to properly configure your display settings, as described on the mozvr.com downloads page [Read Me](http://mozvr.com/downloads). Not doing so can cause the browser to not display VR mode properly or create high levels of judder.
15 |
16 | ### Controls
17 |
18 | * `Double click` or press `F` to enter full-screen virtual reality mode.
19 | * Press `Z` to zero sensor (sets the forward direction of the scene).
20 |
--------------------------------------------------------------------------------
/threejs-vr-boilerplate/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Basic Three.js VR boilerplate
6 |
7 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
31 |
32 |
33 |
36 |
37 |
38 |
150 |
151 |
--------------------------------------------------------------------------------
/threejs-vr-boilerplate/js/VRControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | */
5 |
6 | THREE.VRControls = function ( object, onError ) {
7 |
8 | var scope = this;
9 |
10 | var vrInputs = [];
11 |
12 | function filterInvalidDevices( devices ) {
13 |
14 | // Exclude Cardboard position sensor if Oculus exists.
15 |
16 | var oculusDevices = devices.filter( function ( device ) {
17 |
18 | return device.deviceName.toLowerCase().indexOf('oculus') !== -1;
19 |
20 | } );
21 |
22 | if ( oculusDevices.length >= 1 ) {
23 |
24 | return devices.filter( function ( device ) {
25 |
26 | return device.deviceName.toLowerCase().indexOf('cardboard') === -1;
27 |
28 | } );
29 |
30 | } else {
31 |
32 | return devices;
33 |
34 | }
35 |
36 | }
37 |
38 | function gotVRDevices( devices ) {
39 |
40 | devices = filterInvalidDevices( devices );
41 |
42 | for ( var i = 0; i < devices.length; i ++ ) {
43 |
44 | if ( devices[ i ] instanceof PositionSensorVRDevice ) {
45 |
46 | vrInputs.push( devices[ i ] );
47 |
48 | }
49 |
50 | }
51 |
52 | if ( onError ) onError( 'HMD not available' );
53 |
54 | }
55 |
56 | if ( navigator.getVRDevices ) {
57 |
58 | navigator.getVRDevices().then( gotVRDevices );
59 |
60 | }
61 |
62 | // the Rift SDK returns the position in meters
63 | // this scale factor allows the user to define how meters
64 | // are converted to scene units.
65 |
66 | this.scale = 1;
67 |
68 | this.update = function () {
69 |
70 | for ( var i = 0; i < vrInputs.length; i ++ ) {
71 |
72 | var vrInput = vrInputs[ i ];
73 |
74 | var state = vrInput.getState();
75 |
76 | if ( state.orientation !== null ) {
77 |
78 | object.quaternion.copy( state.orientation );
79 |
80 | }
81 |
82 | if ( state.position !== null ) {
83 |
84 | object.position.copy( state.position ).multiplyScalar( scope.scale );
85 |
86 | }
87 |
88 | }
89 |
90 | };
91 |
92 | this.resetSensor = function () {
93 |
94 | for ( var i = 0; i < vrInputs.length; i ++ ) {
95 |
96 | var vrInput = vrInputs[ i ];
97 |
98 | if ( vrInput.resetSensor !== undefined ) {
99 |
100 | vrInput.resetSensor();
101 |
102 | } else if ( vrInput.zeroSensor !== undefined ) {
103 |
104 | vrInput.zeroSensor();
105 |
106 | }
107 |
108 | }
109 |
110 | };
111 |
112 | this.zeroSensor = function () {
113 |
114 | THREE.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' );
115 | this.resetSensor();
116 |
117 | };
118 |
119 | };
120 |
--------------------------------------------------------------------------------
/threejs-vr-boilerplate/js/VREffect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | *
5 | * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
6 | *
7 | * Firefox: http://mozvr.com/downloads/
8 | * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
9 | *
10 | */
11 |
12 | THREE.VREffect = function ( renderer, onError ) {
13 |
14 | var vrHMD;
15 | var eyeTranslationL, eyeFOVL;
16 | var eyeTranslationR, eyeFOVR;
17 |
18 | function gotVRDevices( devices ) {
19 |
20 | for ( var i = 0; i < devices.length; i ++ ) {
21 |
22 | if ( devices[ i ] instanceof HMDVRDevice ) {
23 |
24 | vrHMD = devices[ i ];
25 |
26 | if ( vrHMD.getEyeParameters !== undefined ) {
27 |
28 | var eyeParamsL = vrHMD.getEyeParameters( 'left' );
29 | var eyeParamsR = vrHMD.getEyeParameters( 'right' );
30 |
31 | eyeTranslationL = eyeParamsL.eyeTranslation;
32 | eyeTranslationR = eyeParamsR.eyeTranslation;
33 | eyeFOVL = eyeParamsL.recommendedFieldOfView;
34 | eyeFOVR = eyeParamsR.recommendedFieldOfView;
35 |
36 | } else {
37 |
38 | // TODO: This is an older code path and not spec compliant.
39 | // It should be removed at some point in the near future.
40 | eyeTranslationL = vrHMD.getEyeTranslation( 'left' );
41 | eyeTranslationR = vrHMD.getEyeTranslation( 'right' );
42 | eyeFOVL = vrHMD.getRecommendedEyeFieldOfView( 'left' );
43 | eyeFOVR = vrHMD.getRecommendedEyeFieldOfView( 'right' );
44 |
45 | }
46 |
47 | break; // We keep the first we encounter
48 |
49 | }
50 |
51 | }
52 |
53 | if ( vrHMD === undefined ) {
54 |
55 | if ( onError ) onError( 'HMD not available' );
56 |
57 | }
58 |
59 | }
60 |
61 | if ( navigator.getVRDevices ) {
62 |
63 | navigator.getVRDevices().then( gotVRDevices );
64 |
65 | }
66 |
67 | //
68 |
69 | this.scale = 1;
70 |
71 | this.setSize = function( width, height ) {
72 |
73 | renderer.setSize( width, height );
74 |
75 | };
76 |
77 | // fullscreen
78 |
79 | var isFullscreen = false;
80 |
81 | var canvas = renderer.domElement;
82 | var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
83 |
84 | document.addEventListener( fullscreenchange, function ( event ) {
85 |
86 | isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement;
87 |
88 | }, false );
89 |
90 | this.setFullScreen = function ( boolean ) {
91 |
92 | if ( vrHMD === undefined ) return;
93 | if ( isFullscreen === boolean ) return;
94 |
95 | if ( canvas.mozRequestFullScreen ) {
96 |
97 | canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
98 |
99 | } else if ( canvas.webkitRequestFullscreen ) {
100 |
101 | canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
102 |
103 | }
104 |
105 | };
106 |
107 | // render
108 |
109 | var cameraL = new THREE.PerspectiveCamera();
110 | var cameraR = new THREE.PerspectiveCamera();
111 |
112 | this.render = function ( scene, camera ) {
113 |
114 | if ( vrHMD ) {
115 |
116 | var sceneL, sceneR;
117 |
118 | if ( scene instanceof Array ) {
119 |
120 | sceneL = scene[ 0 ];
121 | sceneR = scene[ 1 ];
122 |
123 | } else {
124 |
125 | sceneL = scene;
126 | sceneR = scene;
127 |
128 | }
129 |
130 | var size = renderer.getSize();
131 | size.width /= 2;
132 |
133 | renderer.enableScissorTest( true );
134 | renderer.clear();
135 |
136 | if ( camera.parent === undefined ) camera.updateMatrixWorld();
137 |
138 | cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
139 | cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );
140 |
141 | camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
142 | camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
143 |
144 | cameraL.translateX( eyeTranslationL.x * this.scale );
145 | cameraR.translateX( eyeTranslationR.x * this.scale );
146 |
147 | // render left eye
148 | renderer.setViewport( 0, 0, size.width, size.height );
149 | renderer.setScissor( 0, 0, size.width, size.height );
150 | renderer.render( sceneL, cameraL );
151 |
152 | // render right eye
153 | renderer.setViewport( size.width, 0, size.width, size.height );
154 | renderer.setScissor( size.width, 0, size.width, size.height );
155 | renderer.render( sceneR, cameraR );
156 |
157 | renderer.enableScissorTest( false );
158 |
159 | return;
160 |
161 | }
162 |
163 | // Regular render mode if not HMD
164 |
165 | if ( scene instanceof Array ) scene = scene[ 0 ];
166 |
167 | renderer.render( scene, camera );
168 |
169 | };
170 |
171 | //
172 |
173 | function fovToNDCScaleOffset( fov ) {
174 |
175 | var pxscale = 2.0 / (fov.leftTan + fov.rightTan);
176 | var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5;
177 | var pyscale = 2.0 / (fov.upTan + fov.downTan);
178 | var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5;
179 | return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
180 |
181 | }
182 |
183 | function fovPortToProjection( fov, rightHanded, zNear, zFar ) {
184 |
185 | rightHanded = rightHanded === undefined ? true : rightHanded;
186 | zNear = zNear === undefined ? 0.01 : zNear;
187 | zFar = zFar === undefined ? 10000.0 : zFar;
188 |
189 | var handednessScale = rightHanded ? -1.0 : 1.0;
190 |
191 | // start with an identity matrix
192 | var mobj = new THREE.Matrix4();
193 | var m = mobj.elements;
194 |
195 | // and with scale/offset info for normalized device coords
196 | var scaleAndOffset = fovToNDCScaleOffset(fov);
197 |
198 | // X result, map clip edges to [-w,+w]
199 | m[0 * 4 + 0] = scaleAndOffset.scale[0];
200 | m[0 * 4 + 1] = 0.0;
201 | m[0 * 4 + 2] = scaleAndOffset.offset[0] * handednessScale;
202 | m[0 * 4 + 3] = 0.0;
203 |
204 | // Y result, map clip edges to [-w,+w]
205 | // Y offset is negated because this proj matrix transforms from world coords with Y=up,
206 | // but the NDC scaling has Y=down (thanks D3D?)
207 | m[1 * 4 + 0] = 0.0;
208 | m[1 * 4 + 1] = scaleAndOffset.scale[1];
209 | m[1 * 4 + 2] = -scaleAndOffset.offset[1] * handednessScale;
210 | m[1 * 4 + 3] = 0.0;
211 |
212 | // Z result (up to the app)
213 | m[2 * 4 + 0] = 0.0;
214 | m[2 * 4 + 1] = 0.0;
215 | m[2 * 4 + 2] = zFar / (zNear - zFar) * -handednessScale;
216 | m[2 * 4 + 3] = (zFar * zNear) / (zNear - zFar);
217 |
218 | // W result (= Z in)
219 | m[3 * 4 + 0] = 0.0;
220 | m[3 * 4 + 1] = 0.0;
221 | m[3 * 4 + 2] = handednessScale;
222 | m[3 * 4 + 3] = 0.0;
223 |
224 | mobj.transpose();
225 |
226 | return mobj;
227 | }
228 |
229 | function fovToProjection( fov, rightHanded, zNear, zFar ) {
230 |
231 | var DEG2RAD = Math.PI / 180.0;
232 |
233 | var fovPort = {
234 | upTan: Math.tan( fov.upDegrees * DEG2RAD ),
235 | downTan: Math.tan( fov.downDegrees * DEG2RAD ),
236 | leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
237 | rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
238 | };
239 |
240 | return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
241 |
242 | }
243 |
244 | };
245 |
--------------------------------------------------------------------------------