├── 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 |
44 |
45 | 46 |
47 |
48 |
49 |

Settings

50 | 53 | 56 |
57 |
58 | 73 |
74 |
75 |
Author: Luca Siciliano (troffmo5)
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 --------------------------------------------------------------------------------