├── README.md
├── css
├── buttons.css
├── images
│ ├── animated-overlay.gif
│ ├── ui-bg_flat_0_aaaaaa_40x100.png
│ ├── ui-bg_flat_75_ffffff_40x100.png
│ ├── ui-bg_glass_55_fbf9ee_1x400.png
│ ├── ui-bg_glass_65_ffffff_1x400.png
│ ├── ui-bg_glass_75_dadada_1x400.png
│ ├── ui-bg_glass_75_e6e6e6_1x400.png
│ ├── ui-bg_glass_95_fef1ec_1x400.png
│ ├── ui-bg_highlight-soft_75_cccccc_1x100.png
│ ├── ui-icons_222222_256x240.png
│ ├── ui-icons_2e83ff_256x240.png
│ ├── ui-icons_454545_256x240.png
│ ├── ui-icons_888888_256x240.png
│ └── ui-icons_cd0a0a_256x240.png
├── jquery-ui.css
└── osv.css
├── index.html
├── js
├── GSVPanoDepth.js
├── GSVPano_o.js
├── RequestAnimationFrame.js
├── VRControls.js
├── VREffect.js
├── gamepad.js
├── jquery-1.8.2.min.js
├── jquery-ui.min.js
├── jquery.base64.min.js
├── osv.js
├── three.min.js
├── webvr-manager.js
├── webvr-polyfill.js
└── zpipe.min.js
└── placeholder.png
/README.md:
--------------------------------------------------------------------------------
1 | OculusStreetView
2 | ================
3 |
4 | Google Street View viewer for the Oculus Rift
5 |
6 | Demo
7 | -------------
8 | http://oculusstreetview.eu.pn/index.html
9 |
10 | Usage
11 | -------------
12 | - Use the mini-map to select the location.
13 | - Double press the spacebar to toggle mini-map and settings
14 |
15 | Navigation
16 | -------------
17 | - **Keyboard**: Arrows keys to look around and double-press ctrl to enter a new location
18 | - **Mouse**: Click and drag to look around and left double-click to enter a new location
19 | - **Gamepad**: (Chrome only) Button 0 to enter a new location
20 |
21 | URL Parameters
22 | -------------
23 | index.html accepts the following parameters
24 |
25 | - *lat*, *lng* : latitude and longitude (e.g lat=-23.442896&lng=151.906584)
26 | - *q* : image quality (1: worst, 4:best)
27 | - *s* : show mini-map and settings (true or false)
28 |
29 | Example:
30 | http://oculusstreetview.eu.pn/index.html?lat=-23.442896&lng=151.906584&q=4&s=false
31 |
32 | Licence
33 | -------------
34 | MIT Licence
35 |
--------------------------------------------------------------------------------
/css/buttons.css:
--------------------------------------------------------------------------------
1 | .button { display: inline-block; padding: 7px 9px; font-size: inherit; color: #3C3C3D; text-shadow: 1px 1px 0 #FFFFFF; background: #ECECEC; white-space: nowrap; overflow: visible; cursor: pointer; text-decoration: none; border: 1px solid #CACACA; -webkit-border-radius: 2px; -moz-border-radius: 2px; -webkit-background-clip: padding-box; border-radius: 2px; outline: none; position: relative; zoom: 1; *display: inline; }
2 | .button.primary { font-weight: bold }
3 | .button:hover { color: #FFFFFF; border-color: #388AD4; text-decoration: none; text-shadow: -1px -1px 0 rgba(0,0,0,0.3); background-position: 0 -40px; background-color: #2D7DC5; }
4 | .button:active,
5 | .button.active { background-position: 0 -81px; border-color: #347BBA; background-color: #0F5EA2; color: #FFFFFF; text-shadow: none; }
6 | .button:active { top: 1px }
7 | .button.negative:hover { color: #FFFFFF; background-position: 0 -121px; background-color: #D84743; border-color: #911D1B; }
8 | .button.negative:active,
9 | .button.negative.active { background-position: 0 -161px; background-color: #A5211E; border-color: #911D1B; }
10 | .button.pill { -webkit-border-radius: 19px; -moz-border-radius: 19px; border-radius: 19px; padding: 6px 12px; }
11 | .button.left { -webkit-border-bottom-right-radius: 0px; -webkit-border-top-right-radius: 0px; -moz-border-radius-bottomright: 0px; -moz-border-radius-topright: 0px; border-bottom-right-radius: 0px; border-top-right-radius: 0px; margin-right: 0px; }
12 | .button.middle { margin-right: 0px; margin-left: 0px; -webkit-border-radius: 0px; -moz-border-radius: 0px; border-radius: 0px; border-left: none; }
13 | .button.right { -webkit-border-bottom-left-radius: 0px; -webkit-border-top-left-radius: 0px; -moz-border-radius-bottomleft: 0px; -moz-border-radius-topleft: 0px; border-top-left-radius: 0px; border-bottom-left-radius: 0px; margin-left: 0px; border-left: none;}
14 | .button.left:active,
15 | .button.middle:active,
16 | .button.right:active { top: 0px }
17 | .button.big { font-size: 16px; padding: 7px 16px; }
18 | .button span.icon { display: inline-block; width: 14px; height: 12px; margin: auto 7px auto auto; position: relative; top: 1px; background-image: url('/images/css3buttons_icons.png'); background-repeat: no-repeat; }
19 | .big.button span.icon { top: 0px }
20 | .button span.icon.book { background-position: 0 0 }
21 | .button:hover span.icon.book { background-position: 0 -15px }
22 | .button span.icon.calendar { background-position: 0 -30px }
23 | .button:hover span.icon.calendar { background-position: 0 -45px }
24 | .button span.icon.chat { background-position: 0 -60px }
25 | .button:hover span.icon.chat { background-position: 0 -75px }
26 | .button span.icon.check { background-position: 0 -90px }
27 | .button:hover span.icon.check { background-position: 0 -103px }
28 | .button span.icon.clock { background-position: 0 -116px }
29 | .button:hover span.icon.clock { background-position: 0 -131px }
30 | .button span.icon.cog { background-position: 0 -146px }
31 | .button:hover span.icon.cog { background-position: 0 -161px }
32 | .button span.icon.comment { background-position: 0 -176px }
33 | .button:hover span.icon.comment { background-position: 0 -190px }
34 | .button span.icon.cross { background-position: 0 -204px }
35 | .button:hover span.icon.cross { background-position: 0 -219px }
36 | .button span.icon.downarrow { background-position: 0 -234px }
37 | .button:hover span.icon.downarrow { background-position: 0 -249px }
38 | .button span.icon.fork { background-position: 0 -264px }
39 | .button:hover span.icon.fork { background-position: 0 -279px }
40 | .button span.icon.heart { background-position: 0 -294px }
41 | .button:hover span.icon.heart { background-position: 0 -308px }
42 | .button span.icon.home { background-position: 0 -322px }
43 | .button:hover span.icon.home { background-position: 0 -337px }
44 | .button span.icon.key { background-position: 0 -352px }
45 | .button:hover span.icon.key { background-position: 0 -367px }
46 | .button span.icon.leftarrow { background-position: 0 -382px }
47 | .button:hover span.icon.leftarrow { background-position: 0 -397px }
48 | .button span.icon.lock { background-position: 0 -412px }
49 | .button:hover span.icon.lock { background-position: 0 -427px }
50 | .button span.icon.loop { background-position: 0 -442px }
51 | .button:hover span.icon.loop { background-position: 0 -457px }
52 | .button span.icon.magnifier { background-position: 0 -472px }
53 | .button:hover span.icon.magnifier { background-position: 0 -487px }
54 | .button span.icon.mail { background-position: 0 -502px }
55 | .button:hover span.icon.mail { background-position: 0 -514px }
56 | .button span.icon.move { background-position: 0 -526px }
57 | .button:hover span.icon.move { background-position: 0 -541px }
58 | .button span.icon.pen { background-position: 0 -556px }
59 | .button:hover span.icon.pen { background-position: 0 -571px }
60 | .button span.icon.pin { background-position: 0 -586px }
61 | .button:hover span.icon.pin { background-position: 0 -601px }
62 | .button span.icon.plus { background-position: 0 -616px }
63 | .button:hover span.icon.plus { background-position: 0 -631px }
64 | .button span.icon.reload { background-position: 0 -646px }
65 | .button:hover span.icon.reload { background-position: 0 -660px }
66 | .button span.icon.rightarrow { background-position: 0 -674px }
67 | .button:hover span.icon.rightarrow { background-position: 0 -689px }
68 | .button span.icon.rss { background-position: 0 -704px }
69 | .button:hover span.icon.rss { background-position: 0 -719px }
70 | .button span.icon.tag { background-position: 0 -734px }
71 | .button:hover span.icon.tag { background-position: 0 -749px }
72 | .button span.icon.trash { background-position: 0 -764px }
73 | .button:hover span.icon.trash { background-position: 0 -779px }
74 | .button span.icon.unlock { background-position: 0 -794px }
75 | .button:hover span.icon.unlock { background-position: 0 -809px }
76 | .button span.icon.uparrow { background-position: 0 -824px }
77 | .button:hover span.icon.uparrow { background-position: 0 -839px }
78 | .button span.icon.user { background-position: 0 -854px }
79 | .button:hover span.icon.user { background-position: 0 -869px }
80 |
--------------------------------------------------------------------------------
/css/images/animated-overlay.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/animated-overlay.gif
--------------------------------------------------------------------------------
/css/images/ui-bg_flat_0_aaaaaa_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_flat_0_aaaaaa_40x100.png
--------------------------------------------------------------------------------
/css/images/ui-bg_flat_75_ffffff_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_flat_75_ffffff_40x100.png
--------------------------------------------------------------------------------
/css/images/ui-bg_glass_55_fbf9ee_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_glass_55_fbf9ee_1x400.png
--------------------------------------------------------------------------------
/css/images/ui-bg_glass_65_ffffff_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_glass_65_ffffff_1x400.png
--------------------------------------------------------------------------------
/css/images/ui-bg_glass_75_dadada_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_glass_75_dadada_1x400.png
--------------------------------------------------------------------------------
/css/images/ui-bg_glass_75_e6e6e6_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_glass_75_e6e6e6_1x400.png
--------------------------------------------------------------------------------
/css/images/ui-bg_glass_95_fef1ec_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_glass_95_fef1ec_1x400.png
--------------------------------------------------------------------------------
/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png
--------------------------------------------------------------------------------
/css/images/ui-icons_222222_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-icons_222222_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_2e83ff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-icons_2e83ff_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_454545_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-icons_454545_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_888888_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-icons_888888_256x240.png
--------------------------------------------------------------------------------
/css/images/ui-icons_cd0a0a_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/css/images/ui-icons_cd0a0a_256x240.png
--------------------------------------------------------------------------------
/css/jquery-ui.css:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.10.3 - 2013-05-03
2 | * http://jqueryui.com
3 | * Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
4 | * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
5 | /* Layout helpers
6 | ----------------------------------*/
7 | .ui-helper-hidden {
8 | display: none;
9 | }
10 | .ui-helper-hidden-accessible {
11 | border: 0;
12 | clip: rect(0 0 0 0);
13 | height: 1px;
14 | margin: -1px;
15 | overflow: hidden;
16 | padding: 0;
17 | position: absolute;
18 | width: 1px;
19 | }
20 | .ui-helper-reset {
21 | margin: 0;
22 | padding: 0;
23 | border: 0;
24 | outline: 0;
25 | line-height: 1.3;
26 | text-decoration: none;
27 | font-size: 100%;
28 | list-style: none;
29 | }
30 | .ui-helper-clearfix:before,
31 | .ui-helper-clearfix:after {
32 | content: "";
33 | display: table;
34 | border-collapse: collapse;
35 | }
36 | .ui-helper-clearfix:after {
37 | clear: both;
38 | }
39 | .ui-helper-clearfix {
40 | min-height: 0; /* support: IE7 */
41 | }
42 | .ui-helper-zfix {
43 | width: 100%;
44 | height: 100%;
45 | top: 0;
46 | left: 0;
47 | position: absolute;
48 | opacity: 0;
49 | filter:Alpha(Opacity=0);
50 | }
51 |
52 | .ui-front {
53 | z-index: 100;
54 | }
55 |
56 |
57 | /* Interaction Cues
58 | ----------------------------------*/
59 | .ui-state-disabled {
60 | cursor: default !important;
61 | }
62 |
63 |
64 | /* Icons
65 | ----------------------------------*/
66 |
67 | /* states and images */
68 | .ui-icon {
69 | display: block;
70 | text-indent: -99999px;
71 | overflow: hidden;
72 | background-repeat: no-repeat;
73 | }
74 |
75 |
76 | /* Misc visuals
77 | ----------------------------------*/
78 |
79 | /* Overlays */
80 | .ui-widget-overlay {
81 | position: fixed;
82 | top: 0;
83 | left: 0;
84 | width: 100%;
85 | height: 100%;
86 | }
87 |
88 | .ui-accordion .ui-accordion-header {
89 | display: block;
90 | cursor: pointer;
91 | position: relative;
92 | margin-top: 2px;
93 | padding: .5em .5em .5em .7em;
94 | min-height: 0; /* support: IE7 */
95 | }
96 | .ui-accordion .ui-accordion-icons {
97 | padding-left: 2.2em;
98 | }
99 | .ui-accordion .ui-accordion-noicons {
100 | padding-left: .7em;
101 | }
102 | .ui-accordion .ui-accordion-icons .ui-accordion-icons {
103 | padding-left: 2.2em;
104 | }
105 | .ui-accordion .ui-accordion-header .ui-accordion-header-icon {
106 | position: absolute;
107 | left: .5em;
108 | top: 50%;
109 | margin-top: -8px;
110 | }
111 | .ui-accordion .ui-accordion-content {
112 | padding: 1em 2.2em;
113 | border-top: 0;
114 | overflow: auto;
115 | }
116 |
117 | .ui-autocomplete {
118 | position: absolute;
119 | top: 0;
120 | left: 0;
121 | cursor: default;
122 | }
123 |
124 | .ui-button {
125 | display: inline-block;
126 | position: relative;
127 | padding: 0;
128 | line-height: normal;
129 | margin-right: .1em;
130 | cursor: pointer;
131 | vertical-align: middle;
132 | text-align: center;
133 | overflow: visible; /* removes extra width in IE */
134 | }
135 | .ui-button,
136 | .ui-button:link,
137 | .ui-button:visited,
138 | .ui-button:hover,
139 | .ui-button:active {
140 | text-decoration: none;
141 | }
142 | /* to make room for the icon, a width needs to be set here */
143 | .ui-button-icon-only {
144 | width: 2.2em;
145 | }
146 | /* button elements seem to need a little more width */
147 | button.ui-button-icon-only {
148 | width: 2.4em;
149 | }
150 | .ui-button-icons-only {
151 | width: 3.4em;
152 | }
153 | button.ui-button-icons-only {
154 | width: 3.7em;
155 | }
156 |
157 | /* button text element */
158 | .ui-button .ui-button-text {
159 | display: block;
160 | line-height: normal;
161 | }
162 | .ui-button-text-only .ui-button-text {
163 | padding: .4em 1em;
164 | }
165 | .ui-button-icon-only .ui-button-text,
166 | .ui-button-icons-only .ui-button-text {
167 | padding: .4em;
168 | text-indent: -9999999px;
169 | }
170 | .ui-button-text-icon-primary .ui-button-text,
171 | .ui-button-text-icons .ui-button-text {
172 | padding: .4em 1em .4em 2.1em;
173 | }
174 | .ui-button-text-icon-secondary .ui-button-text,
175 | .ui-button-text-icons .ui-button-text {
176 | padding: .4em 2.1em .4em 1em;
177 | }
178 | .ui-button-text-icons .ui-button-text {
179 | padding-left: 2.1em;
180 | padding-right: 2.1em;
181 | }
182 | /* no icon support for input elements, provide padding by default */
183 | input.ui-button {
184 | padding: .4em 1em;
185 | }
186 |
187 | /* button icon element(s) */
188 | .ui-button-icon-only .ui-icon,
189 | .ui-button-text-icon-primary .ui-icon,
190 | .ui-button-text-icon-secondary .ui-icon,
191 | .ui-button-text-icons .ui-icon,
192 | .ui-button-icons-only .ui-icon {
193 | position: absolute;
194 | top: 50%;
195 | margin-top: -8px;
196 | }
197 | .ui-button-icon-only .ui-icon {
198 | left: 50%;
199 | margin-left: -8px;
200 | }
201 | .ui-button-text-icon-primary .ui-button-icon-primary,
202 | .ui-button-text-icons .ui-button-icon-primary,
203 | .ui-button-icons-only .ui-button-icon-primary {
204 | left: .5em;
205 | }
206 | .ui-button-text-icon-secondary .ui-button-icon-secondary,
207 | .ui-button-text-icons .ui-button-icon-secondary,
208 | .ui-button-icons-only .ui-button-icon-secondary {
209 | right: .5em;
210 | }
211 |
212 | /* button sets */
213 | .ui-buttonset {
214 | margin-right: 7px;
215 | }
216 | .ui-buttonset .ui-button {
217 | margin-left: 0;
218 | margin-right: -.3em;
219 | }
220 |
221 | /* workarounds */
222 | /* reset extra padding in Firefox, see h5bp.com/l */
223 | input.ui-button::-moz-focus-inner,
224 | button.ui-button::-moz-focus-inner {
225 | border: 0;
226 | padding: 0;
227 | }
228 |
229 | .ui-datepicker {
230 | width: 17em;
231 | padding: .2em .2em 0;
232 | display: none;
233 | }
234 | .ui-datepicker .ui-datepicker-header {
235 | position: relative;
236 | padding: .2em 0;
237 | }
238 | .ui-datepicker .ui-datepicker-prev,
239 | .ui-datepicker .ui-datepicker-next {
240 | position: absolute;
241 | top: 2px;
242 | width: 1.8em;
243 | height: 1.8em;
244 | }
245 | .ui-datepicker .ui-datepicker-prev-hover,
246 | .ui-datepicker .ui-datepicker-next-hover {
247 | top: 1px;
248 | }
249 | .ui-datepicker .ui-datepicker-prev {
250 | left: 2px;
251 | }
252 | .ui-datepicker .ui-datepicker-next {
253 | right: 2px;
254 | }
255 | .ui-datepicker .ui-datepicker-prev-hover {
256 | left: 1px;
257 | }
258 | .ui-datepicker .ui-datepicker-next-hover {
259 | right: 1px;
260 | }
261 | .ui-datepicker .ui-datepicker-prev span,
262 | .ui-datepicker .ui-datepicker-next span {
263 | display: block;
264 | position: absolute;
265 | left: 50%;
266 | margin-left: -8px;
267 | top: 50%;
268 | margin-top: -8px;
269 | }
270 | .ui-datepicker .ui-datepicker-title {
271 | margin: 0 2.3em;
272 | line-height: 1.8em;
273 | text-align: center;
274 | }
275 | .ui-datepicker .ui-datepicker-title select {
276 | font-size: 1em;
277 | margin: 1px 0;
278 | }
279 | .ui-datepicker select.ui-datepicker-month-year {
280 | width: 100%;
281 | }
282 | .ui-datepicker select.ui-datepicker-month,
283 | .ui-datepicker select.ui-datepicker-year {
284 | width: 49%;
285 | }
286 | .ui-datepicker table {
287 | width: 100%;
288 | font-size: .9em;
289 | border-collapse: collapse;
290 | margin: 0 0 .4em;
291 | }
292 | .ui-datepicker th {
293 | padding: .7em .3em;
294 | text-align: center;
295 | font-weight: bold;
296 | border: 0;
297 | }
298 | .ui-datepicker td {
299 | border: 0;
300 | padding: 1px;
301 | }
302 | .ui-datepicker td span,
303 | .ui-datepicker td a {
304 | display: block;
305 | padding: .2em;
306 | text-align: right;
307 | text-decoration: none;
308 | }
309 | .ui-datepicker .ui-datepicker-buttonpane {
310 | background-image: none;
311 | margin: .7em 0 0 0;
312 | padding: 0 .2em;
313 | border-left: 0;
314 | border-right: 0;
315 | border-bottom: 0;
316 | }
317 | .ui-datepicker .ui-datepicker-buttonpane button {
318 | float: right;
319 | margin: .5em .2em .4em;
320 | cursor: pointer;
321 | padding: .2em .6em .3em .6em;
322 | width: auto;
323 | overflow: visible;
324 | }
325 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
326 | float: left;
327 | }
328 |
329 | /* with multiple calendars */
330 | .ui-datepicker.ui-datepicker-multi {
331 | width: auto;
332 | }
333 | .ui-datepicker-multi .ui-datepicker-group {
334 | float: left;
335 | }
336 | .ui-datepicker-multi .ui-datepicker-group table {
337 | width: 95%;
338 | margin: 0 auto .4em;
339 | }
340 | .ui-datepicker-multi-2 .ui-datepicker-group {
341 | width: 50%;
342 | }
343 | .ui-datepicker-multi-3 .ui-datepicker-group {
344 | width: 33.3%;
345 | }
346 | .ui-datepicker-multi-4 .ui-datepicker-group {
347 | width: 25%;
348 | }
349 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
350 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
351 | border-left-width: 0;
352 | }
353 | .ui-datepicker-multi .ui-datepicker-buttonpane {
354 | clear: left;
355 | }
356 | .ui-datepicker-row-break {
357 | clear: both;
358 | width: 100%;
359 | font-size: 0;
360 | }
361 |
362 | /* RTL support */
363 | .ui-datepicker-rtl {
364 | direction: rtl;
365 | }
366 | .ui-datepicker-rtl .ui-datepicker-prev {
367 | right: 2px;
368 | left: auto;
369 | }
370 | .ui-datepicker-rtl .ui-datepicker-next {
371 | left: 2px;
372 | right: auto;
373 | }
374 | .ui-datepicker-rtl .ui-datepicker-prev:hover {
375 | right: 1px;
376 | left: auto;
377 | }
378 | .ui-datepicker-rtl .ui-datepicker-next:hover {
379 | left: 1px;
380 | right: auto;
381 | }
382 | .ui-datepicker-rtl .ui-datepicker-buttonpane {
383 | clear: right;
384 | }
385 | .ui-datepicker-rtl .ui-datepicker-buttonpane button {
386 | float: left;
387 | }
388 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
389 | .ui-datepicker-rtl .ui-datepicker-group {
390 | float: right;
391 | }
392 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
393 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
394 | border-right-width: 0;
395 | border-left-width: 1px;
396 | }
397 |
398 | .ui-dialog {
399 | position: absolute;
400 | top: 0;
401 | left: 0;
402 | padding: .2em;
403 | outline: 0;
404 | }
405 | .ui-dialog .ui-dialog-titlebar {
406 | padding: .4em 1em;
407 | position: relative;
408 | }
409 | .ui-dialog .ui-dialog-title {
410 | float: left;
411 | margin: .1em 0;
412 | white-space: nowrap;
413 | width: 90%;
414 | overflow: hidden;
415 | text-overflow: ellipsis;
416 | }
417 | .ui-dialog .ui-dialog-titlebar-close {
418 | position: absolute;
419 | right: .3em;
420 | top: 50%;
421 | width: 21px;
422 | margin: -10px 0 0 0;
423 | padding: 1px;
424 | height: 20px;
425 | }
426 | .ui-dialog .ui-dialog-content {
427 | position: relative;
428 | border: 0;
429 | padding: .5em 1em;
430 | background: none;
431 | overflow: auto;
432 | }
433 | .ui-dialog .ui-dialog-buttonpane {
434 | text-align: left;
435 | border-width: 1px 0 0 0;
436 | background-image: none;
437 | margin-top: .5em;
438 | padding: .3em 1em .5em .4em;
439 | }
440 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
441 | float: right;
442 | }
443 | .ui-dialog .ui-dialog-buttonpane button {
444 | margin: .5em .4em .5em 0;
445 | cursor: pointer;
446 | }
447 | .ui-dialog .ui-resizable-se {
448 | width: 12px;
449 | height: 12px;
450 | right: -5px;
451 | bottom: -5px;
452 | background-position: 16px 16px;
453 | }
454 | .ui-draggable .ui-dialog-titlebar {
455 | cursor: move;
456 | }
457 |
458 | .ui-menu {
459 | list-style: none;
460 | padding: 2px;
461 | margin: 0;
462 | display: block;
463 | outline: none;
464 | }
465 | .ui-menu .ui-menu {
466 | margin-top: -3px;
467 | position: absolute;
468 | }
469 | .ui-menu .ui-menu-item {
470 | margin: 0;
471 | padding: 0;
472 | width: 100%;
473 | /* support: IE10, see #8844 */
474 | list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
475 | }
476 | .ui-menu .ui-menu-divider {
477 | margin: 5px -2px 5px -2px;
478 | height: 0;
479 | font-size: 0;
480 | line-height: 0;
481 | border-width: 1px 0 0 0;
482 | }
483 | .ui-menu .ui-menu-item a {
484 | text-decoration: none;
485 | display: block;
486 | padding: 2px .4em;
487 | line-height: 1.5;
488 | min-height: 0; /* support: IE7 */
489 | font-weight: normal;
490 | }
491 | .ui-menu .ui-menu-item a.ui-state-focus,
492 | .ui-menu .ui-menu-item a.ui-state-active {
493 | font-weight: normal;
494 | margin: -1px;
495 | }
496 |
497 | .ui-menu .ui-state-disabled {
498 | font-weight: normal;
499 | margin: .4em 0 .2em;
500 | line-height: 1.5;
501 | }
502 | .ui-menu .ui-state-disabled a {
503 | cursor: default;
504 | }
505 |
506 | /* icon support */
507 | .ui-menu-icons {
508 | position: relative;
509 | }
510 | .ui-menu-icons .ui-menu-item a {
511 | position: relative;
512 | padding-left: 2em;
513 | }
514 |
515 | /* left-aligned */
516 | .ui-menu .ui-icon {
517 | position: absolute;
518 | top: .2em;
519 | left: .2em;
520 | }
521 |
522 | /* right-aligned */
523 | .ui-menu .ui-menu-icon {
524 | position: static;
525 | float: right;
526 | }
527 |
528 | .ui-progressbar {
529 | height: 2em;
530 | text-align: left;
531 | overflow: hidden;
532 | }
533 | .ui-progressbar .ui-progressbar-value {
534 | margin: -1px;
535 | height: 100%;
536 | }
537 | .ui-progressbar .ui-progressbar-overlay {
538 | background: url("images/animated-overlay.gif");
539 | height: 100%;
540 | filter: alpha(opacity=25);
541 | opacity: 0.25;
542 | }
543 | .ui-progressbar-indeterminate .ui-progressbar-value {
544 | background-image: none;
545 | }
546 |
547 | .ui-resizable {
548 | position: relative;
549 | }
550 | .ui-resizable-handle {
551 | position: absolute;
552 | font-size: 0.1px;
553 | display: block;
554 | }
555 | .ui-resizable-disabled .ui-resizable-handle,
556 | .ui-resizable-autohide .ui-resizable-handle {
557 | display: none;
558 | }
559 | .ui-resizable-n {
560 | cursor: n-resize;
561 | height: 7px;
562 | width: 100%;
563 | top: -5px;
564 | left: 0;
565 | }
566 | .ui-resizable-s {
567 | cursor: s-resize;
568 | height: 7px;
569 | width: 100%;
570 | bottom: -5px;
571 | left: 0;
572 | }
573 | .ui-resizable-e {
574 | cursor: e-resize;
575 | width: 7px;
576 | right: -5px;
577 | top: 0;
578 | height: 100%;
579 | }
580 | .ui-resizable-w {
581 | cursor: w-resize;
582 | width: 7px;
583 | left: -5px;
584 | top: 0;
585 | height: 100%;
586 | }
587 | .ui-resizable-se {
588 | cursor: se-resize;
589 | width: 12px;
590 | height: 12px;
591 | right: 1px;
592 | bottom: 1px;
593 | }
594 | .ui-resizable-sw {
595 | cursor: sw-resize;
596 | width: 9px;
597 | height: 9px;
598 | left: -5px;
599 | bottom: -5px;
600 | }
601 | .ui-resizable-nw {
602 | cursor: nw-resize;
603 | width: 9px;
604 | height: 9px;
605 | left: -5px;
606 | top: -5px;
607 | }
608 | .ui-resizable-ne {
609 | cursor: ne-resize;
610 | width: 9px;
611 | height: 9px;
612 | right: -5px;
613 | top: -5px;
614 | }
615 |
616 | .ui-selectable-helper {
617 | position: absolute;
618 | z-index: 100;
619 | border: 1px dotted black;
620 | }
621 |
622 | .ui-slider {
623 | position: relative;
624 | text-align: left;
625 | }
626 | .ui-slider .ui-slider-handle {
627 | position: absolute;
628 | z-index: 2;
629 | width: 1.2em;
630 | height: 1.2em;
631 | cursor: default;
632 | }
633 | .ui-slider .ui-slider-range {
634 | position: absolute;
635 | z-index: 1;
636 | font-size: .7em;
637 | display: block;
638 | border: 0;
639 | background-position: 0 0;
640 | }
641 |
642 | /* For IE8 - See #6727 */
643 | .ui-slider.ui-state-disabled .ui-slider-handle,
644 | .ui-slider.ui-state-disabled .ui-slider-range {
645 | filter: inherit;
646 | }
647 |
648 | .ui-slider-horizontal {
649 | height: .8em;
650 | }
651 | .ui-slider-horizontal .ui-slider-handle {
652 | top: -.3em;
653 | margin-left: -.6em;
654 | }
655 | .ui-slider-horizontal .ui-slider-range {
656 | top: 0;
657 | height: 100%;
658 | }
659 | .ui-slider-horizontal .ui-slider-range-min {
660 | left: 0;
661 | }
662 | .ui-slider-horizontal .ui-slider-range-max {
663 | right: 0;
664 | }
665 |
666 | .ui-slider-vertical {
667 | width: .8em;
668 | height: 100px;
669 | }
670 | .ui-slider-vertical .ui-slider-handle {
671 | left: -.3em;
672 | margin-left: 0;
673 | margin-bottom: -.6em;
674 | }
675 | .ui-slider-vertical .ui-slider-range {
676 | left: 0;
677 | width: 100%;
678 | }
679 | .ui-slider-vertical .ui-slider-range-min {
680 | bottom: 0;
681 | }
682 | .ui-slider-vertical .ui-slider-range-max {
683 | top: 0;
684 | }
685 |
686 | .ui-spinner {
687 | position: relative;
688 | display: inline-block;
689 | overflow: hidden;
690 | padding: 0;
691 | vertical-align: middle;
692 | }
693 | .ui-spinner-input {
694 | border: none;
695 | background: none;
696 | color: inherit;
697 | padding: 0;
698 | margin: .2em 0;
699 | vertical-align: middle;
700 | margin-left: .4em;
701 | margin-right: 22px;
702 | }
703 | .ui-spinner-button {
704 | width: 16px;
705 | height: 50%;
706 | font-size: .5em;
707 | padding: 0;
708 | margin: 0;
709 | text-align: center;
710 | position: absolute;
711 | cursor: default;
712 | display: block;
713 | overflow: hidden;
714 | right: 0;
715 | }
716 | /* more specificity required here to overide default borders */
717 | .ui-spinner a.ui-spinner-button {
718 | border-top: none;
719 | border-bottom: none;
720 | border-right: none;
721 | }
722 | /* vertical centre icon */
723 | .ui-spinner .ui-icon {
724 | position: absolute;
725 | margin-top: -8px;
726 | top: 50%;
727 | left: 0;
728 | }
729 | .ui-spinner-up {
730 | top: 0;
731 | }
732 | .ui-spinner-down {
733 | bottom: 0;
734 | }
735 |
736 | /* TR overrides */
737 | .ui-spinner .ui-icon-triangle-1-s {
738 | /* need to fix icons sprite */
739 | background-position: -65px -16px;
740 | }
741 |
742 | .ui-tabs {
743 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
744 | padding: .2em;
745 | }
746 | .ui-tabs .ui-tabs-nav {
747 | margin: 0;
748 | padding: .2em .2em 0;
749 | }
750 | .ui-tabs .ui-tabs-nav li {
751 | list-style: none;
752 | float: left;
753 | position: relative;
754 | top: 0;
755 | margin: 1px .2em 0 0;
756 | border-bottom-width: 0;
757 | padding: 0;
758 | white-space: nowrap;
759 | }
760 | .ui-tabs .ui-tabs-nav li a {
761 | float: left;
762 | padding: .5em 1em;
763 | text-decoration: none;
764 | }
765 | .ui-tabs .ui-tabs-nav li.ui-tabs-active {
766 | margin-bottom: -1px;
767 | padding-bottom: 1px;
768 | }
769 | .ui-tabs .ui-tabs-nav li.ui-tabs-active a,
770 | .ui-tabs .ui-tabs-nav li.ui-state-disabled a,
771 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading a {
772 | cursor: text;
773 | }
774 | .ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
775 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
776 | cursor: pointer;
777 | }
778 | .ui-tabs .ui-tabs-panel {
779 | display: block;
780 | border-width: 0;
781 | padding: 1em 1.4em;
782 | background: none;
783 | }
784 |
785 | .ui-tooltip {
786 | padding: 8px;
787 | position: absolute;
788 | z-index: 9999;
789 | max-width: 300px;
790 | -webkit-box-shadow: 0 0 5px #aaa;
791 | box-shadow: 0 0 5px #aaa;
792 | }
793 | body .ui-tooltip {
794 | border-width: 2px;
795 | }
796 |
797 | /* Component containers
798 | ----------------------------------*/
799 | .ui-widget {
800 | font-family: Verdana,Arial,sans-serif/*{ffDefault}*/;
801 | font-size: 1.1em/*{fsDefault}*/;
802 | }
803 | .ui-widget .ui-widget {
804 | font-size: 1em;
805 | }
806 | .ui-widget input,
807 | .ui-widget select,
808 | .ui-widget textarea,
809 | .ui-widget button {
810 | font-family: Verdana,Arial,sans-serif/*{ffDefault}*/;
811 | font-size: 1em;
812 | }
813 | .ui-widget-content {
814 | border: 1px solid #aaaaaa/*{borderColorContent}*/;
815 | background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/;
816 | color: #222222/*{fcContent}*/;
817 | }
818 | .ui-widget-content a {
819 | color: #222222/*{fcContent}*/;
820 | }
821 | .ui-widget-header {
822 | border: 1px solid #aaaaaa/*{borderColorHeader}*/;
823 | background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/;
824 | color: #222222/*{fcHeader}*/;
825 | font-weight: bold;
826 | }
827 | .ui-widget-header a {
828 | color: #222222/*{fcHeader}*/;
829 | }
830 |
831 | /* Interaction states
832 | ----------------------------------*/
833 | .ui-state-default,
834 | .ui-widget-content .ui-state-default,
835 | .ui-widget-header .ui-state-default {
836 | border: 1px solid #d3d3d3/*{borderColorDefault}*/;
837 | background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/;
838 | font-weight: normal/*{fwDefault}*/;
839 | color: #555555/*{fcDefault}*/;
840 | }
841 | .ui-state-default a,
842 | .ui-state-default a:link,
843 | .ui-state-default a:visited {
844 | color: #555555/*{fcDefault}*/;
845 | text-decoration: none;
846 | }
847 | .ui-state-hover,
848 | .ui-widget-content .ui-state-hover,
849 | .ui-widget-header .ui-state-hover,
850 | .ui-state-focus,
851 | .ui-widget-content .ui-state-focus,
852 | .ui-widget-header .ui-state-focus {
853 | border: 1px solid #999999/*{borderColorHover}*/;
854 | background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/;
855 | font-weight: normal/*{fwDefault}*/;
856 | color: #212121/*{fcHover}*/;
857 | }
858 | .ui-state-hover a,
859 | .ui-state-hover a:hover,
860 | .ui-state-hover a:link,
861 | .ui-state-hover a:visited {
862 | color: #212121/*{fcHover}*/;
863 | text-decoration: none;
864 | }
865 | .ui-state-active,
866 | .ui-widget-content .ui-state-active,
867 | .ui-widget-header .ui-state-active {
868 | border: 1px solid #aaaaaa/*{borderColorActive}*/;
869 | background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/;
870 | font-weight: normal/*{fwDefault}*/;
871 | color: #212121/*{fcActive}*/;
872 | }
873 | .ui-state-active a,
874 | .ui-state-active a:link,
875 | .ui-state-active a:visited {
876 | color: #212121/*{fcActive}*/;
877 | text-decoration: none;
878 | }
879 |
880 | /* Interaction Cues
881 | ----------------------------------*/
882 | .ui-state-highlight,
883 | .ui-widget-content .ui-state-highlight,
884 | .ui-widget-header .ui-state-highlight {
885 | border: 1px solid #fcefa1/*{borderColorHighlight}*/;
886 | background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/;
887 | color: #363636/*{fcHighlight}*/;
888 | }
889 | .ui-state-highlight a,
890 | .ui-widget-content .ui-state-highlight a,
891 | .ui-widget-header .ui-state-highlight a {
892 | color: #363636/*{fcHighlight}*/;
893 | }
894 | .ui-state-error,
895 | .ui-widget-content .ui-state-error,
896 | .ui-widget-header .ui-state-error {
897 | border: 1px solid #cd0a0a/*{borderColorError}*/;
898 | background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/;
899 | color: #cd0a0a/*{fcError}*/;
900 | }
901 | .ui-state-error a,
902 | .ui-widget-content .ui-state-error a,
903 | .ui-widget-header .ui-state-error a {
904 | color: #cd0a0a/*{fcError}*/;
905 | }
906 | .ui-state-error-text,
907 | .ui-widget-content .ui-state-error-text,
908 | .ui-widget-header .ui-state-error-text {
909 | color: #cd0a0a/*{fcError}*/;
910 | }
911 | .ui-priority-primary,
912 | .ui-widget-content .ui-priority-primary,
913 | .ui-widget-header .ui-priority-primary {
914 | font-weight: bold;
915 | }
916 | .ui-priority-secondary,
917 | .ui-widget-content .ui-priority-secondary,
918 | .ui-widget-header .ui-priority-secondary {
919 | opacity: .7;
920 | filter:Alpha(Opacity=70);
921 | font-weight: normal;
922 | }
923 | .ui-state-disabled,
924 | .ui-widget-content .ui-state-disabled,
925 | .ui-widget-header .ui-state-disabled {
926 | opacity: .35;
927 | filter:Alpha(Opacity=35);
928 | background-image: none;
929 | }
930 | .ui-state-disabled .ui-icon {
931 | filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
932 | }
933 |
934 | /* Icons
935 | ----------------------------------*/
936 |
937 | /* states and images */
938 | .ui-icon {
939 | width: 16px;
940 | height: 16px;
941 | }
942 | .ui-icon,
943 | .ui-widget-content .ui-icon {
944 | background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/;
945 | }
946 | .ui-widget-header .ui-icon {
947 | background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/;
948 | }
949 | .ui-state-default .ui-icon {
950 | background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/;
951 | }
952 | .ui-state-hover .ui-icon,
953 | .ui-state-focus .ui-icon {
954 | background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/;
955 | }
956 | .ui-state-active .ui-icon {
957 | background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/;
958 | }
959 | .ui-state-highlight .ui-icon {
960 | background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/;
961 | }
962 | .ui-state-error .ui-icon,
963 | .ui-state-error-text .ui-icon {
964 | background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/;
965 | }
966 |
967 | /* positioning */
968 | .ui-icon-blank { background-position: 16px 16px; }
969 | .ui-icon-carat-1-n { background-position: 0 0; }
970 | .ui-icon-carat-1-ne { background-position: -16px 0; }
971 | .ui-icon-carat-1-e { background-position: -32px 0; }
972 | .ui-icon-carat-1-se { background-position: -48px 0; }
973 | .ui-icon-carat-1-s { background-position: -64px 0; }
974 | .ui-icon-carat-1-sw { background-position: -80px 0; }
975 | .ui-icon-carat-1-w { background-position: -96px 0; }
976 | .ui-icon-carat-1-nw { background-position: -112px 0; }
977 | .ui-icon-carat-2-n-s { background-position: -128px 0; }
978 | .ui-icon-carat-2-e-w { background-position: -144px 0; }
979 | .ui-icon-triangle-1-n { background-position: 0 -16px; }
980 | .ui-icon-triangle-1-ne { background-position: -16px -16px; }
981 | .ui-icon-triangle-1-e { background-position: -32px -16px; }
982 | .ui-icon-triangle-1-se { background-position: -48px -16px; }
983 | .ui-icon-triangle-1-s { background-position: -64px -16px; }
984 | .ui-icon-triangle-1-sw { background-position: -80px -16px; }
985 | .ui-icon-triangle-1-w { background-position: -96px -16px; }
986 | .ui-icon-triangle-1-nw { background-position: -112px -16px; }
987 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
988 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
989 | .ui-icon-arrow-1-n { background-position: 0 -32px; }
990 | .ui-icon-arrow-1-ne { background-position: -16px -32px; }
991 | .ui-icon-arrow-1-e { background-position: -32px -32px; }
992 | .ui-icon-arrow-1-se { background-position: -48px -32px; }
993 | .ui-icon-arrow-1-s { background-position: -64px -32px; }
994 | .ui-icon-arrow-1-sw { background-position: -80px -32px; }
995 | .ui-icon-arrow-1-w { background-position: -96px -32px; }
996 | .ui-icon-arrow-1-nw { background-position: -112px -32px; }
997 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
998 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
999 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
1000 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
1001 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
1002 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
1003 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
1004 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
1005 | .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
1006 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
1007 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
1008 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
1009 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
1010 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
1011 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
1012 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
1013 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
1014 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
1015 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
1016 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
1017 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
1018 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
1019 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
1020 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
1021 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
1022 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
1023 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
1024 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
1025 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
1026 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
1027 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
1028 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
1029 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
1030 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
1031 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
1032 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
1033 | .ui-icon-arrow-4 { background-position: 0 -80px; }
1034 | .ui-icon-arrow-4-diag { background-position: -16px -80px; }
1035 | .ui-icon-extlink { background-position: -32px -80px; }
1036 | .ui-icon-newwin { background-position: -48px -80px; }
1037 | .ui-icon-refresh { background-position: -64px -80px; }
1038 | .ui-icon-shuffle { background-position: -80px -80px; }
1039 | .ui-icon-transfer-e-w { background-position: -96px -80px; }
1040 | .ui-icon-transferthick-e-w { background-position: -112px -80px; }
1041 | .ui-icon-folder-collapsed { background-position: 0 -96px; }
1042 | .ui-icon-folder-open { background-position: -16px -96px; }
1043 | .ui-icon-document { background-position: -32px -96px; }
1044 | .ui-icon-document-b { background-position: -48px -96px; }
1045 | .ui-icon-note { background-position: -64px -96px; }
1046 | .ui-icon-mail-closed { background-position: -80px -96px; }
1047 | .ui-icon-mail-open { background-position: -96px -96px; }
1048 | .ui-icon-suitcase { background-position: -112px -96px; }
1049 | .ui-icon-comment { background-position: -128px -96px; }
1050 | .ui-icon-person { background-position: -144px -96px; }
1051 | .ui-icon-print { background-position: -160px -96px; }
1052 | .ui-icon-trash { background-position: -176px -96px; }
1053 | .ui-icon-locked { background-position: -192px -96px; }
1054 | .ui-icon-unlocked { background-position: -208px -96px; }
1055 | .ui-icon-bookmark { background-position: -224px -96px; }
1056 | .ui-icon-tag { background-position: -240px -96px; }
1057 | .ui-icon-home { background-position: 0 -112px; }
1058 | .ui-icon-flag { background-position: -16px -112px; }
1059 | .ui-icon-calendar { background-position: -32px -112px; }
1060 | .ui-icon-cart { background-position: -48px -112px; }
1061 | .ui-icon-pencil { background-position: -64px -112px; }
1062 | .ui-icon-clock { background-position: -80px -112px; }
1063 | .ui-icon-disk { background-position: -96px -112px; }
1064 | .ui-icon-calculator { background-position: -112px -112px; }
1065 | .ui-icon-zoomin { background-position: -128px -112px; }
1066 | .ui-icon-zoomout { background-position: -144px -112px; }
1067 | .ui-icon-search { background-position: -160px -112px; }
1068 | .ui-icon-wrench { background-position: -176px -112px; }
1069 | .ui-icon-gear { background-position: -192px -112px; }
1070 | .ui-icon-heart { background-position: -208px -112px; }
1071 | .ui-icon-star { background-position: -224px -112px; }
1072 | .ui-icon-link { background-position: -240px -112px; }
1073 | .ui-icon-cancel { background-position: 0 -128px; }
1074 | .ui-icon-plus { background-position: -16px -128px; }
1075 | .ui-icon-plusthick { background-position: -32px -128px; }
1076 | .ui-icon-minus { background-position: -48px -128px; }
1077 | .ui-icon-minusthick { background-position: -64px -128px; }
1078 | .ui-icon-close { background-position: -80px -128px; }
1079 | .ui-icon-closethick { background-position: -96px -128px; }
1080 | .ui-icon-key { background-position: -112px -128px; }
1081 | .ui-icon-lightbulb { background-position: -128px -128px; }
1082 | .ui-icon-scissors { background-position: -144px -128px; }
1083 | .ui-icon-clipboard { background-position: -160px -128px; }
1084 | .ui-icon-copy { background-position: -176px -128px; }
1085 | .ui-icon-contact { background-position: -192px -128px; }
1086 | .ui-icon-image { background-position: -208px -128px; }
1087 | .ui-icon-video { background-position: -224px -128px; }
1088 | .ui-icon-script { background-position: -240px -128px; }
1089 | .ui-icon-alert { background-position: 0 -144px; }
1090 | .ui-icon-info { background-position: -16px -144px; }
1091 | .ui-icon-notice { background-position: -32px -144px; }
1092 | .ui-icon-help { background-position: -48px -144px; }
1093 | .ui-icon-check { background-position: -64px -144px; }
1094 | .ui-icon-bullet { background-position: -80px -144px; }
1095 | .ui-icon-radio-on { background-position: -96px -144px; }
1096 | .ui-icon-radio-off { background-position: -112px -144px; }
1097 | .ui-icon-pin-w { background-position: -128px -144px; }
1098 | .ui-icon-pin-s { background-position: -144px -144px; }
1099 | .ui-icon-play { background-position: 0 -160px; }
1100 | .ui-icon-pause { background-position: -16px -160px; }
1101 | .ui-icon-seek-next { background-position: -32px -160px; }
1102 | .ui-icon-seek-prev { background-position: -48px -160px; }
1103 | .ui-icon-seek-end { background-position: -64px -160px; }
1104 | .ui-icon-seek-start { background-position: -80px -160px; }
1105 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
1106 | .ui-icon-seek-first { background-position: -80px -160px; }
1107 | .ui-icon-stop { background-position: -96px -160px; }
1108 | .ui-icon-eject { background-position: -112px -160px; }
1109 | .ui-icon-volume-off { background-position: -128px -160px; }
1110 | .ui-icon-volume-on { background-position: -144px -160px; }
1111 | .ui-icon-power { background-position: 0 -176px; }
1112 | .ui-icon-signal-diag { background-position: -16px -176px; }
1113 | .ui-icon-signal { background-position: -32px -176px; }
1114 | .ui-icon-battery-0 { background-position: -48px -176px; }
1115 | .ui-icon-battery-1 { background-position: -64px -176px; }
1116 | .ui-icon-battery-2 { background-position: -80px -176px; }
1117 | .ui-icon-battery-3 { background-position: -96px -176px; }
1118 | .ui-icon-circle-plus { background-position: 0 -192px; }
1119 | .ui-icon-circle-minus { background-position: -16px -192px; }
1120 | .ui-icon-circle-close { background-position: -32px -192px; }
1121 | .ui-icon-circle-triangle-e { background-position: -48px -192px; }
1122 | .ui-icon-circle-triangle-s { background-position: -64px -192px; }
1123 | .ui-icon-circle-triangle-w { background-position: -80px -192px; }
1124 | .ui-icon-circle-triangle-n { background-position: -96px -192px; }
1125 | .ui-icon-circle-arrow-e { background-position: -112px -192px; }
1126 | .ui-icon-circle-arrow-s { background-position: -128px -192px; }
1127 | .ui-icon-circle-arrow-w { background-position: -144px -192px; }
1128 | .ui-icon-circle-arrow-n { background-position: -160px -192px; }
1129 | .ui-icon-circle-zoomin { background-position: -176px -192px; }
1130 | .ui-icon-circle-zoomout { background-position: -192px -192px; }
1131 | .ui-icon-circle-check { background-position: -208px -192px; }
1132 | .ui-icon-circlesmall-plus { background-position: 0 -208px; }
1133 | .ui-icon-circlesmall-minus { background-position: -16px -208px; }
1134 | .ui-icon-circlesmall-close { background-position: -32px -208px; }
1135 | .ui-icon-squaresmall-plus { background-position: -48px -208px; }
1136 | .ui-icon-squaresmall-minus { background-position: -64px -208px; }
1137 | .ui-icon-squaresmall-close { background-position: -80px -208px; }
1138 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
1139 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
1140 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
1141 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
1142 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
1143 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
1144 |
1145 |
1146 | /* Misc visuals
1147 | ----------------------------------*/
1148 |
1149 | /* Corner radius */
1150 | .ui-corner-all,
1151 | .ui-corner-top,
1152 | .ui-corner-left,
1153 | .ui-corner-tl {
1154 | border-top-left-radius: 4px/*{cornerRadius}*/;
1155 | }
1156 | .ui-corner-all,
1157 | .ui-corner-top,
1158 | .ui-corner-right,
1159 | .ui-corner-tr {
1160 | border-top-right-radius: 4px/*{cornerRadius}*/;
1161 | }
1162 | .ui-corner-all,
1163 | .ui-corner-bottom,
1164 | .ui-corner-left,
1165 | .ui-corner-bl {
1166 | border-bottom-left-radius: 4px/*{cornerRadius}*/;
1167 | }
1168 | .ui-corner-all,
1169 | .ui-corner-bottom,
1170 | .ui-corner-right,
1171 | .ui-corner-br {
1172 | border-bottom-right-radius: 4px/*{cornerRadius}*/;
1173 | }
1174 |
1175 | /* Overlays */
1176 | .ui-widget-overlay {
1177 | background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/;
1178 | opacity: .3/*{opacityOverlay}*/;
1179 | filter: Alpha(Opacity=30)/*{opacityFilterOverlay}*/;
1180 | }
1181 | .ui-widget-shadow {
1182 | margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/;
1183 | padding: 8px/*{thicknessShadow}*/;
1184 | background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/;
1185 | opacity: .3/*{opacityShadow}*/;
1186 | filter: Alpha(Opacity=30)/*{opacityFilterShadow}*/;
1187 | border-radius: 8px/*{cornerRadiusShadow}*/;
1188 | }
1189 |
--------------------------------------------------------------------------------
/css/osv.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | background: #000000;
5 | color: #000;
6 | font-family: sans-serif;
7 | font-size: 11px;
8 | line-height: 15px;
9 | height: 100%;
10 | overflow: hidden;
11 | }
12 |
13 | .ui {
14 | position: absolute;
15 | top: 0;
16 | }
17 |
18 | .mapcontainer {
19 | position: absolute;
20 | padding: 5px;
21 | top: 35px;
22 | bottom: 0;
23 | left: 0;
24 | right: 0;
25 | }
26 | .map {
27 | position: absolute;
28 | left: 5px;right: 5px;top: 5px; bottom: 40px;
29 |
30 | padding: 5px;
31 | }
32 | .mapsearch {
33 | position: absolute;
34 | bottom: 10px;
35 | left: 5px;
36 | right: 5px;
37 | }
38 |
39 | .mapprogress {
40 | position: absolute;
41 | top: 50%;
42 | left: 20px;
43 | right: 20px;
44 | }
45 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Oculus Google Street Viewer
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 |
43 |
48 |
49 |
Settings
50 |
53 |
56 |
57 |
58 |
59 | - Rotation with arrow keys
60 | - Move to the adjacent location: doubleclick or double-press ctrl or gamepad button
61 | - Click on the minimap to change location
62 | - Double-press space to toggle dialogs
63 | - Websocket expects a quaternion as array of 4 elements representing the camera rotation. (e.g. [0.1,0,0,0.9])
64 | - You can use URL parameters:
65 |
66 | - lat,lng :latitude and longitude
67 | - q : image quality [1-4]
68 | - s : show settings [true/false]
69 | - example
70 |
71 |
72 |
73 |
74 |
75 |
76 |
Special thanks to:
77 |
91 |
92 |
93 |
94 |
95 |
96 |
Your browser does not support WebVR.
97 |
Use the old version or use a WebVR compatible version of Chrome or Firefox
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/js/GSVPanoDepth.js:
--------------------------------------------------------------------------------
1 | var GSVPANO = GSVPANO || {};
2 | GSVPANO.PanoDepthLoader = function (parameters) {
3 |
4 | 'use strict';
5 |
6 | var _parameters = parameters || {},
7 | onDepthLoad = null;
8 |
9 | this.load = function(panoId) {
10 | var self = this,
11 | url;
12 |
13 | url = "http://maps.google.com/cbk?output=json&cb_client=maps_sv&v=4&dm=1&pm=1&ph=1&hl=en&panoid=" + panoId;
14 |
15 | $.ajax({
16 | url: url,
17 | dataType: 'jsonp'
18 | })
19 | .done(function(data, textStatus, xhr) {
20 | var decoded, depthMap;
21 |
22 | try {
23 | decoded = self.decode(data.model.depth_map);
24 | depthMap = self.parse(decoded);
25 | } catch(e) {
26 | console.error("Error loading depth map for pano " + panoId + "\n" + e.message + "\nAt " + e.filename + "(" + e.lineNumber + ")");
27 | depthMap = self.createEmptyDepthMap();
28 | }
29 | if(self.onDepthLoad) {
30 | self.depthMap = depthMap;
31 | self.onDepthLoad();
32 | }
33 | })
34 | .fail(function(xhr, textStatus, errorThrown) {
35 | console.error("Request failed: " + url + "\n" + textStatus + "\n" + errorThrown);
36 | var depthMap = self.createEmptyDepthMap();
37 | if(self.onDepthLoad) {
38 | self.depthMap = depthMap;
39 | self.onDepthLoad();
40 | }
41 | })
42 | }
43 |
44 | this.decode = function(rawDepthMap) {
45 | var self = this,
46 | i,
47 | compressedDepthMapData,
48 | depthMap,
49 | decompressedDepthMap;
50 |
51 | // Append '=' in order to make the length of the array a multiple of 4
52 | while(rawDepthMap.length %4 != 0)
53 | rawDepthMap += '=';
54 |
55 | // Replace '-' by '+' and '_' by '/'
56 | rawDepthMap = rawDepthMap.replace(/-/g,'+');
57 | rawDepthMap = rawDepthMap.replace(/_/g,'/');
58 |
59 | // Decode and decompress data
60 | compressedDepthMapData = $.base64.decode(rawDepthMap);
61 | decompressedDepthMap = zpipe.inflate(compressedDepthMapData);
62 |
63 | // Convert output of decompressor to Uint8Array
64 | depthMap = new Uint8Array(decompressedDepthMap.length);
65 | for(i=0; i 0) {
130 | plane = planes[planeIdx];
131 |
132 | t = plane.d / (v[0]*plane.n[0] + v[1]*plane.n[1] + v[2]*plane.n[2]);
133 | v[0] *= t;
134 | v[1] *= t;
135 | v[2] *= t;
136 | depthMap[y*w + (w-x-1)] = Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
137 | } else {
138 | depthMap[y*w + (w-x-1)] = 9999999999999999999.;
139 | }
140 | }
141 | }
142 |
143 | return {
144 | width: w,
145 | height: h,
146 | depthMap: depthMap
147 | };
148 | }
149 |
150 | this.parse = function(depthMap) {
151 | var self = this,
152 | depthMapData,
153 | header,
154 | data,
155 | depthMap;
156 |
157 | depthMapData = new DataView(depthMap.buffer);
158 | header = self.parseHeader(depthMapData);
159 | data = self.parsePlanes(header, depthMapData);
160 | depthMap = self.computeDepthMap(header, data.indices, data.planes);
161 |
162 | return depthMap;
163 | }
164 |
165 | this.createEmptyDepthMap = function() {
166 | var depthMap = {
167 | width: 512,
168 | height: 256,
169 | depthMap: new Float32Array(512*256)
170 | };
171 | for(var i=0; i<512*256; ++i)
172 | depthMap.depthMap[i] = 9999999999999999999.;
173 | return depthMap;
174 | }
175 | };
176 |
--------------------------------------------------------------------------------
/js/GSVPano_o.js:
--------------------------------------------------------------------------------
1 | /*
2 | GSVPano.js - Google Street View Panorama lib
3 | http://www.clicktorelease.com/code/street/
4 |
5 | License
6 |
7 | MIT licensed
8 |
9 | Copyright (C) 2012 Jaume Sanchez Elias, http://www.clicktorelease.com
10 |
11 | Modified version by troffmo5 (lsiciliano@web.de)
12 | */
13 | var GSVPANO = GSVPANO || {};
14 | GSVPANO.PanoLoader = function (parameters) {
15 |
16 | 'use strict';
17 |
18 | var _parameters = parameters || {},
19 | _location,
20 | _zoom,
21 | _panoId,
22 | _panoClient = new google.maps.StreetViewService(),
23 | _count = 0,
24 | _total = 0,
25 | _canvas = document.createElement('canvas'),
26 | _ctx = _canvas.getContext('2d'),
27 | rotation = 0,
28 | copyright = '',
29 | links = [],
30 | loading = false,
31 | onSizeChange = null,
32 | onPanoramaLoad = null;
33 |
34 | this.setProgress = function (p) {
35 | if (this.onProgress) {
36 | this.onProgress(p);
37 | }
38 | };
39 |
40 | this.throwError = function (message) {
41 | if (this.onError) {
42 | this.onError(message);
43 | } else {
44 | console.error(message);
45 | }
46 | };
47 |
48 | this.adaptTextureToZoom = function () {
49 | var w = 416 * Math.pow(2, _zoom),
50 | h = (416 * Math.pow(2, _zoom - 1));
51 | _canvas.width = w;
52 | _canvas.height = h;
53 | _ctx.translate( _canvas.width, 0);
54 | _ctx.scale(-1, 1);
55 | };
56 |
57 | this.composeFromTile = function (x, y, texture) {
58 |
59 | _ctx.drawImage(texture, x * 512, y * 512);
60 | _count++;
61 |
62 | var p = Math.round(_count * 100 / _total);
63 | this.setProgress(p);
64 |
65 | if (_count === _total) {
66 | this.canvas = _canvas;
67 | this.loading = false;
68 | if (this.onPanoramaLoad) {
69 | this.onPanoramaLoad();
70 | }
71 | }
72 |
73 | };
74 |
75 | this.composePanorama = function (cache) {
76 | this.setProgress(0);
77 |
78 | var w = Math.pow(2, _zoom ),
79 | h = Math.pow(2, _zoom - 1),
80 | self = this,
81 | url,
82 | x, y;
83 | if (_zoom == 3) w-=1;
84 | if (_zoom == 4) { w -= 3; h-=1;}
85 |
86 | _count = 0;
87 | _total = w * h;
88 |
89 | for( y = 0; y < h; y++) {
90 | for( x = 0; x < w; x++) {
91 | url = 'http://maps.google.com/cbk?output=tile&panoid=' + _panoId + '&zoom=' + _zoom + '&x=' + x + '&y=' + y;
92 | if (!cache) url += '&' + Date.now();
93 | (function (x, y) {
94 | var img = new Image();
95 | img.addEventListener('load', function () {
96 | self.composeFromTile(x, y, this);
97 | });
98 | img.crossOrigin = '';
99 | img.src = url;
100 | })(x, y);
101 | }
102 | }
103 | };
104 |
105 | this.loadCB = function (result, status, location, cache) {
106 | var self = this;
107 | if (status === google.maps.StreetViewStatus.OK) {
108 | if( self.onPanoramaData ) self.onPanoramaData( result );
109 | var h = google.maps.geometry.spherical.computeHeading(location, result.location.latLng);
110 |
111 | rotation = (result.tiles.centerHeading - h) * Math.PI / 180.0;
112 | copyright = result.copyright;
113 | self.copyright = result.copyright;
114 | self.links = result.links;
115 | self.heading = result.tiles.centerHeading;
116 | _panoId = result.location.pano;
117 | self.location = result.location;
118 | self.composePanorama(cache);
119 | } else {
120 | if( self.onNoPanoramaData ) self.onNoPanoramaData( status );
121 | self.loading = false;
122 | self.throwError('Could not retrieve panorama for the following reason: ' + status);
123 | }
124 | },
125 |
126 | this.load = function (location, cache) {
127 | var self = this;
128 | if (self.loading) return;
129 | self.loading = true;
130 | cache = cache || true;
131 | if ((typeof location) === 'string') {
132 | _panoClient.getPanoramaById(location, function(result, status){self.loadCB(result, status, location, cache)})
133 | }
134 | else {
135 | _panoClient.getPanoramaByLocation(location, 50, function(result, status){self.loadCB(result, status, location, cache);})
136 | }
137 | };
138 |
139 | this.setZoom = function( z ) {
140 | _zoom = z;
141 | this.adaptTextureToZoom();
142 | };
143 |
144 | this.setZoom( _parameters.zoom || 1 );
145 |
146 | };
--------------------------------------------------------------------------------
/js/RequestAnimationFrame.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Provides requestAnimationFrame in a cross browser way.
3 | * http://paulirish.com/2011/requestanimationframe-for-smart-animating/
4 | */
5 |
6 | if ( !window.requestAnimationFrame ) {
7 |
8 | window.requestAnimationFrame = ( function() {
9 |
10 | return window.webkitRequestAnimationFrame ||
11 | window.mozRequestAnimationFrame ||
12 | window.oRequestAnimationFrame ||
13 | window.msRequestAnimationFrame ||
14 | function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
15 |
16 | window.setTimeout( callback, 1000 / 60 );
17 |
18 | };
19 |
20 | } )();
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/js/VRControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | */
5 |
6 | THREE.VRControls = function ( object, callback ) {
7 |
8 | var scope = this;
9 |
10 | // Allow for multiple VR input devices.
11 | var vrInputs = [];
12 |
13 | var onVRDevices = function ( devices ) {
14 |
15 | for ( var i = 0; i < devices.length; i ++ ) {
16 |
17 | var device = devices[ i ];
18 |
19 | if ( device instanceof PositionSensorVRDevice ) {
20 |
21 | vrInputs.push( devices[ i ] );
22 |
23 | }
24 |
25 | }
26 |
27 | if ( callback !== undefined ) {
28 |
29 | callback( 'HMD not available' );
30 |
31 | }
32 |
33 | };
34 |
35 | if ( navigator.getVRDevices !== undefined ) {
36 |
37 | navigator.getVRDevices().then( onVRDevices );
38 |
39 | } else if ( callback !== undefined ) {
40 |
41 | callback( 'Your browser is not VR Ready' );
42 |
43 | }
44 |
45 | // the Rift SDK returns the position in meters
46 | // this scale factor allows the user to define how meters
47 | // are converted to scene units.
48 | this.scale = 1;
49 |
50 | this.update = function () {
51 |
52 | for ( var i = 0; i < vrInputs.length; i++ ) {
53 |
54 | var vrInput = vrInputs[ i ];
55 |
56 | var state = vrInput.getState();
57 |
58 | if ( state.orientation !== null ) {
59 |
60 | object.quaternion.copy( state.orientation );
61 |
62 | }
63 |
64 | if ( state.position !== null ) {
65 |
66 | object.position.copy( state.position ).multiplyScalar( scope.scale );
67 |
68 | }
69 |
70 | }
71 |
72 | };
73 |
74 | this.zeroSensor = function () {
75 |
76 | for ( var i = 0; i < vrInputs.length; i++ ) {
77 |
78 | var vrInput = vrInputs[ i ];
79 |
80 | vrInput.zeroSensor();
81 |
82 | }
83 |
84 | };
85 |
86 | };
87 |
--------------------------------------------------------------------------------
/js/VREffect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | *
4 | * It handles stereo rendering
5 | * If mozGetVRDevices and getVRDevices APIs are not available it gracefuly falls back to a
6 | * regular renderer
7 | *
8 | * The HMD supported is the Oculus DK1 and The Web API doesn't currently allow
9 | * to query for the display resolution (only the chrome API allows it).
10 | * The dimensions of the screen are temporarly hardcoded (1280 x 800).
11 | *
12 | * For VR mode to work it has to be used with the Oculus enabled builds of Firefox or Chrome:
13 | *
14 | * Firefox:
15 | *
16 | * http://mozvr.com/downloads.html
17 | *
18 | * Chrome builds:
19 | *
20 | * https://drive.google.com/a/google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
21 | *
22 | */
23 | THREE.VREffect = function ( renderer, done ) {
24 |
25 | var cameraLeft = new THREE.PerspectiveCamera();
26 | var cameraRight = new THREE.PerspectiveCamera();
27 |
28 | this._renderer = renderer;
29 |
30 | this._init = function() {
31 | var self = this;
32 | if ( !navigator.mozGetVRDevices && !navigator.getVRDevices ) {
33 | if ( done ) {
34 | done("Your browser is not VR Ready");
35 | }
36 | return;
37 | }
38 | if ( navigator.getVRDevices ) {
39 | navigator.getVRDevices().then( gotVRDevices );
40 | } else {
41 | navigator.mozGetVRDevices( gotVRDevices );
42 | }
43 | function gotVRDevices( devices ) {
44 | var vrHMD;
45 | var error;
46 | for ( var i = 0; i < devices.length; ++ i ) {
47 | if ( devices[i] instanceof HMDVRDevice ) {
48 | vrHMD = devices[i];
49 | self._vrHMD = vrHMD;
50 | self.leftEyeTranslation = vrHMD.getEyeParameters("left").eyeTranslation;
51 | self.rightEyeTranslation = vrHMD.getEyeParameters("right").eyeTranslation;
52 | self.leftEyeFOV = vrHMD.getEyeParameters("left").recommendedFieldOfView;
53 | self.rightEyeFOV = vrHMD.getEyeParameters("right").recommendedFieldOfView;
54 | break; // We keep the first we encounter
55 | }
56 | }
57 | if ( done ) {
58 | if ( !vrHMD ) {
59 | error = 'HMD not available';
60 | }
61 | done( error );
62 | }
63 | }
64 | };
65 |
66 | this._init();
67 |
68 | this.render = function ( scene, camera ) {
69 | var renderer = this._renderer;
70 | var vrHMD = this._vrHMD;
71 | // VR render mode if HMD is available
72 | if ( vrHMD ) {
73 | this.renderStereo.apply( this, arguments );
74 | return;
75 | }
76 | // Regular render mode if not HMD
77 | renderer.render.apply( this._renderer, arguments );
78 | };
79 |
80 | this.renderStereo = function( scene, camera, renderTarget, forceClear ) {
81 |
82 | var leftEyeTranslation = this.leftEyeTranslation;
83 | var rightEyeTranslation = this.rightEyeTranslation;
84 | var renderer = this._renderer;
85 | var rendererWidth = renderer.context.drawingBufferWidth / renderer.getPixelRatio();
86 | var rendererHeight = renderer.context.drawingBufferHeight / renderer.getPixelRatio();
87 | var eyeDivisionLine = rendererWidth / 2;
88 |
89 | renderer.enableScissorTest( true );
90 | renderer.clear();
91 |
92 | if ( camera.parent === undefined ) {
93 | camera.updateMatrixWorld();
94 | }
95 |
96 | cameraLeft.projectionMatrix = this.FovToProjection( this.leftEyeFOV, true, camera.near, camera.far );
97 | cameraRight.projectionMatrix = this.FovToProjection( this.rightEyeFOV, true, camera.near, camera.far );
98 |
99 | camera.matrixWorld.decompose( cameraLeft.position, cameraLeft.quaternion, cameraLeft.scale );
100 | camera.matrixWorld.decompose( cameraRight.position, cameraRight.quaternion, cameraRight.scale );
101 |
102 | cameraLeft.translateX( leftEyeTranslation.x );
103 | cameraRight.translateX( rightEyeTranslation.x );
104 |
105 | // render left eye
106 | renderer.setViewport( 0, 0, eyeDivisionLine, rendererHeight );
107 | renderer.setScissor( 0, 0, eyeDivisionLine, rendererHeight );
108 | renderer.render( scene, cameraLeft );
109 |
110 | // render right eye
111 | renderer.setViewport( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
112 | renderer.setScissor( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
113 | renderer.render( scene, cameraRight );
114 |
115 | renderer.enableScissorTest( false );
116 |
117 | };
118 |
119 | this.setSize = function( width, height ) {
120 | renderer.setSize( width, height );
121 | };
122 |
123 | this.setFullScreen = function( enable ) {
124 | var renderer = this._renderer;
125 | var vrHMD = this._vrHMD;
126 | var canvasOriginalSize = this._canvasOriginalSize;
127 | if (!vrHMD) {
128 | return;
129 | }
130 | // If state doesn't change we do nothing
131 | if ( enable === this._fullScreen ) {
132 | return;
133 | }
134 | this._fullScreen = !!enable;
135 |
136 | // VR Mode disabled
137 | if ( !enable ) {
138 | // Restores canvas original size
139 | renderer.setSize( canvasOriginalSize.width, canvasOriginalSize.height );
140 | return;
141 | }
142 | // VR Mode enabled
143 | this._canvasOriginalSize = {
144 | width: renderer.domElement.width,
145 | height: renderer.domElement.height
146 | };
147 | // Hardcoded Rift display size
148 | //renderer.setSize( 1280, 800, false );
149 | this.startFullscreen();
150 | };
151 |
152 | this.startFullscreen = function() {
153 | var self = this;
154 | var renderer = this._renderer;
155 | var vrHMD = this._vrHMD;
156 | var canvas = renderer.domElement;
157 | var fullScreenChange =
158 | canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
159 |
160 | document.addEventListener( fullScreenChange, onFullScreenChanged, false );
161 | function onFullScreenChanged() {
162 | if ( !document.mozFullScreenElement && !document.webkitFullscreenElement ) {
163 | self.setFullScreen( false );
164 | }
165 | }
166 | if ( canvas.mozRequestFullScreen ) {
167 | canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
168 | } else if (canvas.webkitRequestFullscreen) {
169 | canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
170 | }
171 | };
172 |
173 | this.FovToNDCScaleOffset = function( fov ) {
174 | var pxscale = 2.0 / (fov.leftTan + fov.rightTan);
175 | var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5;
176 | var pyscale = 2.0 / (fov.upTan + fov.downTan);
177 | var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5;
178 | return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
179 | };
180 |
181 | this.FovPortToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
182 | {
183 | rightHanded = rightHanded === undefined ? true : rightHanded;
184 | zNear = zNear === undefined ? 0.01 : zNear;
185 | zFar = zFar === undefined ? 10000.0 : zFar;
186 |
187 | var handednessScale = rightHanded ? -1.0 : 1.0;
188 |
189 | // start with an identity matrix
190 | var mobj = new THREE.Matrix4();
191 | var m = mobj.elements;
192 |
193 | // and with scale/offset info for normalized device coords
194 | var scaleAndOffset = this.FovToNDCScaleOffset(fov);
195 |
196 | // X result, map clip edges to [-w,+w]
197 | m[0 * 4 + 0] = scaleAndOffset.scale[0];
198 | m[0 * 4 + 1] = 0.0;
199 | m[0 * 4 + 2] = scaleAndOffset.offset[0] * handednessScale;
200 | m[0 * 4 + 3] = 0.0;
201 |
202 | // Y result, map clip edges to [-w,+w]
203 | // Y offset is negated because this proj matrix transforms from world coords with Y=up,
204 | // but the NDC scaling has Y=down (thanks D3D?)
205 | m[1 * 4 + 0] = 0.0;
206 | m[1 * 4 + 1] = scaleAndOffset.scale[1];
207 | m[1 * 4 + 2] = -scaleAndOffset.offset[1] * handednessScale;
208 | m[1 * 4 + 3] = 0.0;
209 |
210 | // Z result (up to the app)
211 | m[2 * 4 + 0] = 0.0;
212 | m[2 * 4 + 1] = 0.0;
213 | m[2 * 4 + 2] = zFar / (zNear - zFar) * -handednessScale;
214 | m[2 * 4 + 3] = (zFar * zNear) / (zNear - zFar);
215 |
216 | // W result (= Z in)
217 | m[3 * 4 + 0] = 0.0;
218 | m[3 * 4 + 1] = 0.0;
219 | m[3 * 4 + 2] = handednessScale;
220 | m[3 * 4 + 3] = 0.0;
221 |
222 | mobj.transpose();
223 |
224 | return mobj;
225 | };
226 |
227 | this.FovToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
228 | {
229 | var fovPort = {
230 | upTan: Math.tan(fov.upDegrees * Math.PI / 180.0),
231 | downTan: Math.tan(fov.downDegrees * Math.PI / 180.0),
232 | leftTan: Math.tan(fov.leftDegrees * Math.PI / 180.0),
233 | rightTan: Math.tan(fov.rightDegrees * Math.PI / 180.0)
234 | };
235 | return this.FovPortToProjection(fovPort, rightHanded, zNear, zFar);
236 | };
237 |
238 | };
239 |
--------------------------------------------------------------------------------
/js/gamepad.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Priit Kallas
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining
5 | * a copy of this software and associated documentation files (the
6 | * "Software"), to deal in the Software without restriction, including
7 | * without limitation the rights to use, copy, modify, merge, publish,
8 | * distribute, sublicense, and/or sell copies of the Software, and to
9 | * permit persons to whom the Software is furnished to do so, subject to
10 | * the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be
13 | * included in all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | (function(exports) {
25 | 'use strict';
26 |
27 | /**
28 | * A null function - does nothing, returns nothing
29 | */
30 | var nullFunction = function() {};
31 |
32 | /**
33 | * The null platform, which doesn't support anything
34 | */
35 | var nullPlatform = {
36 | getType: function() {
37 | return 'null';
38 | },
39 | isSupported: function() {
40 | return false;
41 | },
42 | update: nullFunction
43 | };
44 |
45 | /**
46 | * This strategy uses a timer function to call an update function.
47 | * The timer (re)start function can be provided or the strategy reverts to
48 | * one of the window.*requestAnimationFrame variants.
49 | *
50 | * @class AnimFrameUpdateStrategy
51 | * @constructor
52 | * @param {Function} [requestAnimationFrame] function to use for timer creation
53 | * @module Gamepad
54 | */
55 | var AnimFrameUpdateStrategy = function(requestAnimationFrame) {
56 | var that = this;
57 | var win = window;
58 |
59 | this.update = nullFunction;
60 |
61 | this.requestAnimationFrame = requestAnimationFrame || win.requestAnimationFrame ||
62 | win.webkitRequestAnimationFrame || win.mozRequestAnimationFrame;
63 |
64 | /**
65 | * This method calls the (user) update and restarts itself
66 | * @method tickFunction
67 | */
68 | this.tickFunction = function() {
69 | that.update();
70 | that.startTicker();
71 | };
72 |
73 | /**
74 | * (Re)Starts the ticker
75 | * @method startTicker
76 | */
77 | this.startTicker = function() {
78 | that.requestAnimationFrame.apply(win, [that.tickFunction]);
79 | };
80 | };
81 |
82 | /**
83 | * Starts the update strategy using the given function
84 | *
85 | * @method start
86 | * @param {Function} updateFunction the function to call at each update
87 | */
88 | AnimFrameUpdateStrategy.prototype.start = function(updateFunction) {
89 | this.update = updateFunction || nullFunction;
90 | this.startTicker();
91 | };
92 |
93 | /**
94 | * This strategy gives the user the ability to call the library internal
95 | * update function on request. Use this strategy if you already have a
96 | * timer function running by requestAnimationFrame and you need fine control
97 | * over when the gamepads are updated.
98 | *
99 | * @class ManualUpdateStrategy
100 | * @constructor
101 | * @module Gamepad
102 | */
103 | var ManualUpdateStrategy = function() {
104 |
105 | };
106 |
107 | /**
108 | * Calls the update function in the started state. Does nothing otherwise.
109 | * @method update
110 | */
111 | ManualUpdateStrategy.prototype.update = nullFunction;
112 |
113 | /**
114 | * Starts the update strategy using the given function
115 | *
116 | * @method start
117 | * @param {Function} updateFunction the function to call at each update
118 | */
119 | ManualUpdateStrategy.prototype.start = function(updateFunction) {
120 | this.update = updateFunction || nullFunction;
121 | };
122 |
123 | /**
124 | * This platform is for webkit based environments that need to be polled
125 | * for updates.
126 | *
127 | * @class WebKitPlatform
128 | * @constructor
129 | * @param {Object} listener the listener to provide _connect and _disconnect callbacks
130 | * @param {Function} gamepadGetter the poll function to return an array of connected gamepads
131 | * @module Gamepad
132 | */
133 | var WebKitPlatform = function(listener, gamepadGetter) {
134 | this.listener = listener;
135 | this.gamepadGetter = gamepadGetter;
136 | this.knownGamepads = [];
137 | };
138 |
139 | /**
140 | * Provides a platform object that returns true for is isSupported() if valid.
141 | * @method factory
142 | * @static
143 | * @param {Object} listener the listener to use
144 | * @return {Object} a platform object
145 | */
146 | WebKitPlatform.factory = function(listener) {
147 | var platform = nullPlatform;
148 | var navigator = window && window.navigator;
149 |
150 | if (navigator) {
151 | if (typeof(navigator.webkitGamepads) !== 'undefined') {
152 | platform = new WebKitPlatform(listener, function() {
153 | return navigator.webkitGamepads;
154 | });
155 | } else if (typeof(navigator.webkitGetGamepads) !== 'undefined') {
156 | platform = new WebKitPlatform(listener, function() {
157 | return navigator.webkitGetGamepads();
158 | });
159 | }
160 | }
161 |
162 | return platform;
163 | };
164 |
165 | /**
166 | * @method getType()
167 | * @static
168 | * @return {String} 'WebKit'
169 | */
170 | WebKitPlatform.getType = function() {
171 | return 'WebKit';
172 | },
173 |
174 | /**
175 | * @method getType()
176 | * @return {String} 'WebKit'
177 | */
178 | WebKitPlatform.prototype.getType = function() {
179 | return WebKitPlatform.getType();
180 | },
181 |
182 | /**
183 | * @method isSupported
184 | * @return {Boolean} true
185 | */
186 | WebKitPlatform.prototype.isSupported = function() {
187 | return true;
188 | };
189 |
190 | /**
191 | * Queries the currently connected gamepads and reports any changes.
192 | * @method update
193 | */
194 | WebKitPlatform.prototype.update = function() {
195 | var that = this;
196 | var gamepads = Array.prototype.slice.call(this.gamepadGetter(), 0);
197 | var gamepad;
198 | var i;
199 |
200 | for (i = this.knownGamepads.length - 1; i >= 0; i--) {
201 | gamepad = this.knownGamepads[i];
202 | if (gamepads.indexOf(gamepad) < 0) {
203 | this.knownGamepads.splice(i, 1);
204 | this.listener._disconnect(gamepad);
205 | }
206 | }
207 |
208 | for (i = 0; i < gamepads.length; i++) {
209 | gamepad = gamepads[i];
210 | if (gamepad && (that.knownGamepads.indexOf(gamepad) < 0)) {
211 | that.knownGamepads.push(gamepad);
212 | that.listener._connect(gamepad);
213 | }
214 | }
215 | };
216 |
217 | /**
218 | * This platform is for mozilla based environments that provide gamepad
219 | * updates via events.
220 | *
221 | * @class FirefoxPlatform
222 | * @constructor
223 | * @module Gamepad
224 | */
225 | var FirefoxPlatform = function(listener) {
226 | this.listener = listener;
227 |
228 | window.addEventListener('MozGamepadConnected', function(e) {
229 | listener._connect(e.gamepad);
230 | });
231 | window.addEventListener('MozGamepadDisconnected', function(e) {
232 | listener._disconnect(e.gamepad);
233 | });
234 | };
235 |
236 | /**
237 | * Provides a platform object that returns true for is isSupported() if valid.
238 | * @method factory
239 | * @static
240 | * @param {Object} listener the listener to use
241 | * @return {Object} a platform object
242 | */
243 | FirefoxPlatform.factory = function(listener) {
244 | var platform = nullPlatform;
245 |
246 | if (window && (typeof(window.addEventListener) !== 'undefined')) {
247 | platform = new FirefoxPlatform(listener);
248 | }
249 |
250 | return platform;
251 | };
252 |
253 | /**
254 | * @method getType()
255 | * @static
256 | * @return {String} 'Firefox'
257 | */
258 | FirefoxPlatform.getType = function() {
259 | return 'Firefox';
260 | },
261 |
262 | /**
263 | * @method getType()
264 | * @return {String} 'Firefox'
265 | */
266 | FirefoxPlatform.prototype.getType = function() {
267 | return FirefoxPlatform.getType();
268 | },
269 |
270 | /**
271 | * @method isSupported
272 | * @return {Boolean} true
273 | */
274 | FirefoxPlatform.prototype.isSupported = function() {
275 | return true;
276 | };
277 |
278 | /**
279 | * Does nothing on the Firefox platform
280 | * @method update
281 | */
282 | FirefoxPlatform.prototype.update = nullFunction;
283 |
284 | /**
285 | * Provides simple interface and multi-platform support for the gamepad API.
286 | *
287 | * You can change the deadzone and maximizeThreshold parameters to suit your
288 | * taste but the defaults should generally work fine.
289 | *
290 | * @class Gamepad
291 | * @constructor
292 | * @param {Object} [updateStrategy] an update strategy, defaulting to
293 | * {{#crossLink "AnimFrameUpdateStrategy"}}{{/crossLink}}
294 | * @module Gamepad
295 | * @author Priit Kallas
296 | */
297 | var Gamepad = function(updateStrategy) {
298 | this.updateStrategy = updateStrategy || new AnimFrameUpdateStrategy();
299 | this.gamepads = [];
300 | this.listeners = {};
301 | this.platform = nullPlatform;
302 | this.deadzone = 0.03;
303 | this.maximizeThreshold = 0.97;
304 | };
305 |
306 | /**
307 | * The available update strategies
308 | * @property UpdateStrategies
309 | * @param {AnimFrameUpdateStrategy} AnimFrameUpdateStrategy
310 | * @param {ManualUpdateStrategy} ManualUpdateStrategy
311 | */
312 | Gamepad.UpdateStrategies = {
313 | AnimFrameUpdateStrategy: AnimFrameUpdateStrategy,
314 | ManualUpdateStrategy: ManualUpdateStrategy
315 | };
316 |
317 | /**
318 | * List of factories of supported platforms. Currently available platforms:
319 | * {{#crossLink "WebKitPlatform"}}{{/crossLink}},
320 | * {{#crossLink "FirefoxPlatform"}}{{/crossLink}},
321 | * @property PlatformFactories
322 | * @type {Array}
323 | */
324 | Gamepad.PlatformFactories = [WebKitPlatform.factory, FirefoxPlatform.factory];
325 |
326 | /**
327 | * List of supported controller types.
328 | *
329 | * @property Type
330 | * @param {String} Type.PLAYSTATION Playstation controller
331 | * @param {String} Type.LOGITECH Logitech controller
332 | * @param {String} Type.XBOX XBOX controller
333 | * @param {String} Type.UNKNOWN Unknown controller
334 | */
335 | Gamepad.Type = {
336 | PLAYSTATION: 'playstation',
337 | LOGITECH: 'logitech',
338 | XBOX: 'xbox',
339 | UNKNOWN: 'unknown'
340 | };
341 |
342 | /*
343 | * List of events you can expect from the library.
344 | *
345 | * CONNECTED, DISCONNECTED and UNSUPPORTED events include the gamepad in
346 | * question and tick provides the list of all connected gamepads.
347 | *
348 | * BUTTON_DOWN and BUTTON_UP events provide an alternative to polling button states at each tick.
349 | *
350 | * AXIS_CHANGED is called if a value of some specific axis changes.
351 | */
352 | Gamepad.Event = {
353 | /**
354 | * Triggered when a new controller connects.
355 | *
356 | * @event connected
357 | * @param {Object} device
358 | */
359 | CONNECTED: 'connected',
360 |
361 | /**
362 | * Called when an unsupported controller connects.
363 | *
364 | * @event unsupported
365 | * @param {Object} device
366 | * @deprecated not used anymore. Any controller is supported.
367 | */
368 | UNSUPPORTED: 'unsupported',
369 |
370 | /**
371 | * Triggered when a controller disconnects.
372 | *
373 | * @event disconnected
374 | * @param {Object} device
375 | */
376 | DISCONNECTED: 'disconnected',
377 |
378 | /**
379 | * Called regularly with the latest controllers info.
380 | *
381 | * @event tick
382 | * @param {Array} gamepads
383 | */
384 | TICK: 'tick',
385 |
386 | /**
387 | * Called when a gamepad button is pressed down.
388 | *
389 | * @event button-down
390 | * @param {Object} event
391 | * @param {Object} event.gamepad The gamepad object
392 | * @param {String} event.control Control name
393 | */
394 | BUTTON_DOWN: 'button-down',
395 |
396 | /**
397 | * Called when a gamepad button is released.
398 | *
399 | * @event button-up
400 | * @param {Object} event
401 | * @param {Object} event.gamepad The gamepad object
402 | * @param {String} event.control Control name
403 | */
404 | BUTTON_UP: 'button-up',
405 |
406 | /**
407 | * Called when gamepad axis value changed.
408 | *
409 | * @event axis-changed
410 | * @param {Object} event
411 | * @param {Object} event.gamepad The gamepad object
412 | * @param {String} event.axis Axis name
413 | * @param {Number} event.value New axis value
414 | */
415 | AXIS_CHANGED: 'axis-changed'
416 | };
417 |
418 | /**
419 | * List of standard button names. The index is the according standard button
420 | * index as per standard.
421 | *
422 | * @property StandardButtons
423 | */
424 | Gamepad.StandardButtons = [
425 | 'FACE_1', 'FACE_2', 'FACE_3', 'FACE_4',
426 | 'LEFT_TOP_SHOULDER', 'RIGHT_TOP_SHOULDER', 'LEFT_BOTTOM_SHOULDER', 'RIGHT_BOTTOM_SHOULDER',
427 | 'SELECT_BACK', 'START_FORWARD', 'LEFT_STICK', 'RIGHT_STICK',
428 | 'DPAD_UP', 'DPAD_DOWN', 'DPAD_LEFT', 'DPAD_RIGHT',
429 | 'HOME'
430 | ];
431 |
432 | /**
433 | * List of standard axis names. The index is the according standard axis
434 | * index as per standard.
435 | *
436 | * @property StandardAxes
437 | */
438 | Gamepad.StandardAxes = ['LEFT_STICK_X', 'LEFT_STICK_Y', 'RIGHT_STICK_X', 'RIGHT_STICK_Y'];
439 |
440 | var getControlName = function(names, index, extraPrefix) {
441 | return (index < names.length) ? names[index] : extraPrefix + (index - names.length + 1);
442 | };
443 |
444 | /**
445 | * The standard mapping that represents the mapping as per definition.
446 | * Each button and axis map to the same index.
447 | *
448 | * @property StandardMapping
449 | */
450 | Gamepad.StandardMapping = {
451 | env: {},
452 | buttons: {
453 | byButton: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
454 | },
455 | axes: {
456 | byAxis: [0, 1, 2, 3]
457 | }
458 | };
459 |
460 | /**
461 | * Mapping of various gamepads that differ from the standard mapping on
462 | * different platforms too unify their buttons and axes.
463 | *
464 | * Each mapping should have an 'env' object, which describes the environment
465 | * in which the mapping is active. The more entries such an environment has,
466 | * the more specific it is.
467 | *
468 | * Mappings are expressed for both buttons and axes. Buttons might refer to
469 | * axes if they are notified as such.
470 | *
471 | * @property Mappings
472 | */
473 | Gamepad.Mappings = [
474 | // PS3 controller on Firefox
475 | {
476 | env: {
477 | platform: FirefoxPlatform.getType(),
478 | type: Gamepad.Type.PLAYSTATION
479 | },
480 | buttons: {
481 | byButton: [14, 13, 15, 12, 10, 11, 8, 9, 0, 3, 1, 2, 4, 6, 7, 5, 16]
482 | },
483 | axes: {
484 | byAxis: [0, 1, 2, 3]
485 | }
486 | },
487 | // Logitech gamepad on WebKit
488 | {
489 | env: {
490 | platform: WebKitPlatform.getType(),
491 | type: Gamepad.Type.LOGITECH
492 | },
493 | buttons: { // TODO: This can't be right - LEFT/RIGHT_STICK have same mappings as HOME/DPAD_UP
494 | byButton: [1, 2, 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 10]
495 | },
496 | axes: {
497 | byAxis: [0, 1, 2, 3]
498 | }
499 | },
500 | // Logitech gamepad on Firefox
501 | {
502 | env: {
503 | platform: FirefoxPlatform.getType(),
504 | type: Gamepad.Type.LOGITECH
505 | },
506 | buttons: {
507 | byButton: [0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 11, 12, 13, 14, 10],
508 | byAxis: [-1, -1, -1, -1, -1, -1, [2, 0, 1],
509 | [2, 0, -1]
510 | ]
511 | },
512 | axes: {
513 | byAxis: [0, 1, 3, 4]
514 | }
515 | }
516 | ];
517 |
518 | /**
519 | * Initializes the gamepad.
520 | *
521 | * You usually want to bind to the events first and then initialize it.
522 | *
523 | * @method init
524 | * @return {Boolean} true if a supporting platform was detected, false otherwise.
525 | */
526 | Gamepad.prototype.init = function() {
527 | var platform = Gamepad.resolvePlatform(this);
528 | var that = this;
529 |
530 | this.platform = platform;
531 | this.updateStrategy.start(function() {
532 | that._update();
533 | });
534 |
535 | return platform.isSupported();
536 | };
537 |
538 | /**
539 | * Binds a listener to a gamepad event.
540 | *
541 | * @method bind
542 | * @param {String} event Event to bind to, one of Gamepad.Event..
543 | * @param {Function} listener Listener to call when given event occurs
544 | * @return {Gamepad} Self
545 | */
546 | Gamepad.prototype.bind = function(event, listener) {
547 | if (typeof(this.listeners[event]) === 'undefined') {
548 | this.listeners[event] = [];
549 | }
550 |
551 | this.listeners[event].push(listener);
552 |
553 | return this;
554 | };
555 |
556 | /**
557 | * Removes listener of given type.
558 | *
559 | * If no type is given, all listeners are removed. If no listener is given, all
560 | * listeners of given type are removed.
561 | *
562 | * @method unbind
563 | * @param {String} [type] Type of listener to remove
564 | * @param {Function} [listener] The listener function to remove
565 | * @return {Boolean} Was unbinding the listener successful
566 | */
567 | Gamepad.prototype.unbind = function(type, listener) {
568 | if (typeof(type) === 'undefined') {
569 | this.listeners = {};
570 |
571 | return;
572 | }
573 |
574 | if (typeof(listener) === 'undefined') {
575 | this.listeners[type] = [];
576 |
577 | return;
578 | }
579 |
580 | if (typeof(this.listeners[type]) === 'undefined') {
581 | return false;
582 | }
583 |
584 | for (var i = 0; i < this.listeners[type].length; i++) {
585 | if (this.listeners[type][i] === listener) {
586 | this.listeners[type].splice(i, 1);
587 |
588 | return true;
589 | }
590 | }
591 |
592 | return false;
593 | };
594 |
595 | /**
596 | * Returns the number of connected gamepads.
597 | *
598 | * @method count
599 | * @return {Number}
600 | */
601 | Gamepad.prototype.count = function() {
602 | return this.gamepads.length;
603 | };
604 |
605 | /**
606 | * Fires an internal event with given data.
607 | *
608 | * @method _fire
609 | * @param {String} event Event to fire, one of Gamepad.Event..
610 | * @param {*} data Data to pass to the listener
611 | * @private
612 | */
613 | Gamepad.prototype._fire = function(event, data) {
614 | if (typeof(this.listeners[event]) === 'undefined') {
615 | return;
616 | }
617 |
618 | for (var i = 0; i < this.listeners[event].length; i++) {
619 | this.listeners[event][i].apply(this.listeners[event][i], [data]);
620 | }
621 | };
622 |
623 | /**
624 | * @method getNullPlatform
625 | * @static
626 | * @return {Object} a platform that does not support anything
627 | */
628 | Gamepad.getNullPlatform = function() {
629 | return Object.create(nullPlatform);
630 | };
631 |
632 | /**
633 | * Resolves platform.
634 | *
635 | * @method resolvePlatform
636 | * @static
637 | * @param listener {Object} the listener to handle _connect() or _disconnect() calls
638 | * @return {Object} A platform instance
639 | */
640 | Gamepad.resolvePlatform = function(listener) {
641 | var platform = nullPlatform;
642 | var i;
643 |
644 | for (i = 0; !platform.isSupported() && (i < Gamepad.PlatformFactories.length); i++) {
645 | platform = Gamepad.PlatformFactories[i](listener);
646 | }
647 |
648 | return platform;
649 | };
650 |
651 | /**
652 | * Registers given gamepad.
653 | *
654 | * @method _connect
655 | * @param {Object} gamepad Gamepad to connect to
656 | * @private
657 | */
658 | Gamepad.prototype._connect = function(gamepad) {
659 | var mapping = this._resolveMapping(gamepad);
660 | var count;
661 | var i;
662 |
663 | //gamepad.mapping = this._resolveMapping(gamepad);
664 | gamepad.state = {};
665 | gamepad.lastState = {};
666 | gamepad.updater = [];
667 |
668 | count = mapping.buttons.byButton.length;
669 | for (i = 0; i < count; i++) {
670 | this._addButtonUpdater(gamepad, mapping, i);
671 | }
672 |
673 | count = mapping.axes.byAxis.length;
674 | for (i = 0; i < count; i++) {
675 | this._addAxisUpdater(gamepad, mapping, i);
676 | }
677 |
678 | this.gamepads[gamepad.index] = gamepad;
679 |
680 | this._fire(Gamepad.Event.CONNECTED, gamepad);
681 | };
682 |
683 | /**
684 | * Adds an updater for a button control
685 | *
686 | * @method _addButtonUpdater
687 | * @private
688 | * @param {Object} gamepad the gamepad for which to create the updater
689 | * @param {Object} mapping the mapping on which to work on
690 | * @param {Number} index button index
691 | */
692 | Gamepad.prototype._addButtonUpdater = function(gamepad, mapping, index) {
693 | var updater = nullFunction;
694 | var controlName = getControlName(Gamepad.StandardButtons, index, 'EXTRA_BUTTON_');
695 | var getter = this._createButtonGetter(gamepad, mapping.buttons, index);
696 | var that = this;
697 | var buttonEventData = {
698 | gamepad: gamepad,
699 | control: controlName
700 | };
701 |
702 | gamepad.state[controlName] = 0;
703 | gamepad.lastState[controlName] = 0;
704 |
705 | updater = function() {
706 | var value = getter();
707 | var lastValue = gamepad.lastState[controlName];
708 | var isDown = value > 0.5;
709 | var wasDown = lastValue > 0.5;
710 |
711 | gamepad.state[controlName] = value;
712 |
713 | if (isDown && !wasDown) {
714 | that._fire(Gamepad.Event.BUTTON_DOWN, Object.create(buttonEventData));
715 | } else if (!isDown && wasDown) {
716 | that._fire(Gamepad.Event.BUTTON_UP, Object.create(buttonEventData));
717 | }
718 |
719 | if ((value !== 0) && (value !== 1) && (value !== lastValue)) {
720 | that._fireAxisChangedEvent(gamepad, controlName, value);
721 | }
722 |
723 | gamepad.lastState[controlName] = value;
724 | };
725 |
726 | gamepad.updater.push(updater);
727 | };
728 |
729 | /**
730 | * Adds an updater for an axis control
731 | *
732 | * @method _addAxisUpdater
733 | * @private
734 | * @param {Object} gamepad the gamepad for which to create the updater
735 | * @param {Object} mapping the mapping on which to work on
736 | * @param {Number} index axis index
737 | */
738 | Gamepad.prototype._addAxisUpdater = function(gamepad, mapping, index) {
739 | var updater = nullFunction;
740 | var controlName = getControlName(Gamepad.StandardAxes, index, 'EXTRA_AXIS_');
741 | var getter = this._createAxisGetter(gamepad, mapping.axes, index);
742 | var that = this;
743 |
744 | gamepad.state[controlName] = 0;
745 | gamepad.lastState[controlName] = 0;
746 |
747 | updater = function() {
748 | var value = getter();
749 | var lastValue = gamepad.lastState[controlName];
750 |
751 | gamepad.state[controlName] = value;
752 |
753 | if ((value !== lastValue)) {
754 | that._fireAxisChangedEvent(gamepad, controlName, value);
755 | }
756 |
757 | gamepad.lastState[controlName] = value;
758 | };
759 |
760 | gamepad.updater.push(updater);
761 | };
762 |
763 | /**
764 | * Fires an AXIS_CHANGED event
765 | * @method _fireAxisChangedEvent
766 | * @private
767 | * @param {Object} gamepad the gamepad to notify for
768 | * @param {String} controlName name of the control that changes its value
769 | * @param {Number} value the new value
770 | */
771 | Gamepad.prototype._fireAxisChangedEvent = function(gamepad, controlName, value) {
772 | var eventData = {
773 | gamepad: gamepad,
774 | axis: controlName,
775 | value: value
776 | };
777 |
778 | this._fire(Gamepad.Event.AXIS_CHANGED, eventData);
779 | };
780 |
781 | /**
782 | * Creates a getter according to the mapping entry for the specific index.
783 | * Currently supported entries:
784 | *
785 | * buttons.byButton[index]: Number := Index into gamepad.buttons; -1 tests byAxis
786 | * buttons.byAxis[index]: Array := [Index into gamepad.axes; Zero Value, One Value]
787 | *
788 | * @method _createButtonGetter
789 | * @private
790 | * @param {Object} gamepad the gamepad for which to create a getter
791 | * @param {Object} buttons the mappings entry for the buttons
792 | * @param {Number} index the specific button entry
793 | * @return {Function} a getter returning the value for the requested button
794 | */
795 | Gamepad.prototype._createButtonGetter = (function() {
796 | var nullGetter = function() {
797 | return 0;
798 | };
799 |
800 | var createRangeGetter = function(valueGetter, from, to) {
801 | var getter = nullGetter;
802 |
803 | if (from < to) {
804 | getter = function() {
805 | var range = to - from;
806 | var value = valueGetter();
807 |
808 | value = (value - from) / range;
809 |
810 | return (value < 0) ? 0 : value;
811 | };
812 | } else if (to < from) {
813 | getter = function() {
814 | var range = from - to;
815 | var value = valueGetter();
816 |
817 | value = (value - to) / range;
818 |
819 | return (value > 1) ? 0 : (1 - value);
820 | };
821 | }
822 |
823 | return getter;
824 | };
825 |
826 | var isArray = function(thing) {
827 | return Object.prototype.toString.call(thing) === '[object Array]';
828 | };
829 |
830 | return function(gamepad, buttons, index) {
831 | var getter = nullGetter;
832 | var entry;
833 | var that = this;
834 |
835 | entry = buttons.byButton[index];
836 | if (entry !== -1) {
837 | if ((typeof(entry) === 'number') && (entry < gamepad.buttons.length)) {
838 | getter = function() {
839 | return gamepad.buttons[entry];
840 | };
841 | }
842 | } else if (buttons.byAxis && (index < buttons.byAxis.length)) {
843 | entry = buttons.byAxis[index];
844 | if (isArray(entry) && (entry.length == 3) && (entry[0] < gamepad.axes.length)) {
845 | getter = function() {
846 | var value = gamepad.axes[entry[0]];
847 |
848 | return that._applyDeadzoneMaximize(value);
849 | };
850 |
851 | getter = createRangeGetter(getter, entry[1], entry[2]);
852 | }
853 | }
854 |
855 | return getter;
856 | };
857 | })();
858 |
859 | /**
860 | * Creates a getter according to the mapping entry for the specific index.
861 | * Currently supported entries:
862 | *
863 | * axes.byAxis[index]: Number := Index into gamepad.axes; -1 ignored
864 | *
865 | * @method _createAxisGetter
866 | * @private
867 | * @param {Object} gamepad the gamepad for which to create a getter
868 | * @param {Object} axes the mappings entry for the axes
869 | * @param {Number} index the specific axis entry
870 | * @return {Function} a getter returning the value for the requested axis
871 | */
872 | Gamepad.prototype._createAxisGetter = (function() {
873 | var nullGetter = function() {
874 | return 0;
875 | };
876 |
877 | return function(gamepad, axes, index) {
878 | var getter = nullGetter;
879 | var entry;
880 | var that = this;
881 |
882 | entry = axes.byAxis[index];
883 | if (entry !== -1) {
884 | if ((typeof(entry) === 'number') && (entry < gamepad.axes.length)) {
885 | getter = function() {
886 | var value = gamepad.axes[entry];
887 |
888 | return that._applyDeadzoneMaximize(value);
889 | };
890 | }
891 | }
892 |
893 | return getter;
894 | };
895 | })();
896 |
897 | /**
898 | * Disconnects from given gamepad.
899 | *
900 | * @method _disconnect
901 | * @param {Object} gamepad Gamepad to disconnect
902 | * @private
903 | */
904 | Gamepad.prototype._disconnect = function(gamepad) {
905 | var newGamepads = [],
906 | i;
907 |
908 | if (typeof(this.gamepads[gamepad.index]) !== 'undefined') {
909 | delete this.gamepads[gamepad.index];
910 | }
911 |
912 | for (i = 0; i < this.gamepads.length; i++) {
913 | if (typeof(this.gamepads[i]) !== 'undefined') {
914 | newGamepads[i] = this.gamepads[i];
915 | }
916 | }
917 |
918 | this.gamepads = newGamepads;
919 |
920 | this._fire(Gamepad.Event.DISCONNECTED, gamepad);
921 | };
922 |
923 | /**
924 | * Resolves controller type from its id.
925 | *
926 | * @method _resolveControllerType
927 | * @param {String} id Controller id
928 | * @return {String} Controller type, one of Gamepad.Type
929 | * @private
930 | */
931 | Gamepad.prototype._resolveControllerType = function(id) {
932 | id = id.toLowerCase();
933 |
934 | if (id.indexOf('playstation') !== -1) {
935 | return Gamepad.Type.PLAYSTATION;
936 | } else if (
937 | id.indexOf('logitech') !== -1 || id.indexOf('wireless gamepad') !== -1) {
938 | return Gamepad.Type.LOGITECH;
939 | } else if (id.indexOf('xbox') !== -1 || id.indexOf('360') !== -1) {
940 | return Gamepad.Type.XBOX;
941 | } else {
942 | return Gamepad.Type.UNKNOWN;
943 | }
944 | };
945 |
946 | /**
947 | * @method _resolveMapping
948 | * @private
949 | * @param {Object} gamepad the gamepad for which to resolve the mapping
950 | * @return {Object} a mapping object for the given gamepad
951 | */
952 | Gamepad.prototype._resolveMapping = function(gamepad) {
953 | var mappings = Gamepad.Mappings;
954 | var mapping = null;
955 | var env = {
956 | platform: this.platform.getType(),
957 | type: this._resolveControllerType(gamepad.id)
958 | };
959 | var i;
960 | var test;
961 |
962 | for (i = 0; !mapping && (i < mappings.length); i++) {
963 | test = mappings[i];
964 | if (Gamepad.envMatchesFilter(test.env, env)) {
965 | mapping = test;
966 | }
967 | }
968 |
969 | return mapping || Gamepad.StandardMapping;
970 | };
971 |
972 | /**
973 | * @method envMatchesFilter
974 | * @static
975 | * @param {Object} filter the filter object describing properties to match
976 | * @param {Object} env the environment object that is matched against filter
977 | * @return {Boolean} true if env is covered by filter
978 | */
979 | Gamepad.envMatchesFilter = function(filter, env) {
980 | var result = true;
981 | var field;
982 |
983 | for (field in filter) {
984 | if (filter[field] !== env[field]) {
985 | result = false;
986 | }
987 | }
988 |
989 | return result;
990 | };
991 |
992 | /**
993 | * Updates the controllers, triggering TICK events.
994 | *
995 | * @method _update
996 | * @private
997 | */
998 | Gamepad.prototype._update = function() {
999 | this.platform.update();
1000 |
1001 | this.gamepads.forEach(function(gamepad) {
1002 | if (gamepad) {
1003 | gamepad.updater.forEach(function(updater) {
1004 | updater();
1005 | });
1006 | }
1007 | });
1008 |
1009 | if (this.gamepads.length > 0) {
1010 | this._fire(Gamepad.Event.TICK, this.gamepads);
1011 | }
1012 | },
1013 |
1014 | /**
1015 | * Applies deadzone and maximization.
1016 | *
1017 | * You can change the thresholds via deadzone and maximizeThreshold members.
1018 | *
1019 | * @method _applyDeadzoneMaximize
1020 | * @param {Number} value Value to modify
1021 | * @param {Number} [deadzone] Deadzone to apply
1022 | * @param {Number} [maximizeThreshold] From which value to maximize value
1023 | * @private
1024 | */
1025 | Gamepad.prototype._applyDeadzoneMaximize = function(
1026 | value,
1027 | deadzone,
1028 | maximizeThreshold) {
1029 | deadzone = typeof(deadzone) !== 'undefined' ? deadzone : this.deadzone;
1030 | maximizeThreshold = typeof(maximizeThreshold) !== 'undefined' ? maximizeThreshold : this.maximizeThreshold;
1031 |
1032 | if (value >= 0) {
1033 | if (value < deadzone) {
1034 | value = 0;
1035 | } else if (value > maximizeThreshold) {
1036 | value = 1;
1037 | }
1038 | } else {
1039 | if (value > -deadzone) {
1040 | value = 0;
1041 | } else if (value < -maximizeThreshold) {
1042 | value = -1;
1043 | }
1044 | }
1045 |
1046 | return value;
1047 | };
1048 |
1049 | exports.Gamepad = Gamepad;
1050 |
1051 | })(((typeof(module) !== 'undefined') && module.exports) || window);
1052 |
--------------------------------------------------------------------------------
/js/jquery.base64.min.js:
--------------------------------------------------------------------------------
1 | "use strict";jQuery.base64=(function($){var _PADCHAR="=",_ALPHA="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",_VERSION="1.0";function _getbyte64(s,i){var idx=_ALPHA.indexOf(s.charAt(i));if(idx===-1){throw"Cannot decode base64"}return idx}function _decode(s){var pads=0,i,b10,imax=s.length,x=[];s=String(s);if(imax===0){return s}if(imax%4!==0){throw"Cannot decode base64"}if(s.charAt(imax-1)===_PADCHAR){pads=1;if(s.charAt(imax-2)===_PADCHAR){pads=2}imax-=4}for(i=0;i>16,(b10>>8)&255,b10&255))}switch(pads){case 1:b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i+1)<<12)|(_getbyte64(s,i+2)<<6);x.push(String.fromCharCode(b10>>16,(b10>>8)&255));break;case 2:b10=(_getbyte64(s,i)<<18)|(_getbyte64(s,i+1)<<12);x.push(String.fromCharCode(b10>>16));break}return x.join("")}function _getbyte(s,i){var x=s.charCodeAt(i);if(x>255){throw"INVALID_CHARACTER_ERR: DOM Exception 5"}return x}function _encode(s){if(arguments.length!==1){throw"SyntaxError: exactly one argument required"}s=String(s);var i,b10,x=[],imax=s.length-s.length%3;if(s.length===0){return s}for(i=0;i>18));x.push(_ALPHA.charAt((b10>>12)&63));x.push(_ALPHA.charAt((b10>>6)&63));x.push(_ALPHA.charAt(b10&63))}switch(s.length-imax){case 1:b10=_getbyte(s,i)<<16;x.push(_ALPHA.charAt(b10>>18)+_ALPHA.charAt((b10>>12)&63)+_PADCHAR+_PADCHAR);break;case 2:b10=(_getbyte(s,i)<<16)|(_getbyte(s,i+1)<<8);x.push(_ALPHA.charAt(b10>>18)+_ALPHA.charAt((b10>>12)&63)+_ALPHA.charAt((b10>>6)&63)+_PADCHAR);break}return x.join("")}return{decode:_decode,encode:_encode,VERSION:_VERSION}}(jQuery));
--------------------------------------------------------------------------------
/js/osv.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author troffmo5 / http://github.com/troffmo5
3 | *
4 | * Google Street View viewer for the Oculus Rift
5 | */
6 |
7 | // Parameters
8 | // ----------------------------------------------
9 | var QUALITY = 3;
10 | var DEFAULT_LOCATION = { lat:44.301945982379095, lng:9.211585521697998 };
11 | var USE_TRACKER = false;
12 | var GAMEPAD_SPEED = 0.04;
13 | var DEADZONE = 0.2;
14 | var SHOW_SETTINGS = true;
15 | var NAV_DELTA = 45;
16 | var FAR = 1000;
17 | var USE_DEPTH = true;
18 | var WORLD_FACTOR = 1.0;
19 |
20 | // Globals
21 | // ----------------------------------------------
22 | var WIDTH, HEIGHT;
23 | var currHeading = 0;
24 | var centerHeading = 0;
25 | var navList = [];
26 |
27 | var headingVector = new THREE.Euler();
28 | var gamepadMoveVector = new THREE.Vector3();
29 |
30 | // Utility function
31 | // ----------------------------------------------
32 | function angleRangeDeg(angle) {
33 | angle %= 360;
34 | if (angle < 0) angle += 360;
35 | return angle;
36 | }
37 |
38 | function angleRangeRad(angle) {
39 | angle %= 2*Math.PI;
40 | if (angle < 0) angle += 2*Math.PI;
41 | return angle;
42 | }
43 |
44 | function deltaAngleDeg(a,b) {
45 | return Math.min(360-(Math.abs(a-b)%360),Math.abs(a-b)%360);
46 | }
47 |
48 | function deltaAngleRas(a,b) {
49 | // todo
50 | }
51 |
52 |
53 | // ----------------------------------------------
54 |
55 | function initWebGL() {
56 | // create scene
57 | scene = new THREE.Scene();
58 |
59 | // Create camera
60 | camera = new THREE.PerspectiveCamera( 60, WIDTH/HEIGHT, 0.1, FAR );
61 | camera.target = new THREE.Vector3( 1, 0, 0 );
62 |
63 | controls = new THREE.VRControls(camera);
64 |
65 | scene.add( camera );
66 |
67 | // Add projection sphere
68 | projSphere = new THREE.Mesh( new THREE.SphereGeometry( 500, 512, 256, 0, Math.PI * 2, 0, Math.PI), new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture('placeholder.png'), side: THREE.DoubleSide}) );
69 | projSphere.geometry.dynamic = true;
70 | scene.add( projSphere );
71 |
72 | // Add Progress Bar
73 | progBarContainer = new THREE.Mesh( new THREE.BoxGeometry(1.2,0.2,0.1), new THREE.MeshBasicMaterial({color: 0xaaaaaa}));
74 | progBarContainer.translateZ(-3);
75 | camera.add( progBarContainer );
76 |
77 | progBar = new THREE.Mesh( new THREE.BoxGeometry(1.0,0.1,0.1), new THREE.MeshBasicMaterial({color: 0x0000ff}));
78 | progBar.translateZ(0.2);
79 | progBarContainer.add(progBar);
80 |
81 | // Create render
82 | try {
83 | renderer = new THREE.WebGLRenderer();
84 | }
85 | catch(e){
86 | alert('This application needs WebGL enabled!');
87 | return false;
88 | }
89 |
90 | renderer.autoClearColor = false;
91 | renderer.setSize( WIDTH, HEIGHT );
92 |
93 | effect = new THREE.VREffect( renderer );
94 | effect.setSize(WIDTH, HEIGHT );
95 |
96 | vrmgr = new WebVRManager(effect);
97 |
98 | var viewer = $('#viewer');
99 | viewer.append(renderer.domElement);
100 | }
101 |
102 | function initControls() {
103 |
104 | // Keyboard
105 | // ---------------------------------------
106 | var lastSpaceKeyTime = new Date(),
107 | lastCtrlKeyTime = lastSpaceKeyTime;
108 |
109 | $(document).keydown(function(e) {
110 | //console.log(e.keyCode);
111 | switch(e.keyCode) {
112 | case 32: // Space
113 | var spaceKeyTime = new Date();
114 | if (spaceKeyTime-lastSpaceKeyTime < 300) {
115 | $('.ui').toggle(200);
116 | }
117 | lastSpaceKeyTime = spaceKeyTime;
118 | break;
119 | case 17: // Ctrl
120 | var ctrlKeyTime = new Date();
121 | if (ctrlKeyTime-lastCtrlKeyTime < 300) {
122 | moveToNextPlace();
123 | }
124 | lastCtrlKeyTime = ctrlKeyTime;
125 | break;
126 | case 18: // Alt
127 | USE_DEPTH = !USE_DEPTH;
128 | $('#depth').prop('checked', USE_DEPTH);
129 | setSphereGeometry();
130 | break;
131 | }
132 | });
133 |
134 | // Mouse
135 | // ---------------------------------------
136 | var viewer = $('#viewer');
137 |
138 | viewer.dblclick(function() {
139 | moveToNextPlace();
140 | });
141 |
142 | // Gamepad
143 | // ---------------------------------------
144 | gamepad = new Gamepad();
145 | gamepad.bind(Gamepad.Event.CONNECTED, function(device) {
146 | console.log("Gamepad CONNECTED");
147 | });
148 |
149 | gamepad.bind(Gamepad.Event.BUTTON_DOWN, function(e) {
150 | if (e.control == "FACE_2") {
151 | $('.ui').toggle(200);
152 | }
153 | });
154 |
155 | // Look for tick event so that we can hold down the FACE_1 button and
156 | // continually move in the current direction
157 | gamepad.bind(Gamepad.Event.TICK, function(gamepads) {
158 | // Multiple calls before next place has finished loading do not matter
159 | // GSVPano library will ignore these
160 | if (gamepads[0].state.FACE_1 === 1) {
161 | moveToNextPlace();
162 | }
163 | });
164 |
165 | gamepad.bind(Gamepad.Event.AXIS_CHANGED, function(e) {
166 |
167 | // ignore deadzone
168 | var value = e.value;
169 | if (value < -DEADZONE) value = value + DEADZONE;
170 | else if(value > DEADZONE) value = value - DEADZONE;
171 | else value = 0;
172 |
173 | if (e.axis == "LEFT_STICK_X") {
174 | gamepadMoveVector.y = -value*GAMEPAD_SPEED;
175 | }
176 | else if (e.axis == "LEFT_STICK_Y") {
177 | gamepadMoveVector.x = -value*GAMEPAD_SPEED;
178 | }
179 | });
180 |
181 | if (!gamepad.init()) {
182 | //console.log("Gamepad not supported");
183 | }
184 | }
185 |
186 | function initGui()
187 | {
188 | if (!SHOW_SETTINGS) {
189 | $('.ui').hide();
190 | }
191 |
192 | $('#depth').change(function(event) {
193 | USE_DEPTH = $('#depth').is(':checked');
194 | setSphereGeometry();
195 | });
196 |
197 | window.addEventListener( 'resize', resize, false );
198 |
199 | }
200 |
201 | function initPano() {
202 | panoLoader = new GSVPANO.PanoLoader();
203 | panoDepthLoader = new GSVPANO.PanoDepthLoader();
204 | panoLoader.setZoom(QUALITY);
205 |
206 | panoLoader.onProgress = function( progress ) {
207 | if (progress > 0) {
208 | progBar.visible = true;
209 | progBar.scale = new THREE.Vector3(progress/100.0,1,1);
210 | }
211 | $(".mapprogress").progressbar("option", "value", progress);
212 |
213 | };
214 | panoLoader.onPanoramaData = function( result ) {
215 | progBarContainer.visible = true;
216 | progBar.visible = false;
217 | $('.mapprogress').show();
218 | };
219 |
220 | panoLoader.onNoPanoramaData = function( status ) {
221 | //alert('no data!');
222 | };
223 |
224 | panoLoader.onPanoramaLoad = function() {
225 | var a = THREE.Math.degToRad(90-panoLoader.heading);
226 | projSphere.quaternion.setFromEuler(new THREE.Euler(0, a, 0, 'YZX'));
227 |
228 | projSphere.material.wireframe = false;
229 | projSphere.material.map.needsUpdate = true;
230 | projSphere.material.map = new THREE.Texture( this.canvas );
231 | projSphere.material.map.needsUpdate = true;
232 | centerHeading = panoLoader.heading;
233 |
234 | progBarContainer.visible = false;
235 | progBar.visible = false;
236 |
237 | marker.setMap( null );
238 | marker= new google.maps.Marker({ position: this.location.latLng, map: gmap});
239 | marker.setMap( gmap);
240 |
241 | $('.mapprogress').hide();
242 |
243 | if (window.history) {
244 | var newUrl = '?lat='+this.location.latLng.lat()+'&lng='+this.location.latLng.lng();
245 | newUrl += USE_TRACKER ? '&sock='+escape(WEBSOCKET_ADDR.slice(5)) : '';
246 | newUrl += '&q='+QUALITY;
247 | newUrl += '&s='+$('#settings').is(':visible');
248 | newUrl += '&heading='+currHeading;
249 | window.history.pushState('','',newUrl);
250 | }
251 |
252 | panoDepthLoader.load(this.location.pano);
253 | };
254 |
255 | panoDepthLoader.onDepthLoad = function() {
256 | setSphereGeometry();
257 | };
258 | }
259 |
260 | function setSphereGeometry() {
261 | var geom = projSphere.geometry;
262 | var geomParam = geom.parameters;
263 | var depthMap = panoDepthLoader.depthMap.depthMap;
264 | var y, x, u, v, radius, i=0;
265 | for ( y = 0; y <= geomParam.heightSegments; y ++ ) {
266 | for ( x = 0; x <= geomParam.widthSegments; x ++ ) {
267 | u = x / geomParam.widthSegments;
268 | v = y / geomParam.heightSegments;
269 |
270 | radius = USE_DEPTH ? Math.min(depthMap[y*512 + x], FAR) : 500;
271 |
272 | var vertex = geom.vertices[i];
273 | vertex.x = - radius * Math.cos( geomParam.phiStart + u * geomParam.phiLength ) * Math.sin( geomParam.thetaStart + v * geomParam.thetaLength );
274 | vertex.y = radius * Math.cos( geomParam.thetaStart + v * geomParam.thetaLength );
275 | vertex.z = radius * Math.sin( geomParam.phiStart + u * geomParam.phiLength ) * Math.sin( geomParam.thetaStart + v * geomParam.thetaLength );
276 | i++;
277 | }
278 | }
279 | geom.verticesNeedUpdate = true;
280 | }
281 |
282 | function initGoogleMap() {
283 | $('.mapprogress').progressbar({ value: false });
284 |
285 | currentLocation = new google.maps.LatLng( DEFAULT_LOCATION.lat, DEFAULT_LOCATION.lng );
286 |
287 | var mapel = $('#map');
288 | mapel.on('mousemove', function (e) {
289 | e.stopPropagation();
290 | });
291 | gmap = new google.maps.Map(mapel[0], {
292 | zoom: 14,
293 | center: currentLocation,
294 | mapTypeId: google.maps.MapTypeId.HYBRID,
295 | streetViewControl: false
296 | });
297 | google.maps.event.addListener(gmap, 'click', function(event) {
298 | panoLoader.load(event.latLng);
299 | });
300 |
301 | google.maps.event.addListener(gmap, 'center_changed', function(event) {
302 | });
303 | google.maps.event.addListener(gmap, 'zoom_changed', function(event) {
304 | });
305 | google.maps.event.addListener(gmap, 'maptypeid_changed', function(event) {
306 | });
307 |
308 | svCoverage= new google.maps.StreetViewCoverageLayer();
309 | svCoverage.setMap(gmap);
310 |
311 | geocoder = new google.maps.Geocoder();
312 |
313 | $('#mapsearch').change(function() {
314 | geocoder.geocode( { 'address': $('#mapsearch').val()}, function(results, status) {
315 | if (status == google.maps.GeocoderStatus.OK) {
316 | gmap.setCenter(results[0].geometry.location);
317 | }
318 | });
319 | }).on('keydown', function (e) {
320 | e.stopPropagation();
321 | });
322 |
323 | marker = new google.maps.Marker({ position: currentLocation, map: gmap });
324 | marker.setMap( gmap );
325 | }
326 |
327 | function checkWebVR() {
328 | if(!vrmgr.isWebVRCompatible()) {
329 | $("#webvrinfo").dialog({
330 | modal: true,
331 | buttons: {
332 | Ok: function() {
333 | $(this).dialog("close");
334 | }
335 | }
336 | });
337 | }
338 | else {
339 | $("#webvrinfo").hide();
340 | }
341 | }
342 |
343 |
344 | function moveToNextPlace() {
345 | var nextPoint = null;
346 | var minDelta = 360;
347 | var navList = panoLoader.links;
348 | for (var i = 0; i < navList.length; i++) {
349 | var delta = deltaAngleDeg(currHeading, navList[i].heading);
350 | if (delta < minDelta && delta < NAV_DELTA) {
351 | minDelta = delta;
352 | nextPoint = navList[i].pano;
353 | }
354 | }
355 |
356 | if (nextPoint) {
357 | panoLoader.load(nextPoint);
358 | }
359 | }
360 |
361 | function render() {
362 | if (vrmgr.isVRMode()) {
363 | effect.render( scene, camera );
364 | }
365 | else {
366 | renderer.render(scene, camera);
367 | }
368 | }
369 |
370 | function setUiSize() {
371 | var width = window.innerWidth, hwidth = width/2,
372 | height = window.innerHeight;
373 |
374 | var ui = $('#ui-main');
375 | var hsize=0.60, vsize = 0.40, outOffset=0;
376 | ui.css('width', hwidth*hsize);
377 | ui.css('left', hwidth-hwidth*hsize/2) ;
378 | ui.css('height', height*vsize);
379 | ui.css('margin-top', height*(1-vsize)/2);
380 |
381 | }
382 |
383 | function resize( event ) {
384 | WIDTH = window.innerWidth;
385 | HEIGHT = window.innerHeight;
386 | setUiSize();
387 |
388 | renderer.setSize( WIDTH, HEIGHT );
389 | camera.projectionMatrix.makePerspective( 60, WIDTH /HEIGHT, 1, 1100 );
390 | }
391 |
392 | function loop() {
393 | requestAnimationFrame( loop );
394 |
395 | // Apply movement
396 | // BaseRotationEuler.set( angleRangeRad(BaseRotationEuler.x + gamepadMoveVector.x), angleRangeRad(BaseRotationEuler.y + gamepadMoveVector.y), 0.0 );
397 | // BaseRotation.setFromEuler(BaseRotationEuler, 'YZX');
398 |
399 | // Compute heading
400 | headingVector.setFromQuaternion(camera.quaternion, 'YZX');
401 | currHeading = angleRangeDeg(THREE.Math.radToDeg(-headingVector.y));
402 |
403 | controls.update();
404 |
405 | // render
406 | render();
407 | }
408 |
409 | function getParams() {
410 | var params = {};
411 | var items = window.location.search.substring(1).split("&");
412 | for (var i=0;i= KEY_ANIMATION_DURATION) {
322 | this[angleName] = targetAngle;
323 | clearInterval(this.angleAnimation);
324 | return;
325 | }
326 | // Linearly interpolate the angle some amount.
327 | var percent = elapsed / KEY_ANIMATION_DURATION;
328 | this[angleName] = startAngle + (targetAngle - startAngle) * percent;
329 | }.bind(this), 1000/60);
330 | };
331 |
332 | MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_ = function(e) {
333 | this.rotateStart.set(e.clientX, e.clientY);
334 | this.isDragging = true;
335 | };
336 |
337 | // Very similar to https://gist.github.com/mrflix/8351020
338 | MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_ = function(e) {
339 | if (!this.isDragging && !this.isPointerLocked_()) {
340 | return;
341 | }
342 | // Support pointer lock API.
343 | if (this.isPointerLocked_()) {
344 | var movementX = e.movementX || e.mozMovementX || e.webkitMovementX || 0;
345 | var movementY = e.movementY || e.mozMovementY || e.webkitMovementY || 0;
346 | this.rotateEnd.set(this.rotateStart.x - movementX, this.rotateStart.y - movementY);
347 | } else {
348 | this.rotateEnd.set(e.clientX, e.clientY);
349 | }
350 | // Calculate how much we moved in mouse space.
351 | this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
352 | this.rotateStart.copy(this.rotateEnd);
353 |
354 | // Keep track of the cumulative euler angles.
355 | var element = document.body;
356 | this.phi += 2 * Math.PI * this.rotateDelta.y / element.clientHeight * MOUSE_SPEED_Y;
357 | this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * MOUSE_SPEED_X;
358 |
359 | // Clamp phi to be in [-Math.PI, Math.PI].
360 | this.phi = this.clamp_(this.phi, -Math.PI, Math.PI);
361 | };
362 |
363 | MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_ = function(e) {
364 | this.isDragging = false;
365 | };
366 |
367 | MouseKeyboardPositionSensorVRDevice.prototype.clamp_ = function(value, min, max) {
368 | return Math.min(Math.max(min, value), max);
369 | };
370 |
371 | MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_ = function() {
372 | var el = document.pointerLockElement || document.mozPointerLockElement ||
373 | document.webkitPointerLockElement;
374 | return el !== undefined;
375 | };
376 |
377 | module.exports = MouseKeyboardPositionSensorVRDevice;
378 |
379 | },{"./base.js":1,"./three-math.js":6}],6:[function(require,module,exports){
380 | /*
381 | * A subset of THREE.js, providing mostly quaternion and euler-related
382 | * operations, manually lifted from
383 | * https://github.com/mrdoob/three.js/tree/master/src/math, as of 9c30286b38df039fca389989ff06ea1c15d6bad1
384 | */
385 |
386 | // Only use if the real THREE is not provided.
387 | var THREE = window.THREE || {};
388 |
389 | // If some piece of THREE is missing, fill it in here.
390 | if (!THREE.Quaternion || !THREE.Vector3 || !THREE.Vector2 || !THREE.Euler) {
391 | console.log('No THREE.js found.');
392 |
393 |
394 | /*** START Quaternion ***/
395 |
396 | /**
397 | * @author mikael emtinger / http://gomo.se/
398 | * @author alteredq / http://alteredqualia.com/
399 | * @author WestLangley / http://github.com/WestLangley
400 | * @author bhouston / http://exocortex.com
401 | */
402 |
403 | THREE.Quaternion = function ( x, y, z, w ) {
404 |
405 | this._x = x || 0;
406 | this._y = y || 0;
407 | this._z = z || 0;
408 | this._w = ( w !== undefined ) ? w : 1;
409 |
410 | };
411 |
412 | THREE.Quaternion.prototype = {
413 |
414 | constructor: THREE.Quaternion,
415 |
416 | _x: 0,_y: 0, _z: 0, _w: 0,
417 |
418 | get x () {
419 |
420 | return this._x;
421 |
422 | },
423 |
424 | set x ( value ) {
425 |
426 | this._x = value;
427 | this.onChangeCallback();
428 |
429 | },
430 |
431 | get y () {
432 |
433 | return this._y;
434 |
435 | },
436 |
437 | set y ( value ) {
438 |
439 | this._y = value;
440 | this.onChangeCallback();
441 |
442 | },
443 |
444 | get z () {
445 |
446 | return this._z;
447 |
448 | },
449 |
450 | set z ( value ) {
451 |
452 | this._z = value;
453 | this.onChangeCallback();
454 |
455 | },
456 |
457 | get w () {
458 |
459 | return this._w;
460 |
461 | },
462 |
463 | set w ( value ) {
464 |
465 | this._w = value;
466 | this.onChangeCallback();
467 |
468 | },
469 |
470 | set: function ( x, y, z, w ) {
471 |
472 | this._x = x;
473 | this._y = y;
474 | this._z = z;
475 | this._w = w;
476 |
477 | this.onChangeCallback();
478 |
479 | return this;
480 |
481 | },
482 |
483 | copy: function ( quaternion ) {
484 |
485 | this._x = quaternion.x;
486 | this._y = quaternion.y;
487 | this._z = quaternion.z;
488 | this._w = quaternion.w;
489 |
490 | this.onChangeCallback();
491 |
492 | return this;
493 |
494 | },
495 |
496 | setFromEuler: function ( euler, update ) {
497 |
498 | if ( euler instanceof THREE.Euler === false ) {
499 |
500 | throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
501 | }
502 |
503 | // http://www.mathworks.com/matlabcentral/fileexchange/
504 | // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
505 | // content/SpinCalc.m
506 |
507 | var c1 = Math.cos( euler._x / 2 );
508 | var c2 = Math.cos( euler._y / 2 );
509 | var c3 = Math.cos( euler._z / 2 );
510 | var s1 = Math.sin( euler._x / 2 );
511 | var s2 = Math.sin( euler._y / 2 );
512 | var s3 = Math.sin( euler._z / 2 );
513 |
514 | if ( euler.order === 'XYZ' ) {
515 |
516 | this._x = s1 * c2 * c3 + c1 * s2 * s3;
517 | this._y = c1 * s2 * c3 - s1 * c2 * s3;
518 | this._z = c1 * c2 * s3 + s1 * s2 * c3;
519 | this._w = c1 * c2 * c3 - s1 * s2 * s3;
520 |
521 | } else if ( euler.order === 'YXZ' ) {
522 |
523 | this._x = s1 * c2 * c3 + c1 * s2 * s3;
524 | this._y = c1 * s2 * c3 - s1 * c2 * s3;
525 | this._z = c1 * c2 * s3 - s1 * s2 * c3;
526 | this._w = c1 * c2 * c3 + s1 * s2 * s3;
527 |
528 | } else if ( euler.order === 'ZXY' ) {
529 |
530 | this._x = s1 * c2 * c3 - c1 * s2 * s3;
531 | this._y = c1 * s2 * c3 + s1 * c2 * s3;
532 | this._z = c1 * c2 * s3 + s1 * s2 * c3;
533 | this._w = c1 * c2 * c3 - s1 * s2 * s3;
534 |
535 | } else if ( euler.order === 'ZYX' ) {
536 |
537 | this._x = s1 * c2 * c3 - c1 * s2 * s3;
538 | this._y = c1 * s2 * c3 + s1 * c2 * s3;
539 | this._z = c1 * c2 * s3 - s1 * s2 * c3;
540 | this._w = c1 * c2 * c3 + s1 * s2 * s3;
541 |
542 | } else if ( euler.order === 'YZX' ) {
543 |
544 | this._x = s1 * c2 * c3 + c1 * s2 * s3;
545 | this._y = c1 * s2 * c3 + s1 * c2 * s3;
546 | this._z = c1 * c2 * s3 - s1 * s2 * c3;
547 | this._w = c1 * c2 * c3 - s1 * s2 * s3;
548 |
549 | } else if ( euler.order === 'XZY' ) {
550 |
551 | this._x = s1 * c2 * c3 - c1 * s2 * s3;
552 | this._y = c1 * s2 * c3 - s1 * c2 * s3;
553 | this._z = c1 * c2 * s3 + s1 * s2 * c3;
554 | this._w = c1 * c2 * c3 + s1 * s2 * s3;
555 |
556 | }
557 |
558 | if ( update !== false ) this.onChangeCallback();
559 |
560 | return this;
561 |
562 | },
563 |
564 | setFromAxisAngle: function ( axis, angle ) {
565 |
566 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
567 |
568 | // assumes axis is normalized
569 |
570 | var halfAngle = angle / 2, s = Math.sin( halfAngle );
571 |
572 | this._x = axis.x * s;
573 | this._y = axis.y * s;
574 | this._z = axis.z * s;
575 | this._w = Math.cos( halfAngle );
576 |
577 | this.onChangeCallback();
578 |
579 | return this;
580 |
581 | },
582 |
583 | setFromRotationMatrix: function ( m ) {
584 |
585 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
586 |
587 | // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
588 |
589 | var te = m.elements,
590 |
591 | m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
592 | m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
593 | m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
594 |
595 | trace = m11 + m22 + m33,
596 | s;
597 |
598 | if ( trace > 0 ) {
599 |
600 | s = 0.5 / Math.sqrt( trace + 1.0 );
601 |
602 | this._w = 0.25 / s;
603 | this._x = ( m32 - m23 ) * s;
604 | this._y = ( m13 - m31 ) * s;
605 | this._z = ( m21 - m12 ) * s;
606 |
607 | } else if ( m11 > m22 && m11 > m33 ) {
608 |
609 | s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
610 |
611 | this._w = ( m32 - m23 ) / s;
612 | this._x = 0.25 * s;
613 | this._y = ( m12 + m21 ) / s;
614 | this._z = ( m13 + m31 ) / s;
615 |
616 | } else if ( m22 > m33 ) {
617 |
618 | s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
619 |
620 | this._w = ( m13 - m31 ) / s;
621 | this._x = ( m12 + m21 ) / s;
622 | this._y = 0.25 * s;
623 | this._z = ( m23 + m32 ) / s;
624 |
625 | } else {
626 |
627 | s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
628 |
629 | this._w = ( m21 - m12 ) / s;
630 | this._x = ( m13 + m31 ) / s;
631 | this._y = ( m23 + m32 ) / s;
632 | this._z = 0.25 * s;
633 |
634 | }
635 |
636 | this.onChangeCallback();
637 |
638 | return this;
639 |
640 | },
641 |
642 | setFromUnitVectors: function () {
643 |
644 | // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
645 |
646 | // assumes direction vectors vFrom and vTo are normalized
647 |
648 | var v1, r;
649 |
650 | var EPS = 0.000001;
651 |
652 | return function ( vFrom, vTo ) {
653 |
654 | if ( v1 === undefined ) v1 = new THREE.Vector3();
655 |
656 | r = vFrom.dot( vTo ) + 1;
657 |
658 | if ( r < EPS ) {
659 |
660 | r = 0;
661 |
662 | if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
663 |
664 | v1.set( - vFrom.y, vFrom.x, 0 );
665 |
666 | } else {
667 |
668 | v1.set( 0, - vFrom.z, vFrom.y );
669 |
670 | }
671 |
672 | } else {
673 |
674 | v1.crossVectors( vFrom, vTo );
675 |
676 | }
677 |
678 | this._x = v1.x;
679 | this._y = v1.y;
680 | this._z = v1.z;
681 | this._w = r;
682 |
683 | this.normalize();
684 |
685 | return this;
686 |
687 | }
688 |
689 | }(),
690 |
691 | inverse: function () {
692 |
693 | this.conjugate().normalize();
694 |
695 | return this;
696 |
697 | },
698 |
699 | conjugate: function () {
700 |
701 | this._x *= - 1;
702 | this._y *= - 1;
703 | this._z *= - 1;
704 |
705 | this.onChangeCallback();
706 |
707 | return this;
708 |
709 | },
710 |
711 | dot: function ( v ) {
712 |
713 | return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
714 |
715 | },
716 |
717 | lengthSq: function () {
718 |
719 | return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
720 |
721 | },
722 |
723 | length: function () {
724 |
725 | return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
726 |
727 | },
728 |
729 | normalize: function () {
730 |
731 | var l = this.length();
732 |
733 | if ( l === 0 ) {
734 |
735 | this._x = 0;
736 | this._y = 0;
737 | this._z = 0;
738 | this._w = 1;
739 |
740 | } else {
741 |
742 | l = 1 / l;
743 |
744 | this._x = this._x * l;
745 | this._y = this._y * l;
746 | this._z = this._z * l;
747 | this._w = this._w * l;
748 |
749 | }
750 |
751 | this.onChangeCallback();
752 |
753 | return this;
754 |
755 | },
756 |
757 | multiply: function ( q, p ) {
758 |
759 | if ( p !== undefined ) {
760 |
761 | console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
762 | return this.multiplyQuaternions( q, p );
763 |
764 | }
765 |
766 | return this.multiplyQuaternions( this, q );
767 |
768 | },
769 |
770 | multiplyQuaternions: function ( a, b ) {
771 |
772 | // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
773 |
774 | var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
775 | var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
776 |
777 | this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
778 | this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
779 | this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
780 | this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
781 |
782 | this.onChangeCallback();
783 |
784 | return this;
785 |
786 | },
787 |
788 | multiplyVector3: function ( vector ) {
789 |
790 | console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
791 | return vector.applyQuaternion( this );
792 |
793 | },
794 |
795 | slerp: function ( qb, t ) {
796 |
797 | if ( t === 0 ) return this;
798 | if ( t === 1 ) return this.copy( qb );
799 |
800 | var x = this._x, y = this._y, z = this._z, w = this._w;
801 |
802 | // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
803 |
804 | var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
805 |
806 | if ( cosHalfTheta < 0 ) {
807 |
808 | this._w = - qb._w;
809 | this._x = - qb._x;
810 | this._y = - qb._y;
811 | this._z = - qb._z;
812 |
813 | cosHalfTheta = - cosHalfTheta;
814 |
815 | } else {
816 |
817 | this.copy( qb );
818 |
819 | }
820 |
821 | if ( cosHalfTheta >= 1.0 ) {
822 |
823 | this._w = w;
824 | this._x = x;
825 | this._y = y;
826 | this._z = z;
827 |
828 | return this;
829 |
830 | }
831 |
832 | var halfTheta = Math.acos( cosHalfTheta );
833 | var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
834 |
835 | if ( Math.abs( sinHalfTheta ) < 0.001 ) {
836 |
837 | this._w = 0.5 * ( w + this._w );
838 | this._x = 0.5 * ( x + this._x );
839 | this._y = 0.5 * ( y + this._y );
840 | this._z = 0.5 * ( z + this._z );
841 |
842 | return this;
843 |
844 | }
845 |
846 | var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
847 | ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
848 |
849 | this._w = ( w * ratioA + this._w * ratioB );
850 | this._x = ( x * ratioA + this._x * ratioB );
851 | this._y = ( y * ratioA + this._y * ratioB );
852 | this._z = ( z * ratioA + this._z * ratioB );
853 |
854 | this.onChangeCallback();
855 |
856 | return this;
857 |
858 | },
859 |
860 | equals: function ( quaternion ) {
861 |
862 | return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
863 |
864 | },
865 |
866 | fromArray: function ( array, offset ) {
867 |
868 | if ( offset === undefined ) offset = 0;
869 |
870 | this._x = array[ offset ];
871 | this._y = array[ offset + 1 ];
872 | this._z = array[ offset + 2 ];
873 | this._w = array[ offset + 3 ];
874 |
875 | this.onChangeCallback();
876 |
877 | return this;
878 |
879 | },
880 |
881 | toArray: function ( array, offset ) {
882 |
883 | if ( array === undefined ) array = [];
884 | if ( offset === undefined ) offset = 0;
885 |
886 | array[ offset ] = this._x;
887 | array[ offset + 1 ] = this._y;
888 | array[ offset + 2 ] = this._z;
889 | array[ offset + 3 ] = this._w;
890 |
891 | return array;
892 |
893 | },
894 |
895 | onChange: function ( callback ) {
896 |
897 | this.onChangeCallback = callback;
898 |
899 | return this;
900 |
901 | },
902 |
903 | onChangeCallback: function () {},
904 |
905 | clone: function () {
906 |
907 | return new THREE.Quaternion( this._x, this._y, this._z, this._w );
908 |
909 | }
910 |
911 | };
912 |
913 | THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
914 |
915 | return qm.copy( qa ).slerp( qb, t );
916 |
917 | }
918 |
919 | /*** END Quaternion ***/
920 | /*** START Vector2 ***/
921 | /**
922 | * @author mrdoob / http://mrdoob.com/
923 | * @author philogb / http://blog.thejit.org/
924 | * @author egraether / http://egraether.com/
925 | * @author zz85 / http://www.lab4games.net/zz85/blog
926 | */
927 |
928 | THREE.Vector2 = function ( x, y ) {
929 |
930 | this.x = x || 0;
931 | this.y = y || 0;
932 |
933 | };
934 |
935 | THREE.Vector2.prototype = {
936 |
937 | constructor: THREE.Vector2,
938 |
939 | set: function ( x, y ) {
940 |
941 | this.x = x;
942 | this.y = y;
943 |
944 | return this;
945 |
946 | },
947 |
948 | setX: function ( x ) {
949 |
950 | this.x = x;
951 |
952 | return this;
953 |
954 | },
955 |
956 | setY: function ( y ) {
957 |
958 | this.y = y;
959 |
960 | return this;
961 |
962 | },
963 |
964 | setComponent: function ( index, value ) {
965 |
966 | switch ( index ) {
967 |
968 | case 0: this.x = value; break;
969 | case 1: this.y = value; break;
970 | default: throw new Error( 'index is out of range: ' + index );
971 |
972 | }
973 |
974 | },
975 |
976 | getComponent: function ( index ) {
977 |
978 | switch ( index ) {
979 |
980 | case 0: return this.x;
981 | case 1: return this.y;
982 | default: throw new Error( 'index is out of range: ' + index );
983 |
984 | }
985 |
986 | },
987 |
988 | copy: function ( v ) {
989 |
990 | this.x = v.x;
991 | this.y = v.y;
992 |
993 | return this;
994 |
995 | },
996 |
997 | add: function ( v, w ) {
998 |
999 | if ( w !== undefined ) {
1000 |
1001 | console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
1002 | return this.addVectors( v, w );
1003 |
1004 | }
1005 |
1006 | this.x += v.x;
1007 | this.y += v.y;
1008 |
1009 | return this;
1010 |
1011 | },
1012 |
1013 | addVectors: function ( a, b ) {
1014 |
1015 | this.x = a.x + b.x;
1016 | this.y = a.y + b.y;
1017 |
1018 | return this;
1019 |
1020 | },
1021 |
1022 | addScalar: function ( s ) {
1023 |
1024 | this.x += s;
1025 | this.y += s;
1026 |
1027 | return this;
1028 |
1029 | },
1030 |
1031 | sub: function ( v, w ) {
1032 |
1033 | if ( w !== undefined ) {
1034 |
1035 | console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
1036 | return this.subVectors( v, w );
1037 |
1038 | }
1039 |
1040 | this.x -= v.x;
1041 | this.y -= v.y;
1042 |
1043 | return this;
1044 |
1045 | },
1046 |
1047 | subVectors: function ( a, b ) {
1048 |
1049 | this.x = a.x - b.x;
1050 | this.y = a.y - b.y;
1051 |
1052 | return this;
1053 |
1054 | },
1055 |
1056 | multiply: function ( v ) {
1057 |
1058 | this.x *= v.x;
1059 | this.y *= v.y;
1060 |
1061 | return this;
1062 |
1063 | },
1064 |
1065 | multiplyScalar: function ( s ) {
1066 |
1067 | this.x *= s;
1068 | this.y *= s;
1069 |
1070 | return this;
1071 |
1072 | },
1073 |
1074 | divide: function ( v ) {
1075 |
1076 | this.x /= v.x;
1077 | this.y /= v.y;
1078 |
1079 | return this;
1080 |
1081 | },
1082 |
1083 | divideScalar: function ( scalar ) {
1084 |
1085 | if ( scalar !== 0 ) {
1086 |
1087 | var invScalar = 1 / scalar;
1088 |
1089 | this.x *= invScalar;
1090 | this.y *= invScalar;
1091 |
1092 | } else {
1093 |
1094 | this.x = 0;
1095 | this.y = 0;
1096 |
1097 | }
1098 |
1099 | return this;
1100 |
1101 | },
1102 |
1103 | min: function ( v ) {
1104 |
1105 | if ( this.x > v.x ) {
1106 |
1107 | this.x = v.x;
1108 |
1109 | }
1110 |
1111 | if ( this.y > v.y ) {
1112 |
1113 | this.y = v.y;
1114 |
1115 | }
1116 |
1117 | return this;
1118 |
1119 | },
1120 |
1121 | max: function ( v ) {
1122 |
1123 | if ( this.x < v.x ) {
1124 |
1125 | this.x = v.x;
1126 |
1127 | }
1128 |
1129 | if ( this.y < v.y ) {
1130 |
1131 | this.y = v.y;
1132 |
1133 | }
1134 |
1135 | return this;
1136 |
1137 | },
1138 |
1139 | clamp: function ( min, max ) {
1140 |
1141 | // This function assumes min < max, if this assumption isn't true it will not operate correctly
1142 |
1143 | if ( this.x < min.x ) {
1144 |
1145 | this.x = min.x;
1146 |
1147 | } else if ( this.x > max.x ) {
1148 |
1149 | this.x = max.x;
1150 |
1151 | }
1152 |
1153 | if ( this.y < min.y ) {
1154 |
1155 | this.y = min.y;
1156 |
1157 | } else if ( this.y > max.y ) {
1158 |
1159 | this.y = max.y;
1160 |
1161 | }
1162 |
1163 | return this;
1164 | },
1165 |
1166 | clampScalar: ( function () {
1167 |
1168 | var min, max;
1169 |
1170 | return function ( minVal, maxVal ) {
1171 |
1172 | if ( min === undefined ) {
1173 |
1174 | min = new THREE.Vector2();
1175 | max = new THREE.Vector2();
1176 |
1177 | }
1178 |
1179 | min.set( minVal, minVal );
1180 | max.set( maxVal, maxVal );
1181 |
1182 | return this.clamp( min, max );
1183 |
1184 | };
1185 |
1186 | } )(),
1187 |
1188 | floor: function () {
1189 |
1190 | this.x = Math.floor( this.x );
1191 | this.y = Math.floor( this.y );
1192 |
1193 | return this;
1194 |
1195 | },
1196 |
1197 | ceil: function () {
1198 |
1199 | this.x = Math.ceil( this.x );
1200 | this.y = Math.ceil( this.y );
1201 |
1202 | return this;
1203 |
1204 | },
1205 |
1206 | round: function () {
1207 |
1208 | this.x = Math.round( this.x );
1209 | this.y = Math.round( this.y );
1210 |
1211 | return this;
1212 |
1213 | },
1214 |
1215 | roundToZero: function () {
1216 |
1217 | this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
1218 | this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
1219 |
1220 | return this;
1221 |
1222 | },
1223 |
1224 | negate: function () {
1225 |
1226 | this.x = - this.x;
1227 | this.y = - this.y;
1228 |
1229 | return this;
1230 |
1231 | },
1232 |
1233 | dot: function ( v ) {
1234 |
1235 | return this.x * v.x + this.y * v.y;
1236 |
1237 | },
1238 |
1239 | lengthSq: function () {
1240 |
1241 | return this.x * this.x + this.y * this.y;
1242 |
1243 | },
1244 |
1245 | length: function () {
1246 |
1247 | return Math.sqrt( this.x * this.x + this.y * this.y );
1248 |
1249 | },
1250 |
1251 | normalize: function () {
1252 |
1253 | return this.divideScalar( this.length() );
1254 |
1255 | },
1256 |
1257 | distanceTo: function ( v ) {
1258 |
1259 | return Math.sqrt( this.distanceToSquared( v ) );
1260 |
1261 | },
1262 |
1263 | distanceToSquared: function ( v ) {
1264 |
1265 | var dx = this.x - v.x, dy = this.y - v.y;
1266 | return dx * dx + dy * dy;
1267 |
1268 | },
1269 |
1270 | setLength: function ( l ) {
1271 |
1272 | var oldLength = this.length();
1273 |
1274 | if ( oldLength !== 0 && l !== oldLength ) {
1275 |
1276 | this.multiplyScalar( l / oldLength );
1277 | }
1278 |
1279 | return this;
1280 |
1281 | },
1282 |
1283 | lerp: function ( v, alpha ) {
1284 |
1285 | this.x += ( v.x - this.x ) * alpha;
1286 | this.y += ( v.y - this.y ) * alpha;
1287 |
1288 | return this;
1289 |
1290 | },
1291 |
1292 | equals: function ( v ) {
1293 |
1294 | return ( ( v.x === this.x ) && ( v.y === this.y ) );
1295 |
1296 | },
1297 |
1298 | fromArray: function ( array, offset ) {
1299 |
1300 | if ( offset === undefined ) offset = 0;
1301 |
1302 | this.x = array[ offset ];
1303 | this.y = array[ offset + 1 ];
1304 |
1305 | return this;
1306 |
1307 | },
1308 |
1309 | toArray: function ( array, offset ) {
1310 |
1311 | if ( array === undefined ) array = [];
1312 | if ( offset === undefined ) offset = 0;
1313 |
1314 | array[ offset ] = this.x;
1315 | array[ offset + 1 ] = this.y;
1316 |
1317 | return array;
1318 |
1319 | },
1320 |
1321 | fromAttribute: function ( attribute, index, offset ) {
1322 |
1323 | if ( offset === undefined ) offset = 0;
1324 |
1325 | index = index * attribute.itemSize + offset;
1326 |
1327 | this.x = attribute.array[ index ];
1328 | this.y = attribute.array[ index + 1 ];
1329 |
1330 | return this;
1331 |
1332 | },
1333 |
1334 | clone: function () {
1335 |
1336 | return new THREE.Vector2( this.x, this.y );
1337 |
1338 | }
1339 |
1340 | };
1341 | /*** END Vector2 ***/
1342 | /*** START Vector3 ***/
1343 |
1344 | /**
1345 | * @author mrdoob / http://mrdoob.com/
1346 | * @author *kile / http://kile.stravaganza.org/
1347 | * @author philogb / http://blog.thejit.org/
1348 | * @author mikael emtinger / http://gomo.se/
1349 | * @author egraether / http://egraether.com/
1350 | * @author WestLangley / http://github.com/WestLangley
1351 | */
1352 |
1353 | THREE.Vector3 = function ( x, y, z ) {
1354 |
1355 | this.x = x || 0;
1356 | this.y = y || 0;
1357 | this.z = z || 0;
1358 |
1359 | };
1360 |
1361 | THREE.Vector3.prototype = {
1362 |
1363 | constructor: THREE.Vector3,
1364 |
1365 | set: function ( x, y, z ) {
1366 |
1367 | this.x = x;
1368 | this.y = y;
1369 | this.z = z;
1370 |
1371 | return this;
1372 |
1373 | },
1374 |
1375 | setX: function ( x ) {
1376 |
1377 | this.x = x;
1378 |
1379 | return this;
1380 |
1381 | },
1382 |
1383 | setY: function ( y ) {
1384 |
1385 | this.y = y;
1386 |
1387 | return this;
1388 |
1389 | },
1390 |
1391 | setZ: function ( z ) {
1392 |
1393 | this.z = z;
1394 |
1395 | return this;
1396 |
1397 | },
1398 |
1399 | setComponent: function ( index, value ) {
1400 |
1401 | switch ( index ) {
1402 |
1403 | case 0: this.x = value; break;
1404 | case 1: this.y = value; break;
1405 | case 2: this.z = value; break;
1406 | default: throw new Error( 'index is out of range: ' + index );
1407 |
1408 | }
1409 |
1410 | },
1411 |
1412 | getComponent: function ( index ) {
1413 |
1414 | switch ( index ) {
1415 |
1416 | case 0: return this.x;
1417 | case 1: return this.y;
1418 | case 2: return this.z;
1419 | default: throw new Error( 'index is out of range: ' + index );
1420 |
1421 | }
1422 |
1423 | },
1424 |
1425 | copy: function ( v ) {
1426 |
1427 | this.x = v.x;
1428 | this.y = v.y;
1429 | this.z = v.z;
1430 |
1431 | return this;
1432 |
1433 | },
1434 |
1435 | add: function ( v, w ) {
1436 |
1437 | if ( w !== undefined ) {
1438 |
1439 | console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
1440 | return this.addVectors( v, w );
1441 |
1442 | }
1443 |
1444 | this.x += v.x;
1445 | this.y += v.y;
1446 | this.z += v.z;
1447 |
1448 | return this;
1449 |
1450 | },
1451 |
1452 | addScalar: function ( s ) {
1453 |
1454 | this.x += s;
1455 | this.y += s;
1456 | this.z += s;
1457 |
1458 | return this;
1459 |
1460 | },
1461 |
1462 | addVectors: function ( a, b ) {
1463 |
1464 | this.x = a.x + b.x;
1465 | this.y = a.y + b.y;
1466 | this.z = a.z + b.z;
1467 |
1468 | return this;
1469 |
1470 | },
1471 |
1472 | sub: function ( v, w ) {
1473 |
1474 | if ( w !== undefined ) {
1475 |
1476 | console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
1477 | return this.subVectors( v, w );
1478 |
1479 | }
1480 |
1481 | this.x -= v.x;
1482 | this.y -= v.y;
1483 | this.z -= v.z;
1484 |
1485 | return this;
1486 |
1487 | },
1488 |
1489 | subVectors: function ( a, b ) {
1490 |
1491 | this.x = a.x - b.x;
1492 | this.y = a.y - b.y;
1493 | this.z = a.z - b.z;
1494 |
1495 | return this;
1496 |
1497 | },
1498 |
1499 | multiply: function ( v, w ) {
1500 |
1501 | if ( w !== undefined ) {
1502 |
1503 | console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
1504 | return this.multiplyVectors( v, w );
1505 |
1506 | }
1507 |
1508 | this.x *= v.x;
1509 | this.y *= v.y;
1510 | this.z *= v.z;
1511 |
1512 | return this;
1513 |
1514 | },
1515 |
1516 | multiplyScalar: function ( scalar ) {
1517 |
1518 | this.x *= scalar;
1519 | this.y *= scalar;
1520 | this.z *= scalar;
1521 |
1522 | return this;
1523 |
1524 | },
1525 |
1526 | multiplyVectors: function ( a, b ) {
1527 |
1528 | this.x = a.x * b.x;
1529 | this.y = a.y * b.y;
1530 | this.z = a.z * b.z;
1531 |
1532 | return this;
1533 |
1534 | },
1535 |
1536 | applyEuler: function () {
1537 |
1538 | var quaternion;
1539 |
1540 | return function ( euler ) {
1541 |
1542 | if ( euler instanceof THREE.Euler === false ) {
1543 |
1544 | console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );
1545 |
1546 | }
1547 |
1548 | if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
1549 |
1550 | this.applyQuaternion( quaternion.setFromEuler( euler ) );
1551 |
1552 | return this;
1553 |
1554 | };
1555 |
1556 | }(),
1557 |
1558 | applyAxisAngle: function () {
1559 |
1560 | var quaternion;
1561 |
1562 | return function ( axis, angle ) {
1563 |
1564 | if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
1565 |
1566 | this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
1567 |
1568 | return this;
1569 |
1570 | };
1571 |
1572 | }(),
1573 |
1574 | applyMatrix3: function ( m ) {
1575 |
1576 | var x = this.x;
1577 | var y = this.y;
1578 | var z = this.z;
1579 |
1580 | var e = m.elements;
1581 |
1582 | this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
1583 | this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
1584 | this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
1585 |
1586 | return this;
1587 |
1588 | },
1589 |
1590 | applyMatrix4: function ( m ) {
1591 |
1592 | // input: THREE.Matrix4 affine matrix
1593 |
1594 | var x = this.x, y = this.y, z = this.z;
1595 |
1596 | var e = m.elements;
1597 |
1598 | this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];
1599 | this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];
1600 | this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
1601 |
1602 | return this;
1603 |
1604 | },
1605 |
1606 | applyProjection: function ( m ) {
1607 |
1608 | // input: THREE.Matrix4 projection matrix
1609 |
1610 | var x = this.x, y = this.y, z = this.z;
1611 |
1612 | var e = m.elements;
1613 | var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
1614 |
1615 | this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;
1616 | this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;
1617 | this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
1618 |
1619 | return this;
1620 |
1621 | },
1622 |
1623 | applyQuaternion: function ( q ) {
1624 |
1625 | var x = this.x;
1626 | var y = this.y;
1627 | var z = this.z;
1628 |
1629 | var qx = q.x;
1630 | var qy = q.y;
1631 | var qz = q.z;
1632 | var qw = q.w;
1633 |
1634 | // calculate quat * vector
1635 |
1636 | var ix = qw * x + qy * z - qz * y;
1637 | var iy = qw * y + qz * x - qx * z;
1638 | var iz = qw * z + qx * y - qy * x;
1639 | var iw = - qx * x - qy * y - qz * z;
1640 |
1641 | // calculate result * inverse quat
1642 |
1643 | this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
1644 | this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
1645 | this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
1646 |
1647 | return this;
1648 |
1649 | },
1650 |
1651 | project: function () {
1652 |
1653 | var matrix;
1654 |
1655 | return function ( camera ) {
1656 |
1657 | if ( matrix === undefined ) matrix = new THREE.Matrix4();
1658 |
1659 | matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
1660 | return this.applyProjection( matrix );
1661 |
1662 | };
1663 |
1664 | }(),
1665 |
1666 | unproject: function () {
1667 |
1668 | var matrix;
1669 |
1670 | return function ( camera ) {
1671 |
1672 | if ( matrix === undefined ) matrix = new THREE.Matrix4();
1673 |
1674 | matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
1675 | return this.applyProjection( matrix );
1676 |
1677 | };
1678 |
1679 | }(),
1680 |
1681 | transformDirection: function ( m ) {
1682 |
1683 | // input: THREE.Matrix4 affine matrix
1684 | // vector interpreted as a direction
1685 |
1686 | var x = this.x, y = this.y, z = this.z;
1687 |
1688 | var e = m.elements;
1689 |
1690 | this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
1691 | this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
1692 | this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
1693 |
1694 | this.normalize();
1695 |
1696 | return this;
1697 |
1698 | },
1699 |
1700 | divide: function ( v ) {
1701 |
1702 | this.x /= v.x;
1703 | this.y /= v.y;
1704 | this.z /= v.z;
1705 |
1706 | return this;
1707 |
1708 | },
1709 |
1710 | divideScalar: function ( scalar ) {
1711 |
1712 | if ( scalar !== 0 ) {
1713 |
1714 | var invScalar = 1 / scalar;
1715 |
1716 | this.x *= invScalar;
1717 | this.y *= invScalar;
1718 | this.z *= invScalar;
1719 |
1720 | } else {
1721 |
1722 | this.x = 0;
1723 | this.y = 0;
1724 | this.z = 0;
1725 |
1726 | }
1727 |
1728 | return this;
1729 |
1730 | },
1731 |
1732 | min: function ( v ) {
1733 |
1734 | if ( this.x > v.x ) {
1735 |
1736 | this.x = v.x;
1737 |
1738 | }
1739 |
1740 | if ( this.y > v.y ) {
1741 |
1742 | this.y = v.y;
1743 |
1744 | }
1745 |
1746 | if ( this.z > v.z ) {
1747 |
1748 | this.z = v.z;
1749 |
1750 | }
1751 |
1752 | return this;
1753 |
1754 | },
1755 |
1756 | max: function ( v ) {
1757 |
1758 | if ( this.x < v.x ) {
1759 |
1760 | this.x = v.x;
1761 |
1762 | }
1763 |
1764 | if ( this.y < v.y ) {
1765 |
1766 | this.y = v.y;
1767 |
1768 | }
1769 |
1770 | if ( this.z < v.z ) {
1771 |
1772 | this.z = v.z;
1773 |
1774 | }
1775 |
1776 | return this;
1777 |
1778 | },
1779 |
1780 | clamp: function ( min, max ) {
1781 |
1782 | // This function assumes min < max, if this assumption isn't true it will not operate correctly
1783 |
1784 | if ( this.x < min.x ) {
1785 |
1786 | this.x = min.x;
1787 |
1788 | } else if ( this.x > max.x ) {
1789 |
1790 | this.x = max.x;
1791 |
1792 | }
1793 |
1794 | if ( this.y < min.y ) {
1795 |
1796 | this.y = min.y;
1797 |
1798 | } else if ( this.y > max.y ) {
1799 |
1800 | this.y = max.y;
1801 |
1802 | }
1803 |
1804 | if ( this.z < min.z ) {
1805 |
1806 | this.z = min.z;
1807 |
1808 | } else if ( this.z > max.z ) {
1809 |
1810 | this.z = max.z;
1811 |
1812 | }
1813 |
1814 | return this;
1815 |
1816 | },
1817 |
1818 | clampScalar: ( function () {
1819 |
1820 | var min, max;
1821 |
1822 | return function ( minVal, maxVal ) {
1823 |
1824 | if ( min === undefined ) {
1825 |
1826 | min = new THREE.Vector3();
1827 | max = new THREE.Vector3();
1828 |
1829 | }
1830 |
1831 | min.set( minVal, minVal, minVal );
1832 | max.set( maxVal, maxVal, maxVal );
1833 |
1834 | return this.clamp( min, max );
1835 |
1836 | };
1837 |
1838 | } )(),
1839 |
1840 | floor: function () {
1841 |
1842 | this.x = Math.floor( this.x );
1843 | this.y = Math.floor( this.y );
1844 | this.z = Math.floor( this.z );
1845 |
1846 | return this;
1847 |
1848 | },
1849 |
1850 | ceil: function () {
1851 |
1852 | this.x = Math.ceil( this.x );
1853 | this.y = Math.ceil( this.y );
1854 | this.z = Math.ceil( this.z );
1855 |
1856 | return this;
1857 |
1858 | },
1859 |
1860 | round: function () {
1861 |
1862 | this.x = Math.round( this.x );
1863 | this.y = Math.round( this.y );
1864 | this.z = Math.round( this.z );
1865 |
1866 | return this;
1867 |
1868 | },
1869 |
1870 | roundToZero: function () {
1871 |
1872 | this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
1873 | this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
1874 | this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
1875 |
1876 | return this;
1877 |
1878 | },
1879 |
1880 | negate: function () {
1881 |
1882 | this.x = - this.x;
1883 | this.y = - this.y;
1884 | this.z = - this.z;
1885 |
1886 | return this;
1887 |
1888 | },
1889 |
1890 | dot: function ( v ) {
1891 |
1892 | return this.x * v.x + this.y * v.y + this.z * v.z;
1893 |
1894 | },
1895 |
1896 | lengthSq: function () {
1897 |
1898 | return this.x * this.x + this.y * this.y + this.z * this.z;
1899 |
1900 | },
1901 |
1902 | length: function () {
1903 |
1904 | return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
1905 |
1906 | },
1907 |
1908 | lengthManhattan: function () {
1909 |
1910 | return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
1911 |
1912 | },
1913 |
1914 | normalize: function () {
1915 |
1916 | return this.divideScalar( this.length() );
1917 |
1918 | },
1919 |
1920 | setLength: function ( l ) {
1921 |
1922 | var oldLength = this.length();
1923 |
1924 | if ( oldLength !== 0 && l !== oldLength ) {
1925 |
1926 | this.multiplyScalar( l / oldLength );
1927 | }
1928 |
1929 | return this;
1930 |
1931 | },
1932 |
1933 | lerp: function ( v, alpha ) {
1934 |
1935 | this.x += ( v.x - this.x ) * alpha;
1936 | this.y += ( v.y - this.y ) * alpha;
1937 | this.z += ( v.z - this.z ) * alpha;
1938 |
1939 | return this;
1940 |
1941 | },
1942 |
1943 | cross: function ( v, w ) {
1944 |
1945 | if ( w !== undefined ) {
1946 |
1947 | console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
1948 | return this.crossVectors( v, w );
1949 |
1950 | }
1951 |
1952 | var x = this.x, y = this.y, z = this.z;
1953 |
1954 | this.x = y * v.z - z * v.y;
1955 | this.y = z * v.x - x * v.z;
1956 | this.z = x * v.y - y * v.x;
1957 |
1958 | return this;
1959 |
1960 | },
1961 |
1962 | crossVectors: function ( a, b ) {
1963 |
1964 | var ax = a.x, ay = a.y, az = a.z;
1965 | var bx = b.x, by = b.y, bz = b.z;
1966 |
1967 | this.x = ay * bz - az * by;
1968 | this.y = az * bx - ax * bz;
1969 | this.z = ax * by - ay * bx;
1970 |
1971 | return this;
1972 |
1973 | },
1974 |
1975 | projectOnVector: function () {
1976 |
1977 | var v1, dot;
1978 |
1979 | return function ( vector ) {
1980 |
1981 | if ( v1 === undefined ) v1 = new THREE.Vector3();
1982 |
1983 | v1.copy( vector ).normalize();
1984 |
1985 | dot = this.dot( v1 );
1986 |
1987 | return this.copy( v1 ).multiplyScalar( dot );
1988 |
1989 | };
1990 |
1991 | }(),
1992 |
1993 | projectOnPlane: function () {
1994 |
1995 | var v1;
1996 |
1997 | return function ( planeNormal ) {
1998 |
1999 | if ( v1 === undefined ) v1 = new THREE.Vector3();
2000 |
2001 | v1.copy( this ).projectOnVector( planeNormal );
2002 |
2003 | return this.sub( v1 );
2004 |
2005 | }
2006 |
2007 | }(),
2008 |
2009 | reflect: function () {
2010 |
2011 | // reflect incident vector off plane orthogonal to normal
2012 | // normal is assumed to have unit length
2013 |
2014 | var v1;
2015 |
2016 | return function ( normal ) {
2017 |
2018 | if ( v1 === undefined ) v1 = new THREE.Vector3();
2019 |
2020 | return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
2021 |
2022 | }
2023 |
2024 | }(),
2025 |
2026 | angleTo: function ( v ) {
2027 |
2028 | var theta = this.dot( v ) / ( this.length() * v.length() );
2029 |
2030 | // clamp, to handle numerical problems
2031 |
2032 | return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
2033 |
2034 | },
2035 |
2036 | distanceTo: function ( v ) {
2037 |
2038 | return Math.sqrt( this.distanceToSquared( v ) );
2039 |
2040 | },
2041 |
2042 | distanceToSquared: function ( v ) {
2043 |
2044 | var dx = this.x - v.x;
2045 | var dy = this.y - v.y;
2046 | var dz = this.z - v.z;
2047 |
2048 | return dx * dx + dy * dy + dz * dz;
2049 |
2050 | },
2051 |
2052 | setEulerFromRotationMatrix: function ( m, order ) {
2053 |
2054 | console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
2055 |
2056 | },
2057 |
2058 | setEulerFromQuaternion: function ( q, order ) {
2059 |
2060 | console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
2061 |
2062 | },
2063 |
2064 | getPositionFromMatrix: function ( m ) {
2065 |
2066 | console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
2067 |
2068 | return this.setFromMatrixPosition( m );
2069 |
2070 | },
2071 |
2072 | getScaleFromMatrix: function ( m ) {
2073 |
2074 | console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
2075 |
2076 | return this.setFromMatrixScale( m );
2077 | },
2078 |
2079 | getColumnFromMatrix: function ( index, matrix ) {
2080 |
2081 | console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
2082 |
2083 | return this.setFromMatrixColumn( index, matrix );
2084 |
2085 | },
2086 |
2087 | setFromMatrixPosition: function ( m ) {
2088 |
2089 | this.x = m.elements[ 12 ];
2090 | this.y = m.elements[ 13 ];
2091 | this.z = m.elements[ 14 ];
2092 |
2093 | return this;
2094 |
2095 | },
2096 |
2097 | setFromMatrixScale: function ( m ) {
2098 |
2099 | var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length();
2100 | var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length();
2101 | var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();
2102 |
2103 | this.x = sx;
2104 | this.y = sy;
2105 | this.z = sz;
2106 |
2107 | return this;
2108 | },
2109 |
2110 | setFromMatrixColumn: function ( index, matrix ) {
2111 |
2112 | var offset = index * 4;
2113 |
2114 | var me = matrix.elements;
2115 |
2116 | this.x = me[ offset ];
2117 | this.y = me[ offset + 1 ];
2118 | this.z = me[ offset + 2 ];
2119 |
2120 | return this;
2121 |
2122 | },
2123 |
2124 | equals: function ( v ) {
2125 |
2126 | return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
2127 |
2128 | },
2129 |
2130 | fromArray: function ( array, offset ) {
2131 |
2132 | if ( offset === undefined ) offset = 0;
2133 |
2134 | this.x = array[ offset ];
2135 | this.y = array[ offset + 1 ];
2136 | this.z = array[ offset + 2 ];
2137 |
2138 | return this;
2139 |
2140 | },
2141 |
2142 | toArray: function ( array, offset ) {
2143 |
2144 | if ( array === undefined ) array = [];
2145 | if ( offset === undefined ) offset = 0;
2146 |
2147 | array[ offset ] = this.x;
2148 | array[ offset + 1 ] = this.y;
2149 | array[ offset + 2 ] = this.z;
2150 |
2151 | return array;
2152 |
2153 | },
2154 |
2155 | fromAttribute: function ( attribute, index, offset ) {
2156 |
2157 | if ( offset === undefined ) offset = 0;
2158 |
2159 | index = index * attribute.itemSize + offset;
2160 |
2161 | this.x = attribute.array[ index ];
2162 | this.y = attribute.array[ index + 1 ];
2163 | this.z = attribute.array[ index + 2 ];
2164 |
2165 | return this;
2166 |
2167 | },
2168 |
2169 | clone: function () {
2170 |
2171 | return new THREE.Vector3( this.x, this.y, this.z );
2172 |
2173 | }
2174 |
2175 | };
2176 | /*** END Vector3 ***/
2177 | /*** START Euler ***/
2178 | /**
2179 | * @author mrdoob / http://mrdoob.com/
2180 | * @author WestLangley / http://github.com/WestLangley
2181 | * @author bhouston / http://exocortex.com
2182 | */
2183 |
2184 | THREE.Euler = function ( x, y, z, order ) {
2185 |
2186 | this._x = x || 0;
2187 | this._y = y || 0;
2188 | this._z = z || 0;
2189 | this._order = order || THREE.Euler.DefaultOrder;
2190 |
2191 | };
2192 |
2193 | THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
2194 |
2195 | THREE.Euler.DefaultOrder = 'XYZ';
2196 |
2197 | THREE.Euler.prototype = {
2198 |
2199 | constructor: THREE.Euler,
2200 |
2201 | _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder,
2202 |
2203 | get x () {
2204 |
2205 | return this._x;
2206 |
2207 | },
2208 |
2209 | set x ( value ) {
2210 |
2211 | this._x = value;
2212 | this.onChangeCallback();
2213 |
2214 | },
2215 |
2216 | get y () {
2217 |
2218 | return this._y;
2219 |
2220 | },
2221 |
2222 | set y ( value ) {
2223 |
2224 | this._y = value;
2225 | this.onChangeCallback();
2226 |
2227 | },
2228 |
2229 | get z () {
2230 |
2231 | return this._z;
2232 |
2233 | },
2234 |
2235 | set z ( value ) {
2236 |
2237 | this._z = value;
2238 | this.onChangeCallback();
2239 |
2240 | },
2241 |
2242 | get order () {
2243 |
2244 | return this._order;
2245 |
2246 | },
2247 |
2248 | set order ( value ) {
2249 |
2250 | this._order = value;
2251 | this.onChangeCallback();
2252 |
2253 | },
2254 |
2255 | set: function ( x, y, z, order ) {
2256 |
2257 | this._x = x;
2258 | this._y = y;
2259 | this._z = z;
2260 | this._order = order || this._order;
2261 |
2262 | this.onChangeCallback();
2263 |
2264 | return this;
2265 |
2266 | },
2267 |
2268 | copy: function ( euler ) {
2269 |
2270 | this._x = euler._x;
2271 | this._y = euler._y;
2272 | this._z = euler._z;
2273 | this._order = euler._order;
2274 |
2275 | this.onChangeCallback();
2276 |
2277 | return this;
2278 |
2279 | },
2280 |
2281 | setFromRotationMatrix: function ( m, order, update ) {
2282 |
2283 | var clamp = THREE.Math.clamp;
2284 |
2285 | // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
2286 |
2287 | var te = m.elements;
2288 | var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
2289 | var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
2290 | var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
2291 |
2292 | order = order || this._order;
2293 |
2294 | if ( order === 'XYZ' ) {
2295 |
2296 | this._y = Math.asin( clamp( m13, - 1, 1 ) );
2297 |
2298 | if ( Math.abs( m13 ) < 0.99999 ) {
2299 |
2300 | this._x = Math.atan2( - m23, m33 );
2301 | this._z = Math.atan2( - m12, m11 );
2302 |
2303 | } else {
2304 |
2305 | this._x = Math.atan2( m32, m22 );
2306 | this._z = 0;
2307 |
2308 | }
2309 |
2310 | } else if ( order === 'YXZ' ) {
2311 |
2312 | this._x = Math.asin( - clamp( m23, - 1, 1 ) );
2313 |
2314 | if ( Math.abs( m23 ) < 0.99999 ) {
2315 |
2316 | this._y = Math.atan2( m13, m33 );
2317 | this._z = Math.atan2( m21, m22 );
2318 |
2319 | } else {
2320 |
2321 | this._y = Math.atan2( - m31, m11 );
2322 | this._z = 0;
2323 |
2324 | }
2325 |
2326 | } else if ( order === 'ZXY' ) {
2327 |
2328 | this._x = Math.asin( clamp( m32, - 1, 1 ) );
2329 |
2330 | if ( Math.abs( m32 ) < 0.99999 ) {
2331 |
2332 | this._y = Math.atan2( - m31, m33 );
2333 | this._z = Math.atan2( - m12, m22 );
2334 |
2335 | } else {
2336 |
2337 | this._y = 0;
2338 | this._z = Math.atan2( m21, m11 );
2339 |
2340 | }
2341 |
2342 | } else if ( order === 'ZYX' ) {
2343 |
2344 | this._y = Math.asin( - clamp( m31, - 1, 1 ) );
2345 |
2346 | if ( Math.abs( m31 ) < 0.99999 ) {
2347 |
2348 | this._x = Math.atan2( m32, m33 );
2349 | this._z = Math.atan2( m21, m11 );
2350 |
2351 | } else {
2352 |
2353 | this._x = 0;
2354 | this._z = Math.atan2( - m12, m22 );
2355 |
2356 | }
2357 |
2358 | } else if ( order === 'YZX' ) {
2359 |
2360 | this._z = Math.asin( clamp( m21, - 1, 1 ) );
2361 |
2362 | if ( Math.abs( m21 ) < 0.99999 ) {
2363 |
2364 | this._x = Math.atan2( - m23, m22 );
2365 | this._y = Math.atan2( - m31, m11 );
2366 |
2367 | } else {
2368 |
2369 | this._x = 0;
2370 | this._y = Math.atan2( m13, m33 );
2371 |
2372 | }
2373 |
2374 | } else if ( order === 'XZY' ) {
2375 |
2376 | this._z = Math.asin( - clamp( m12, - 1, 1 ) );
2377 |
2378 | if ( Math.abs( m12 ) < 0.99999 ) {
2379 |
2380 | this._x = Math.atan2( m32, m22 );
2381 | this._y = Math.atan2( m13, m11 );
2382 |
2383 | } else {
2384 |
2385 | this._x = Math.atan2( - m23, m33 );
2386 | this._y = 0;
2387 |
2388 | }
2389 |
2390 | } else {
2391 |
2392 | console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )
2393 |
2394 | }
2395 |
2396 | this._order = order;
2397 |
2398 | if ( update !== false ) this.onChangeCallback();
2399 |
2400 | return this;
2401 |
2402 | },
2403 |
2404 | setFromQuaternion: function () {
2405 |
2406 | var matrix;
2407 |
2408 | return function ( q, order, update ) {
2409 |
2410 | if ( matrix === undefined ) matrix = new THREE.Matrix4();
2411 | matrix.makeRotationFromQuaternion( q );
2412 | this.setFromRotationMatrix( matrix, order, update );
2413 |
2414 | return this;
2415 |
2416 | };
2417 |
2418 | }(),
2419 |
2420 | setFromVector3: function ( v, order ) {
2421 |
2422 | return this.set( v.x, v.y, v.z, order || this._order );
2423 |
2424 | },
2425 |
2426 | reorder: function () {
2427 |
2428 | // WARNING: this discards revolution information -bhouston
2429 |
2430 | var q = new THREE.Quaternion();
2431 |
2432 | return function ( newOrder ) {
2433 |
2434 | q.setFromEuler( this );
2435 | this.setFromQuaternion( q, newOrder );
2436 |
2437 | };
2438 |
2439 | }(),
2440 |
2441 | equals: function ( euler ) {
2442 |
2443 | return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
2444 |
2445 | },
2446 |
2447 | fromArray: function ( array ) {
2448 |
2449 | this._x = array[ 0 ];
2450 | this._y = array[ 1 ];
2451 | this._z = array[ 2 ];
2452 | if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
2453 |
2454 | this.onChangeCallback();
2455 |
2456 | return this;
2457 |
2458 | },
2459 |
2460 | toArray: function () {
2461 |
2462 | return [ this._x, this._y, this._z, this._order ];
2463 |
2464 | },
2465 |
2466 | toVector3: function ( optionalResult ) {
2467 |
2468 | if ( optionalResult ) {
2469 |
2470 | return optionalResult.set( this._x, this._y, this._z );
2471 |
2472 | } else {
2473 |
2474 | return new THREE.Vector3( this._x, this._y, this._z );
2475 |
2476 | }
2477 |
2478 | },
2479 |
2480 | onChange: function ( callback ) {
2481 |
2482 | this.onChangeCallback = callback;
2483 |
2484 | return this;
2485 |
2486 | },
2487 |
2488 | onChangeCallback: function () {},
2489 |
2490 | clone: function () {
2491 |
2492 | return new THREE.Euler( this._x, this._y, this._z, this._order );
2493 |
2494 | }
2495 |
2496 | };
2497 | /*** END Euler ***/
2498 |
2499 | }
2500 |
2501 | module.exports = THREE;
2502 |
2503 | },{}],7:[function(require,module,exports){
2504 | /*
2505 | * Copyright 2015 Boris Smus. All Rights Reserved.
2506 | * Licensed under the Apache License, Version 2.0 (the "License");
2507 | * you may not use this file except in compliance with the License.
2508 | * You may obtain a copy of the License at
2509 | *
2510 | * http://www.apache.org/licenses/LICENSE-2.0
2511 | *
2512 | * Unless required by applicable law or agreed to in writing, software
2513 | * distributed under the License is distributed on an "AS IS" BASIS,
2514 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2515 | * See the License for the specific language governing permissions and
2516 | * limitations under the License.
2517 | */
2518 | var CardboardHMDVRDevice = require('./cardboard-hmd-vr-device.js');
2519 | var GyroPositionSensorVRDevice = require('./gyro-position-sensor-vr-device.js');
2520 | var MouseKeyboardPositionSensorVRDevice = require('./mouse-keyboard-position-sensor-vr-device.js');
2521 | // Uncomment to add positional tracking via webcam.
2522 | //var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js');
2523 | var HMDVRDevice = require('./base.js').HMDVRDevice;
2524 | var PositionSensorVRDevice = require('./base.js').PositionSensorVRDevice;
2525 |
2526 | function WebVRPolyfill() {
2527 | this.devices = [];
2528 |
2529 | if (!('getVRDevices' in navigator)) {
2530 | // If the WebVR API doesn't exist, we should enable the polyfill.
2531 | this.enablePolyfill();
2532 | }
2533 | }
2534 |
2535 | WebVRPolyfill.prototype.enablePolyfill = function() {
2536 | // Initialize our virtual VR devices.
2537 | if (this.isCardboardCompatible()) {
2538 | this.devices.push(new CardboardHMDVRDevice());
2539 | }
2540 |
2541 | // Polyfill using the right position sensor.
2542 | if (this.isMobile()) {
2543 | this.devices.push(new GyroPositionSensorVRDevice());
2544 | } else {
2545 | this.devices.push(new MouseKeyboardPositionSensorVRDevice());
2546 | // Uncomment to add positional tracking via webcam.
2547 | //this.devices.push(new WebcamPositionSensorVRDevice());
2548 | }
2549 |
2550 | // Provide navigator.getVRDevices.
2551 | navigator.getVRDevices = this.getVRDevices.bind(this);
2552 |
2553 | // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects.
2554 | window.HMDVRDevice = HMDVRDevice;
2555 | window.PositionSensorVRDevice = PositionSensorVRDevice;
2556 | };
2557 |
2558 | WebVRPolyfill.prototype.getVRDevices = function() {
2559 | var devices = this.devices;
2560 | return new Promise(function(resolve, reject) {
2561 | try {
2562 | resolve(devices);
2563 | } catch (e) {
2564 | reject(e);
2565 | }
2566 | });
2567 | };
2568 |
2569 | /**
2570 | * Determine if a device is mobile.
2571 | */
2572 | WebVRPolyfill.prototype.isMobile = function() {
2573 | return /Android/i.test(navigator.userAgent) ||
2574 | /iPhone|iPad|iPod/i.test(navigator.userAgent);;
2575 | };
2576 |
2577 | WebVRPolyfill.prototype.isCardboardCompatible = function() {
2578 | // For now, support all iOS and Android devices.
2579 | return this.isMobile();
2580 | };
2581 |
2582 | module.exports = WebVRPolyfill;
2583 |
2584 | },{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./gyro-position-sensor-vr-device.js":3,"./mouse-keyboard-position-sensor-vr-device.js":5}]},{},[4]);
2585 |
--------------------------------------------------------------------------------
/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troffmo5/OculusStreetView/19e8b5eff1953700a1cb0ae234341c6d84e9f338/placeholder.png
--------------------------------------------------------------------------------